├── .gitignore ├── .rustfmt.toml ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── README.md └── src ├── btc_on_eos ├── btc │ ├── account_for_fees.rs │ ├── get_btc_output_json.rs │ ├── minting_params.rs │ ├── mod.rs │ ├── sign_transactions.rs │ └── submit_btc_block.rs ├── check_core_is_initialized.rs ├── crypto_utils.rs ├── debug_functions │ ├── block_reprocessors.rs │ └── mod.rs ├── eos │ ├── account_for_fees.rs │ ├── extract_utxos_from_btc_txs.rs │ ├── get_eos_output.rs │ ├── mod.rs │ ├── redeem_info.rs │ ├── save_btc_utxos_to_db.rs │ ├── sign_transactions.rs │ └── submit_eos_block.rs ├── get_enclave_state │ └── mod.rs ├── get_latest_block_numbers │ └── mod.rs ├── mod.rs ├── test_utils │ └── mod.rs └── utils.rs ├── btc_on_eth ├── btc │ ├── account_for_fees.rs │ ├── get_btc_output_json.rs │ ├── minting_params.rs │ ├── mod.rs │ ├── sign_any_sender_transactions.rs │ ├── sign_normal_eth_transactions.rs │ └── submit_btc_block.rs ├── check_core_is_initialized.rs ├── debug_functions │ ├── block_reprocessors.rs │ └── mod.rs ├── eth │ ├── account_for_fees.rs │ ├── add_erc777_contract_address.rs │ ├── create_btc_transactions.rs │ ├── extract_utxos_from_btc_txs.rs │ ├── filter_receipts_in_state.rs │ ├── filter_redeem_infos_in_state.rs │ ├── get_eth_output_json.rs │ ├── increment_btc_nonce.rs │ ├── initialize_eth_core.rs │ ├── mod.rs │ ├── redeem_info.rs │ ├── save_btc_utxos_to_db.rs │ └── submit_eth_block.rs ├── get_enclave_state │ └── mod.rs ├── get_latest_block_numbers │ └── mod.rs ├── mod.rs ├── test_utils │ └── mod.rs └── utils.rs ├── chains ├── btc │ ├── add_btc_block_to_db.rs │ ├── btc_block.rs │ ├── btc_chain_id.rs │ ├── btc_constants.rs │ ├── btc_crypto │ │ ├── btc_private_key.rs │ │ └── mod.rs │ ├── btc_database_utils.rs │ ├── btc_debug_functions.rs │ ├── btc_enclave_state.rs │ ├── btc_state.rs │ ├── btc_submission_material.rs │ ├── btc_test_utils │ │ ├── 1610046-testnet-block-with-tx-to-test-address.json │ │ ├── 1610161-testnet-block-with-tx-to-test-address.json │ │ ├── 1610166-testnet-block-with-tx-to-test-address.json │ │ ├── 1610826-testnet-block-with-tx-to-test-address.json │ │ ├── 1637173-testnet-block-and-txs-with-p2sh-deposit.json │ │ ├── 1660807-testnet-block-and-txs-with-2-p2sh-deposits.json │ │ ├── 1661611-block-and-txs-with-no-op-return.json │ │ ├── 1666951-block-and-txs-p2sh-and-op-return-below-threshold.json │ │ ├── 604700-btc-block-and-txs.json │ │ ├── btc-1661479-btc-block-and-txs-with-deposit-originating-from-enclave-key.json │ │ ├── btc-1670411-block-and-txs.json │ │ ├── btc-1670534-block-and-txs.json │ │ ├── btc-1670541-block-and-txs.json │ │ ├── mod.rs │ │ └── sequential_block_and_ids │ │ │ ├── 1611090-btc-block-and-txs.json │ │ │ ├── 1611091-btc-block-and-txs.json │ │ │ ├── 1611092-btc-block-and-txs.json │ │ │ ├── 1611093-btc-block-and-txs.json │ │ │ ├── 1611094-btc-block-and-txs.json │ │ │ ├── 1611095-btc-block-and-txs.json │ │ │ ├── 1611096-btc-block-and-txs.json │ │ │ ├── 1611097-btc-block-and-txs.json │ │ │ ├── 1611098-btc-block-and-txs.json │ │ │ ├── 1611099-btc-block-and-txs.json │ │ │ └── 1611100-btc-block-and-txs.json │ ├── btc_transaction.rs │ ├── btc_types.rs │ ├── btc_utils.rs │ ├── check_btc_parent_exists.rs │ ├── core_initialization │ │ ├── btc_init_utils.rs │ │ ├── check_btc_core_is_initialized.rs │ │ ├── generate_and_store_btc_keys.rs │ │ ├── get_btc_init_output_json.rs │ │ ├── initialize_btc_core.rs │ │ └── mod.rs │ ├── deposit_address_info.rs │ ├── extract_utxos_from_p2pkh_txs.rs │ ├── extract_utxos_from_p2sh_txs.rs │ ├── filter_minting_params.rs │ ├── filter_p2pkh_deposit_txs.rs │ ├── filter_p2sh_deposit_txs.rs │ ├── filter_utxos.rs │ ├── get_btc_block_in_db_format.rs │ ├── get_deposit_info_hash_map.rs │ ├── increment_any_sender_nonce.rs │ ├── increment_btc_account_nonce.rs │ ├── increment_eos_nonce.rs │ ├── increment_eth_nonce.rs │ ├── mod.rs │ ├── remove_minting_params_from_canon_block.rs │ ├── remove_old_btc_tail_block.rs │ ├── save_utxos_to_db.rs │ ├── set_btc_anchor_block_hash.rs │ ├── set_btc_canon_block_hash.rs │ ├── set_btc_latest_block_hash.rs │ ├── set_flags.rs │ ├── update_btc_canon_block_hash.rs │ ├── update_btc_latest_block_hash.rs │ ├── update_btc_linker_hash.rs │ ├── update_btc_tail_block_hash.rs │ ├── utxo_manager │ │ ├── debug_utxo_utils.rs │ │ ├── mod.rs │ │ ├── utxo_constants.rs │ │ ├── utxo_database_utils.rs │ │ ├── utxo_types.rs │ │ └── utxo_utils.rs │ ├── validate_btc_block_header.rs │ ├── validate_btc_difficulty.rs │ ├── validate_btc_merkle_root.rs │ └── validate_btc_proof_of_work.rs ├── eos │ ├── add_schedule.rs │ ├── append_interim_block_ids.rs │ ├── core_initialization │ │ ├── check_eos_core_is_initialized.rs │ │ ├── eos_init_utils.rs │ │ ├── initialize_eos_core.rs │ │ └── mod.rs │ ├── disable_protocol_feature.rs │ ├── enable_protocol_feature.rs │ ├── eos_action_proofs.rs │ ├── eos_action_receipt.rs │ ├── eos_actions.rs │ ├── eos_block_header.rs │ ├── eos_chain_id.rs │ ├── eos_constants.rs │ ├── eos_crypto │ │ ├── eos_private_key.rs │ │ ├── eos_public_key.rs │ │ ├── eos_signature.rs │ │ ├── eos_transaction.rs │ │ └── mod.rs │ ├── eos_database_transactions.rs │ ├── eos_database_utils.rs │ ├── eos_debug_functions.rs │ ├── eos_enclave_state.rs │ ├── eos_extension.rs │ ├── eos_global_sequences.rs │ ├── eos_hash.rs │ ├── eos_merkle_utils.rs │ ├── eos_metadata.rs │ ├── eos_producer_key.rs │ ├── eos_producer_schedule.rs │ ├── eos_state.rs │ ├── eos_submission_material.rs │ ├── eos_test_utils │ │ ├── eos-block-10700626.json │ │ ├── eos-block-10800.json │ │ ├── eos-block-80440580.json │ │ ├── eos-block-81772484.json │ │ ├── eos-block-81784220.json │ │ ├── eos-block-84187467.json │ │ ├── eos-init-and-subsequent-blocks-jungle-3-1.json │ │ ├── eos-init-and-subsequent-blocks-mainnet-1.json │ │ ├── eos-j3-block-with-schedule.json │ │ ├── eos-mainnet-block-with-schedule-1714.json │ │ ├── jungle-3-block-8242000.json │ │ ├── jungle-3-init-block-10857380.json │ │ ├── jungle-3-init-block-11379805.json │ │ ├── jungle-3-init-block-11879805.json │ │ ├── mainnet-init-block-125292121.json │ │ ├── mainnet-init-block-125293807.json │ │ ├── mainnet-init-block-125293952.json │ │ ├── mainnet-init-block-125293952_with_eos_eth_token_dictionary.json │ │ ├── mainnet-submission-material-with-perc20-redeem.json │ │ ├── mod.rs │ │ ├── sample-active-schedule-2.json │ │ ├── sample-active-schedule-389.json │ │ ├── sample-j3-schedule-37.json │ │ ├── sample-schedule-1713-v1.json │ │ ├── sample-schedule-28-v2.json │ │ ├── sample-schedule-389-v1.json │ │ ├── sample-submission-material-with-bad-account-name-parsing.json │ │ └── sample_init_block_with_v1_schedule.json │ ├── eos_types.rs │ ├── eos_unit_conversions.rs │ ├── eos_utils.rs │ ├── extract_utxos_from_btc_txs.rs │ ├── filter_action_proofs.rs │ ├── get_active_schedule.rs │ ├── get_enabled_protocol_features.rs │ ├── get_eos_incremerkle.rs │ ├── increment_eos_account_nonce.rs │ ├── mod.rs │ ├── protocol_features.rs │ ├── save_incremerkle.rs │ ├── save_latest_block_id.rs │ ├── save_latest_block_num.rs │ ├── validate_producer_slot.rs │ └── validate_signature.rs ├── eth │ ├── add_block_and_receipts_to_db.rs │ ├── any_sender │ │ ├── mod.rs │ │ ├── relay_contract.rs │ │ ├── relay_transaction.rs │ │ └── serde.rs │ ├── calculate_linker_hash.rs │ ├── check_parent_exists.rs │ ├── core_initialization │ │ ├── check_eth_core_is_initialized.rs │ │ ├── eth_core_init_utils.rs │ │ ├── generate_eth_address.rs │ │ ├── generate_eth_contract_address.rs │ │ ├── generate_eth_contract_tx.rs │ │ ├── generate_eth_private_key.rs │ │ ├── get_eth_core_init_output_json.rs │ │ ├── initialize_eth_core.rs │ │ ├── mod.rs │ │ └── reset_eth_chain.rs │ ├── eip_1559.rs │ ├── eth_block.rs │ ├── eth_chain_id.rs │ ├── eth_constants.rs │ ├── eth_contracts │ │ ├── erc20_vault │ │ │ └── mod.rs │ │ ├── erc777 │ │ │ └── mod.rs │ │ ├── erc777_proxy │ │ │ └── mod.rs │ │ └── mod.rs │ ├── eth_crypto │ │ ├── eth_private_key.rs │ │ ├── eth_public_key.rs │ │ ├── eth_transaction.rs │ │ └── mod.rs │ ├── eth_crypto_utils.rs │ ├── eth_database_transactions.rs │ ├── eth_database_utils.rs │ ├── eth_debug_functions.rs │ ├── eth_enclave_state.rs │ ├── eth_log.rs │ ├── eth_message_signer.rs │ ├── eth_receipt.rs │ ├── eth_receipt_type.rs │ ├── eth_state.rs │ ├── eth_submission_material.rs │ ├── eth_test_utils │ │ ├── eth-7004586-ropsten-eth-block-and-receipts.json │ │ ├── eth-7120953-ropsten-eth-block-and-receipts.json │ │ ├── eth-7129763-ropsten-eth-block-and-receipts.json │ │ ├── eth-7418933-ropsten-eth-block-and-receipts.json │ │ ├── eth-7420497-ropsten-eth-block-and-receipts.json │ │ ├── eth-7425517-ropsten-eth-block-and-receipts.json │ │ ├── eth-submission-material-block-11011551.json │ │ ├── eth-submission-material-block-11087536-with-erc20-peg-in-event.json │ │ ├── eth-submission-material-block-13041925.json │ │ ├── eth-submission-material-block-13041927.json │ │ ├── eth-submission-material-block-8739996-with-erc20-peg-in-event.json │ │ ├── eth-submission-material-with-new-erc777-event.json │ │ ├── mainnet-eip1559-block-12991830.json │ │ ├── mod.rs │ │ ├── ptoken-erc777-bytecode │ │ ├── ropsten-eip1559-block.json │ │ ├── ropsten-submission-material-with-eip-2718-tx.json │ │ ├── sample-block-json │ │ ├── sample-eth-block-and-receipts-json │ │ ├── sample-invalid-eth-block-and-receipts-json │ │ ├── sample-receipt-json │ │ ├── sample-ropsten-eth-block-and-receipts.json │ │ └── sequential_block_and_receipts_jsons │ │ │ ├── eth_block_and_receipts_num_8065750.json │ │ │ ├── eth_block_and_receipts_num_8065751.json │ │ │ ├── eth_block_and_receipts_num_8065752.json │ │ │ ├── eth_block_and_receipts_num_8065753.json │ │ │ ├── eth_block_and_receipts_num_8065754.json │ │ │ ├── eth_block_and_receipts_num_8065755.json │ │ │ ├── eth_block_and_receipts_num_8065756.json │ │ │ ├── eth_block_and_receipts_num_8065757.json │ │ │ ├── eth_block_and_receipts_num_8065758.json │ │ │ ├── eth_block_and_receipts_num_8065759.json │ │ │ ├── eth_block_and_receipts_num_8065760.json │ │ │ ├── eth_block_and_receipts_num_8065761.json │ │ │ ├── eth_block_and_receipts_num_8065762.json │ │ │ ├── eth_block_and_receipts_num_8065763.json │ │ │ ├── eth_block_and_receipts_num_8065764.json │ │ │ ├── eth_block_and_receipts_num_8065765.json │ │ │ ├── eth_block_and_receipts_num_8065766.json │ │ │ ├── eth_block_and_receipts_num_8065767.json │ │ │ ├── eth_block_and_receipts_num_8065768.json │ │ │ ├── eth_block_and_receipts_num_8065769.json │ │ │ └── eth_block_and_receipts_num_8065770.json │ ├── eth_traits.rs │ ├── eth_types.rs │ ├── eth_utils.rs │ ├── get_linker_hash.rs │ ├── increment_eos_account_nonce.rs │ ├── increment_eth_account_nonce.rs │ ├── increment_evm_account_nonce.rs │ ├── mod.rs │ ├── remove_old_eth_tail_block.rs │ ├── remove_receipts_from_canon_block.rs │ ├── trie_nodes.rs │ ├── update_eth_canon_block_hash.rs │ ├── update_eth_linker_hash.rs │ ├── update_eth_tail_block_hash.rs │ ├── update_latest_block_hash.rs │ ├── validate_block_in_state.rs │ └── validate_receipts_in_state.rs ├── evm │ ├── add_block_and_receipts_to_db.rs │ ├── calculate_linker_hash.rs │ ├── check_parent_exists.rs │ ├── core_initialization │ │ ├── check_eth_core_is_initialized.rs │ │ ├── eth_core_init_utils.rs │ │ ├── generate_eth_address.rs │ │ ├── generate_eth_contract_tx.rs │ │ ├── generate_eth_private_key.rs │ │ ├── get_eth_core_init_output_json.rs │ │ ├── initialize_eth_core.rs │ │ ├── mod.rs │ │ └── reset_eth_chain.rs │ ├── eth_block.rs │ ├── eth_constants.rs │ ├── eth_crypto │ │ ├── eth_private_key.rs │ │ ├── eth_public_key.rs │ │ ├── eth_transaction.rs │ │ └── mod.rs │ ├── eth_crypto_utils.rs │ ├── eth_database_transactions.rs │ ├── eth_database_utils.rs │ ├── eth_enclave_state.rs │ ├── eth_log.rs │ ├── eth_message_signer.rs │ ├── eth_receipt.rs │ ├── eth_state.rs │ ├── eth_submission_material.rs │ ├── eth_test_utils │ │ ├── eth-7004586-ropsten-eth-block-and-receipts.json │ │ ├── eth-7120953-ropsten-eth-block-and-receipts.json │ │ ├── eth-7129763-ropsten-eth-block-and-receipts.json │ │ ├── eth-7418933-ropsten-eth-block-and-receipts.json │ │ ├── eth-7420497-ropsten-eth-block-and-receipts.json │ │ ├── eth-7425517-ropsten-eth-block-and-receipts.json │ │ ├── eth-submission-material-block-11011551.json │ │ ├── eth-submission-material-block-11087536-with-erc20-peg-in-event.json │ │ ├── eth-submission-material-block-8739996-with-erc20-peg-in-event.json │ │ ├── mod.rs │ │ ├── ptoken-erc777-bytecode │ │ ├── sample-block-json │ │ ├── sample-eth-block-and-receipts-json │ │ ├── sample-invalid-eth-block-and-receipts-json │ │ ├── sample-receipt-json │ │ ├── sample-ropsten-eth-block-and-receipts.json │ │ └── sequential_block_and_receipts_jsons │ │ │ ├── eth_block_and_receipts_num_8065750.json │ │ │ ├── eth_block_and_receipts_num_8065751.json │ │ │ ├── eth_block_and_receipts_num_8065752.json │ │ │ ├── eth_block_and_receipts_num_8065753.json │ │ │ ├── eth_block_and_receipts_num_8065754.json │ │ │ ├── eth_block_and_receipts_num_8065755.json │ │ │ ├── eth_block_and_receipts_num_8065756.json │ │ │ ├── eth_block_and_receipts_num_8065757.json │ │ │ ├── eth_block_and_receipts_num_8065758.json │ │ │ ├── eth_block_and_receipts_num_8065759.json │ │ │ ├── eth_block_and_receipts_num_8065760.json │ │ │ ├── eth_block_and_receipts_num_8065761.json │ │ │ ├── eth_block_and_receipts_num_8065762.json │ │ │ ├── eth_block_and_receipts_num_8065763.json │ │ │ ├── eth_block_and_receipts_num_8065764.json │ │ │ ├── eth_block_and_receipts_num_8065765.json │ │ │ ├── eth_block_and_receipts_num_8065766.json │ │ │ ├── eth_block_and_receipts_num_8065767.json │ │ │ ├── eth_block_and_receipts_num_8065768.json │ │ │ ├── eth_block_and_receipts_num_8065769.json │ │ │ └── eth_block_and_receipts_num_8065770.json │ ├── eth_traits.rs │ ├── eth_types.rs │ ├── eth_utils.rs │ ├── evm_debug_functions.rs │ ├── get_linker_hash.rs │ ├── get_trie_hash_map.rs │ ├── increment_eth_account_nonce_and_return_evm_state.rs │ ├── increment_evm_account_nonce.rs │ ├── mod.rs │ ├── nibble_utils.rs │ ├── path_codec.rs │ ├── remove_old_eth_tail_block.rs │ ├── remove_receipts_from_canon_block.rs │ ├── trie.rs │ ├── trie_nodes.rs │ ├── update_eth_canon_block_hash.rs │ ├── update_eth_linker_hash.rs │ ├── update_eth_tail_block_hash.rs │ ├── update_latest_block_hash.rs │ ├── validate_block_in_state.rs │ └── validate_receipts_in_state.rs └── mod.rs ├── check_debug_mode.rs ├── constants.rs ├── core_type.rs ├── crypto_utils.rs ├── database_utils.rs ├── debug_database_utils.rs ├── dictionaries ├── dictionary_constants.rs ├── eos_eth │ ├── mod.rs │ └── test_utils │ │ └── mod.rs ├── eth_evm │ ├── mod.rs │ └── test_utils │ │ ├── eth-evm-sample-dictionary.json │ │ └── mod.rs └── mod.rs ├── enclave_info.rs ├── eos_on_eth ├── check_core_is_initialized.rs ├── constants.rs ├── debug_functions │ └── mod.rs ├── eos │ ├── eos_tx_info.rs │ ├── get_eos_output.rs │ ├── increment_eth_nonce.rs │ ├── mod.rs │ └── submit_eos_block.rs ├── eth │ ├── eth_tx_info.rs │ ├── filter_receipts_in_state.rs │ ├── get_output_json.rs │ ├── initialize_eth_core.rs │ ├── mod.rs │ └── submit_eth_block.rs ├── get_enclave_state │ └── mod.rs ├── get_latest_block_numbers │ └── mod.rs ├── mod.rs └── test_utils │ ├── eos-bad-account-name-submission-material.json │ ├── eos-submission-material-1.json │ ├── eth-submission-material-1.json │ ├── eth-submission-material-2.json │ ├── eth-submission-material-with-two-eos-on-eth-pegins.json │ └── mod.rs ├── erc20_on_eos ├── add_vault_contract_address_to_db.rs ├── check_core_is_initialized.rs ├── debug_functions │ └── mod.rs ├── eos │ ├── get_eos_output.rs │ ├── increment_eth_nonce.rs │ ├── mod.rs │ ├── redeem_info.rs │ ├── sign_normal_eth_txs.rs │ └── submit_eos_block.rs ├── eth │ ├── get_output_json.rs │ ├── initialize_eth_core.rs │ ├── mod.rs │ ├── peg_in_info.rs │ ├── sign_eos_transactions.rs │ └── submit_eth_block.rs ├── get_enclave_state │ └── mod.rs ├── get_latest_block_numbers │ └── mod.rs └── mod.rs ├── erc20_on_evm ├── check_core_is_initialized.rs ├── debug_functions │ ├── block_reprocessors.rs │ └── mod.rs ├── eth │ ├── account_for_fees.rs │ ├── add_vault_contract_address.rs │ ├── evm_tx_info.rs │ ├── get_eth_output_json.rs │ ├── initialize_eth_core.rs │ ├── mod.rs │ └── submit_eth_block.rs ├── evm │ ├── account_for_fees.rs │ ├── eth_tx_info.rs │ ├── get_evm_output_json.rs │ ├── initialize_evm_core.rs │ ├── mod.rs │ └── submit_evm_block.rs ├── get_enclave_state │ └── mod.rs ├── get_latest_block_numbers │ └── mod.rs ├── mod.rs ├── test_utils │ ├── eth-submission-material-1.json │ ├── evm-submission-material-1.json │ └── mod.rs └── traits.rs ├── errors.rs ├── fees ├── fee_constants.rs ├── fee_database_utils.rs ├── fee_enclave_state.rs ├── fee_utils.rs ├── fee_withdrawals.rs └── mod.rs ├── lib.rs ├── metadata ├── metadata_chain_id.rs ├── metadata_origin_address.rs ├── metadata_protocol_id.rs ├── metadata_traits.rs ├── metadata_version.rs ├── mod.rs └── test_utils.rs ├── notes ├── test_utils.rs ├── traits.rs ├── types.rs └── utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | *.env 3 | temp*/ 4 | build/ 5 | target/ 6 | *.log 7 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | comment_width = 120 2 | condense_wildcard_suffixes = true 3 | edition = "2018" 4 | enum_discrim_align_threshold = 20 5 | # error_on_line_overflow = true 6 | # error_on_unformatted = true 7 | format_code_in_doc_comments = true 8 | format_macro_matchers = true 9 | group_imports = "StdExternalCrate" 10 | imports_layout = "HorizontalVertical" 11 | match_block_trailing_comma = true 12 | max_width = 120 13 | imports_granularity = "Crate" 14 | newline_style = "Unix" 15 | normalize_comments = true 16 | normalize_doc_attributes = true 17 | overflow_delimited_expr = true 18 | reorder_impl_items = true 19 | unstable_features = true 20 | use_field_init_shorthand = true 21 | use_try_shorthand = true 22 | # width_heuristics = "Max" 23 | wrap_comments = true 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | license = "MIT" 3 | publish = false 4 | edition = "2018" 5 | version = "4.50.0" 6 | readme = "README.md" 7 | name = "ptokens_core" 8 | keywords = ["provable", "defi", "crypto"] 9 | authors = ["Provable Things ", "Greg Kapka "] 10 | 11 | [features] 12 | debug = [] 13 | disable-fees = ["debug"] 14 | non-validating = ["debug"] 15 | 16 | [profile.dev] 17 | split-debuginfo = "unpacked" 18 | 19 | [profile.release] 20 | lto = "thin" 21 | overflow-checks = true 22 | 23 | [dependencies] 24 | hex = "0.4.3" 25 | log = "0.4.14" 26 | rlp = "0.5.0" 27 | rand = "0.8.3" 28 | strum = "0.20.0" 29 | chrono = "0.4.19" 30 | ethabi = "14.0.0" 31 | byteorder = "1.4.3" 32 | rustc-hex = "2.1.0" 33 | simplelog = "0.10.0" 34 | serde_json = "1.0.64" 35 | lazy_static = "1.4.0" 36 | strum_macros = "0.20.1" 37 | ethereum-types = "0.11.0" 38 | serde = { version = "1.0.125", features = ["derive"] } 39 | tiny-keccak = { version = "2.0.2", features = ["keccak"] } 40 | bitcoin = { version = "0.26.0", features = ["use-serde", "rand"] } 41 | eos-chain = { git = "https://github.com/bifrost-finance/rust-eos" } 42 | secp256k1 = { git = "https://github.com/bifrost-finance/rust-secp256k1", features = ["recovery", "rand-std"] } 43 | quick-error = "2.0.0" 44 | derive_more = "0.99.13" 45 | keccak-hasher = "0.15.3" 46 | triehash = "0.8.4" 47 | 48 | [dev-dependencies] 49 | simple_logger = "1.11.0" 50 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.46 2 | 3 | LABEL maintainer="Provable Things Ltd (info@provable.xyz)" \ 4 | description="Serve as a base image layer to work with \ 5 | pTokens command line interfaces." 6 | 7 | WORKDIR /root 8 | 9 | RUN mkdir core 10 | 11 | COPY src core/src 12 | 13 | COPY Cargo.* core/ 14 | -------------------------------------------------------------------------------- /src/btc_on_eos/btc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod submit_btc_block; 2 | 3 | pub(crate) mod account_for_fees; 4 | pub(crate) mod get_btc_output_json; 5 | pub(crate) mod minting_params; 6 | pub(crate) mod sign_transactions; 7 | -------------------------------------------------------------------------------- /src/btc_on_eos/btc/sign_transactions.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | btc_on_eos::btc::minting_params::BtcOnEosMintingParams, 3 | chains::{ 4 | btc::{btc_database_utils::get_btc_canon_block_from_db, btc_state::BtcState}, 5 | eos::{ 6 | eos_chain_id::EosChainId, 7 | eos_crypto::{ 8 | eos_private_key::EosPrivateKey, 9 | eos_transaction::{get_signed_eos_ptoken_issue_tx, EosSignedTransaction, EosSignedTransactions}, 10 | }, 11 | eos_database_utils::{get_eos_account_name_string_from_db, get_eos_chain_id_from_db}, 12 | eos_utils::get_eos_tx_expiration_timestamp_with_offset, 13 | }, 14 | }, 15 | traits::DatabaseInterface, 16 | types::Result, 17 | }; 18 | 19 | pub fn get_signed_eos_ptoken_issue_txs( 20 | ref_block_num: u16, 21 | ref_block_prefix: u32, 22 | chain_id: &EosChainId, 23 | pk: &EosPrivateKey, 24 | account: &str, 25 | minting_params: &BtcOnEosMintingParams, 26 | ) -> Result { 27 | info!("✔ Signing {} txs...", minting_params.len()); 28 | Ok(EosSignedTransactions::new( 29 | minting_params 30 | .iter() 31 | .enumerate() 32 | .map(|(i, params)| { 33 | get_signed_eos_ptoken_issue_tx( 34 | ref_block_num, 35 | ref_block_prefix, 36 | ¶ms.to, 37 | ¶ms.amount, 38 | chain_id, 39 | pk, 40 | account, 41 | get_eos_tx_expiration_timestamp_with_offset(i as u32)?, 42 | None, 43 | ) 44 | }) 45 | .collect::>>()?, 46 | )) 47 | } 48 | 49 | pub fn maybe_sign_canon_block_txs_and_add_to_state(state: BtcState) -> Result> { 50 | info!("✔ Maybe signing minting txs..."); 51 | get_signed_eos_ptoken_issue_txs( 52 | state.get_eos_ref_block_num()?, 53 | state.get_eos_ref_block_prefix()?, 54 | &get_eos_chain_id_from_db(&state.db)?, 55 | &EosPrivateKey::get_from_db(&state.db)?, 56 | &get_eos_account_name_string_from_db(&state.db)?, 57 | &get_btc_canon_block_from_db(&state.db)?.get_eos_minting_params(), 58 | ) 59 | .and_then(|signed_txs| state.add_signed_txs(signed_txs)) 60 | } 61 | -------------------------------------------------------------------------------- /src/btc_on_eos/check_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | btc::{btc_state::BtcState, core_initialization::check_btc_core_is_initialized::check_btc_core_is_initialized}, 4 | eos::{core_initialization::check_eos_core_is_initialized::check_eos_core_is_initialized, eos_state::EosState}, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn check_core_is_initialized(db: &D) -> Result<()> { 11 | check_btc_core_is_initialized(db).and_then(|_| check_eos_core_is_initialized(db)) 12 | } 13 | 14 | pub fn check_core_is_initialized_and_return_eos_state(state: EosState) -> Result> { 15 | check_core_is_initialized(&state.db).and(Ok(state)) 16 | } 17 | 18 | pub fn check_core_is_initialized_and_return_btc_state(state: BtcState) -> Result> { 19 | check_core_is_initialized(&state.db).and(Ok(state)) 20 | } 21 | -------------------------------------------------------------------------------- /src/btc_on_eos/crypto_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::types::Result; 2 | use secp256k1::key::SecretKey; 3 | use rand::{ 4 | RngCore, 5 | thread_rng, 6 | }; 7 | 8 | fn get_x_random_bytes(num_bytes: usize) -> Vec { 9 | let mut bytes = vec![0u8; num_bytes]; 10 | thread_rng().fill_bytes(&mut bytes); 11 | bytes 12 | } 13 | 14 | fn get_32_random_bytes_arr() -> [u8; 32] { 15 | let mut arr = [0; 32]; 16 | arr.copy_from_slice(&get_x_random_bytes(32)); 17 | arr 18 | } 19 | 20 | pub fn generate_random_private_key() -> Result { 21 | Ok(SecretKey::from_slice(&get_32_random_bytes_arr())?) 22 | } 23 | 24 | #[cfg(test)] 25 | mod test { 26 | use super::*; 27 | 28 | #[test] 29 | fn should_generate_32_random_bytes() { 30 | let result = get_32_random_bytes_arr(); 31 | assert_eq!(result.len(), 32); 32 | } 33 | 34 | #[test] 35 | fn should_generate_x_random_bytes() { 36 | let x: usize = 100; // TODO: get a random num here! 37 | let result = get_x_random_bytes(x); 38 | assert_eq!(result.len(), x); 39 | } 40 | 41 | #[test] 42 | fn should_generate_random_private_key() { 43 | generate_random_private_key().unwrap(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/btc_on_eos/eos/extract_utxos_from_btc_txs.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_state::EosState, extract_utxos_from_btc_txs::extract_btc_utxo_from_btc_tx}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_extract_btc_utxo_from_btc_tx_in_state(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Maybe extracting UTXOs from BTC txs in state..."); 12 | match state.btc_on_eos_signed_txs.len() { 13 | 0 => { 14 | info!("✔ No BTC txs in state ∴ no UTXOs to extract..."); 15 | Ok(state) 16 | }, 17 | _ => { 18 | info!("✔ Extracting BTC UTXOs..."); 19 | extract_btc_utxo_from_btc_tx(&state.db, &state.btc_on_eos_signed_txs) 20 | .and_then(|utxos| state.add_btc_utxos_and_values(utxos)) 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/btc_on_eos/eos/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod submit_eos_block; 2 | 3 | pub(crate) mod account_for_fees; 4 | pub(crate) mod extract_utxos_from_btc_txs; 5 | pub(crate) mod get_eos_output; 6 | pub(crate) mod redeem_info; 7 | pub(crate) mod save_btc_utxos_to_db; 8 | pub(crate) mod sign_transactions; 9 | -------------------------------------------------------------------------------- /src/btc_on_eos/eos/save_btc_utxos_to_db.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{btc::utxo_manager::utxo_database_utils::save_utxos_to_db, eos::eos_state::EosState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_save_btc_utxos_to_db(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Maybe saving BTC UTXOs..."); 12 | match &state.btc_utxos_and_values { 13 | None => { 14 | info!("✔ No BTC UTXOs in state to save!"); 15 | Ok(state) 16 | }, 17 | Some(utxos) => save_utxos_to_db(&state.db, utxos).map(|_| state), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/btc_on_eos/get_enclave_state/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | btc_on_eos::check_core_is_initialized::check_core_is_initialized, 5 | chains::{btc::btc_enclave_state::BtcEnclaveState, eos::eos_enclave_state::EosEnclaveState}, 6 | enclave_info::EnclaveInfo, 7 | fees::fee_enclave_state::FeesEnclaveState, 8 | traits::DatabaseInterface, 9 | types::Result, 10 | }; 11 | 12 | #[derive(Serialize, Deserialize)] 13 | struct EnclaveState { 14 | info: EnclaveInfo, 15 | eos: EosEnclaveState, 16 | btc: BtcEnclaveState, 17 | fees: FeesEnclaveState, 18 | } 19 | 20 | impl EnclaveState { 21 | pub fn new(db: &D) -> Result { 22 | Ok(Self { 23 | info: EnclaveInfo::new(), 24 | eos: EosEnclaveState::new(db)?, 25 | btc: BtcEnclaveState::new(db)?, 26 | fees: FeesEnclaveState::new_for_btc_on_eos(db)?, 27 | }) 28 | } 29 | 30 | pub fn to_string(&self) -> Result { 31 | Ok(serde_json::to_string(self)?) 32 | } 33 | } 34 | 35 | /// # Get Enclave State 36 | /// 37 | /// This function returns a JSON containing the enclave state, including state relevant to each 38 | /// blockchain controlled by this instance. 39 | pub fn get_enclave_state(db: D) -> Result { 40 | info!("✔ Getting core state..."); 41 | check_core_is_initialized(&db).and_then(|_| EnclaveState::new(&db)?.to_string()) 42 | } 43 | -------------------------------------------------------------------------------- /src/btc_on_eos/get_latest_block_numbers/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | btc_on_eos::check_core_is_initialized::check_core_is_initialized, 5 | chains::{ 6 | btc::btc_database_utils::get_latest_btc_block_number, 7 | eos::eos_database_utils::get_latest_eos_block_number, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | #[derive(Serialize, Deserialize)] 14 | struct BlockNumbers { 15 | btc_latest_block_number: u64, 16 | eos_latest_block_number: u64, 17 | } 18 | 19 | /// # Get Latest Block Numbers 20 | /// 21 | /// This function returns a JSON containing the last processed block number of each of the 22 | /// blockchains this instance manages. 23 | pub fn get_latest_block_numbers(db: D) -> Result { 24 | info!("✔ Getting latest block numbers..."); 25 | check_core_is_initialized(&db).and_then(|_| { 26 | Ok(serde_json::to_string(&BlockNumbers { 27 | btc_latest_block_number: get_latest_btc_block_number(&db)?, 28 | eos_latest_block_number: get_latest_eos_block_number(&db)?, 29 | })?) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/btc_on_eos/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use crate::{ 3 | btc_on_eos::eos::redeem_info::{BtcOnEosRedeemInfo, BtcOnEosRedeemInfos}, 4 | chains::eos::eos_test_utils::get_sample_eos_submission_material_n, 5 | }; 6 | 7 | pub fn get_sample_redeem_info() -> BtcOnEosRedeemInfo { 8 | let action_proof = get_sample_eos_submission_material_n(1).action_proofs[0].clone(); 9 | BtcOnEosRedeemInfo::from_action_proof(&action_proof).unwrap() 10 | } 11 | 12 | pub fn get_sample_redeem_infos() -> BtcOnEosRedeemInfos { 13 | BtcOnEosRedeemInfos::new(vec![get_sample_redeem_info(), get_sample_redeem_info()]) 14 | } 15 | -------------------------------------------------------------------------------- /src/btc_on_eth/btc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod submit_btc_block; 2 | 3 | pub(crate) mod account_for_fees; 4 | pub(crate) mod get_btc_output_json; 5 | pub(crate) mod minting_params; 6 | pub(crate) mod sign_any_sender_transactions; 7 | pub(crate) mod sign_normal_eth_transactions; 8 | -------------------------------------------------------------------------------- /src/btc_on_eth/check_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | btc::{btc_state::BtcState, core_initialization::check_btc_core_is_initialized::check_btc_core_is_initialized}, 4 | eth::{core_initialization::check_eth_core_is_initialized::check_eth_core_is_initialized, eth_state::EthState}, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn check_core_is_initialized(db: &D) -> Result<()> { 11 | info!("✔ Checking core is initialized..."); 12 | check_btc_core_is_initialized(db).and_then(|_| check_eth_core_is_initialized(db)) 13 | } 14 | 15 | pub fn check_core_is_initialized_and_return_btc_state(state: BtcState) -> Result> { 16 | check_core_is_initialized(&state.db).and(Ok(state)) 17 | } 18 | 19 | pub fn check_core_is_initialized_and_return_eth_state(state: EthState) -> Result> { 20 | check_core_is_initialized(&state.db).and(Ok(state)) 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | use crate::{ 27 | chains::{ 28 | btc::{btc_database_utils::put_btc_address_in_db, btc_test_utils::SAMPLE_TARGET_BTC_ADDRESS}, 29 | eth::{eth_database_utils::put_public_eth_address_in_db, eth_test_utils::get_sample_eth_address}, 30 | }, 31 | test_utils::get_test_database, 32 | }; 33 | 34 | #[test] 35 | fn should_err_if_core_not_initialized() { 36 | let db = get_test_database(); 37 | let result = check_core_is_initialized(&db); 38 | assert!(result.is_err()); 39 | } 40 | 41 | #[test] 42 | fn should_be_ok_if_core_initialized() { 43 | let db = get_test_database(); 44 | put_btc_address_in_db(&db, &SAMPLE_TARGET_BTC_ADDRESS).unwrap(); 45 | put_public_eth_address_in_db(&db, &get_sample_eth_address()).unwrap(); 46 | let result = check_core_is_initialized(&db); 47 | assert!(result.is_ok()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/btc_on_eth/eth/add_erc777_contract_address.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | btc_on_eth::check_core_is_initialized::check_core_is_initialized, 3 | chains::eth::{eth_database_utils::put_btc_on_eth_smart_contract_address_in_db, eth_utils::convert_hex_to_address}, 4 | traits::DatabaseInterface, 5 | types::Result, 6 | }; 7 | 8 | /// # Maybe Add ERC777 Contract Address 9 | /// 10 | /// This function will add a passed in ETH address to the encrypted database since the ETH 11 | /// initialization no longer creates one. Until this step has been carried out after ETH core 12 | /// initialization, the `get_enclave_state` command will error with a message telling you to call 13 | /// this function. 14 | /// 15 | /// ### BEWARE: 16 | /// The ERC777 contract can only be set ONCE. Further attempts to do so will not succeed. 17 | pub fn maybe_add_erc777_contract_address(db: D, hex_address: &str) -> Result { 18 | check_core_is_initialized(&db) 19 | .and_then(|_| db.start_transaction()) 20 | .and_then(|_| convert_hex_to_address(hex_address)) 21 | .and_then(|ref address| put_btc_on_eth_smart_contract_address_in_db(&db, address)) 22 | .and_then(|_| db.end_transaction()) 23 | .map(|_| "{add_erc777_address_success:true}".to_string()) 24 | } 25 | -------------------------------------------------------------------------------- /src/btc_on_eth/eth/extract_utxos_from_btc_txs.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | btc::{ 4 | btc_database_utils::get_btc_address_from_db, 5 | btc_types::BtcTransaction, 6 | btc_utils::get_pay_to_pub_key_hash_script, 7 | extract_utxos_from_p2pkh_txs::extract_utxos_from_txs, 8 | utxo_manager::utxo_types::BtcUtxosAndValues, 9 | }, 10 | eth::eth_state::EthState, 11 | }, 12 | traits::DatabaseInterface, 13 | types::Result, 14 | }; 15 | 16 | pub fn extract_btc_utxos_from_btc_txs(enclave_address: &str, btc_txs: &[BtcTransaction]) -> Result { 17 | get_pay_to_pub_key_hash_script(enclave_address).map(|target_script| extract_utxos_from_txs(&target_script, btc_txs)) 18 | } 19 | 20 | pub fn maybe_extract_btc_utxo_from_btc_tx_in_state(state: EthState) -> Result> { 21 | info!("✔ Maybe extracting UTXOs from BTC txs in state..."); 22 | match &state.btc_transactions { 23 | None => { 24 | info!("✔ No BTC txs in state ∴ no UTXOs to extract..."); 25 | Ok(state) 26 | }, 27 | Some(btc_txs) => { 28 | info!("✔ Extracting BTC UTXOs & adding to state..."); 29 | extract_btc_utxos_from_btc_txs(&get_btc_address_from_db(&state.db)?, btc_txs) 30 | .and_then(|utxos| state.add_btc_utxos_and_values(utxos)) 31 | }, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/btc_on_eth/eth/filter_receipts_in_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_contracts::erc777::{ 4 | ERC_777_REDEEM_EVENT_TOPIC_WITHOUT_USER_DATA, 5 | ERC_777_REDEEM_EVENT_TOPIC_WITH_USER_DATA, 6 | }, 7 | eth_database_utils::get_erc777_contract_address_from_db, 8 | eth_state::EthState, 9 | }, 10 | traits::DatabaseInterface, 11 | types::Result, 12 | }; 13 | 14 | pub fn filter_receipts_for_btc_on_eth_redeem_events_in_state( 15 | state: EthState, 16 | ) -> Result> { 17 | info!("✔ Filtering receipts for those containing `btc-on-eth` redeem events..."); 18 | state 19 | .get_eth_submission_material()? 20 | .get_receipts_containing_log_from_address_and_with_topics(&get_erc777_contract_address_from_db(&state.db)?, &[ 21 | *ERC_777_REDEEM_EVENT_TOPIC_WITHOUT_USER_DATA, 22 | *ERC_777_REDEEM_EVENT_TOPIC_WITH_USER_DATA, 23 | ]) 24 | .and_then(|filtered_block_and_receipts| state.update_eth_submission_material(filtered_block_and_receipts)) 25 | } 26 | -------------------------------------------------------------------------------- /src/btc_on_eth/eth/increment_btc_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{btc::btc_database_utils::increment_btc_account_nonce_in_db, eth::eth_state::EthState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_increment_btc_nonce_in_db_and_return_state(state: EthState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | match &state.btc_transactions { 12 | None => { 13 | info!("✔ Not incrementing BTC account nonce - no signatures made!"); 14 | Ok(state) 15 | }, 16 | Some(signed_txs) => { 17 | info!("✔ Incrementing BTC account nonce by {}", signed_txs.len()); 18 | increment_btc_account_nonce_in_db(&state.db, signed_txs.len() as u64).and(Ok(state)) 19 | }, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/btc_on_eth/eth/initialize_eth_core.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use crate::{ 4 | chains::eth::{ 5 | core_initialization::{ 6 | check_eth_core_is_initialized::is_eth_core_initialized, 7 | get_eth_core_init_output_json::EthInitializationOutput, 8 | initialize_eth_core::initialize_eth_core_with_no_contract_tx, 9 | }, 10 | eth_chain_id::EthChainId, 11 | eth_constants::ETH_CORE_IS_INITIALIZED_JSON, 12 | eth_database_transactions::{ 13 | end_eth_db_transaction_and_return_state, 14 | start_eth_db_transaction_and_return_state, 15 | }, 16 | eth_state::EthState, 17 | }, 18 | traits::DatabaseInterface, 19 | types::Result, 20 | }; 21 | 22 | pub fn maybe_initialize_eth_enclave( 23 | db: D, 24 | block_json: &str, 25 | chain_id: u8, 26 | gas_price: u64, 27 | confs: u64, 28 | _bytecode_path: &str, 29 | ) -> Result { 30 | match is_eth_core_initialized(&db) { 31 | true => Ok(ETH_CORE_IS_INITIALIZED_JSON.to_string()), 32 | false => start_eth_db_transaction_and_return_state(EthState::init(db)) 33 | .and_then(|state| { 34 | initialize_eth_core_with_no_contract_tx( 35 | block_json, 36 | &EthChainId::try_from(chain_id)?, 37 | gas_price, 38 | confs, 39 | state, 40 | ) 41 | }) 42 | .and_then(end_eth_db_transaction_and_return_state) 43 | .and_then(EthInitializationOutput::new_with_no_contract), 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/btc_on_eth/eth/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod initialize_eth_core; 2 | pub mod submit_eth_block; 3 | 4 | pub(crate) mod account_for_fees; 5 | pub(crate) mod add_erc777_contract_address; 6 | pub(crate) mod create_btc_transactions; 7 | pub(crate) mod extract_utxos_from_btc_txs; 8 | pub(crate) mod filter_receipts_in_state; 9 | pub(crate) mod filter_redeem_infos_in_state; 10 | pub(crate) mod get_eth_output_json; 11 | pub(crate) mod increment_btc_nonce; 12 | pub(crate) mod redeem_info; 13 | pub(crate) mod save_btc_utxos_to_db; 14 | -------------------------------------------------------------------------------- /src/btc_on_eth/eth/save_btc_utxos_to_db.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{btc::utxo_manager::utxo_database_utils::save_utxos_to_db, eth::eth_state::EthState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_save_btc_utxos_to_db_and_return_state(state: EthState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Maybe saving BTC UTXOs..."); 12 | match &state.btc_utxos_and_values { 13 | Some(utxos) => save_utxos_to_db(&state.db, utxos).and(Ok(state)), 14 | None => { 15 | info!("✔ No BTC UTXOs in state to save!"); 16 | Ok(state) 17 | }, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/btc_on_eth/get_enclave_state/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | btc_on_eth::check_core_is_initialized::check_core_is_initialized, 5 | chains::{btc::btc_enclave_state::BtcEnclaveState, eth::eth_enclave_state::EthEnclaveState}, 6 | enclave_info::EnclaveInfo, 7 | fees::fee_enclave_state::FeesEnclaveState, 8 | traits::DatabaseInterface, 9 | types::Result, 10 | }; 11 | 12 | #[derive(Serialize, Deserialize)] 13 | struct EnclaveState { 14 | info: EnclaveInfo, 15 | btc: BtcEnclaveState, 16 | eth: EthEnclaveState, 17 | fees: FeesEnclaveState, 18 | } 19 | 20 | impl EnclaveState { 21 | pub fn new(db: &D) -> Result { 22 | Ok(Self { 23 | info: EnclaveInfo::new(), 24 | btc: BtcEnclaveState::new(db)?, 25 | eth: EthEnclaveState::new_for_btc_on_eth(db)?, 26 | fees: FeesEnclaveState::new_for_btc_on_eth(db)?, 27 | }) 28 | } 29 | 30 | pub fn to_string(&self) -> Result { 31 | Ok(serde_json::to_string(self)?) 32 | } 33 | } 34 | 35 | /// # Get Enclave State 36 | /// 37 | /// This function returns a JSON containing the enclave state, including state relevant to each 38 | /// blockchain controlled by this instance. 39 | pub fn get_enclave_state(db: D) -> Result { 40 | info!("✔ Getting enclave state..."); 41 | check_core_is_initialized(&db).and_then(|_| EnclaveState::new(&db)?.to_string()) 42 | } 43 | -------------------------------------------------------------------------------- /src/btc_on_eth/get_latest_block_numbers/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | btc_on_eth::check_core_is_initialized::check_core_is_initialized, 5 | chains::{ 6 | btc::btc_database_utils::get_latest_btc_block_number, 7 | eth::eth_database_utils::get_latest_eth_block_number, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | #[derive(Serialize, Deserialize)] 14 | struct BlockNumbers { 15 | btc_latest_block_number: u64, 16 | eth_latest_block_number: usize, 17 | } 18 | 19 | /// # Get Latest Block Numbers 20 | /// 21 | /// This function returns a JSON containing the last processed block number of each of the 22 | /// blockchains this instance manages. 23 | pub fn get_latest_block_numbers(db: D) -> Result { 24 | info!("✔ Getting latest block numbers..."); 25 | check_core_is_initialized(&db).and_then(|_| { 26 | Ok(serde_json::to_string(&BlockNumbers { 27 | btc_latest_block_number: get_latest_btc_block_number(&db)?, 28 | eth_latest_block_number: get_latest_eth_block_number(&db)?, 29 | })?) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/btc_on_eth/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use ethereum_types::{Address as EthAddress, H256 as EthHash}; 3 | 4 | use crate::btc_on_eth::eth::redeem_info::{BtcOnEthRedeemInfo, BtcOnEthRedeemInfos}; 5 | 6 | pub fn get_sample_btc_on_eth_redeem_info_1() -> BtcOnEthRedeemInfo { 7 | BtcOnEthRedeemInfo { 8 | amount_in_satoshis: 123456789, 9 | recipient: "mudzxCq9aCQ4Una9MmayvJVCF1Tj9fypiM".to_string(), 10 | from: EthAddress::from_slice(&hex::decode("7d39fb393c5597dddccf1c428f030913fe7f67ab").unwrap()), 11 | originating_tx_hash: EthHash::from_slice( 12 | &hex::decode("01920b62cd2e77204b2fa59932f9d6dd54fd43c99095aee808b700ed2b6ee9cf").unwrap(), 13 | ), 14 | } 15 | } 16 | 17 | fn get_sample_btc_on_eth_redeem_info_2() -> BtcOnEthRedeemInfo { 18 | BtcOnEthRedeemInfo { 19 | amount_in_satoshis: 987654321, 20 | recipient: "mudzxCq9aCQ4Una9MmayvJVCF1Tj9fypiM".to_string(), 21 | from: EthAddress::from_slice(&hex::decode("7d39fb393c5597dddccf1c428f030913fe7f67ab").unwrap()), 22 | originating_tx_hash: EthHash::from_slice( 23 | &hex::decode("01920b62cd2e77204b2fa59932f9d6dd54fd43c99095aee808b700ed2b6ee9cf").unwrap(), 24 | ), 25 | } 26 | } 27 | 28 | pub fn get_sample_btc_on_eth_redeem_infos() -> BtcOnEthRedeemInfos { 29 | BtcOnEthRedeemInfos::new(vec![ 30 | get_sample_btc_on_eth_redeem_info_1(), 31 | get_sample_btc_on_eth_redeem_info_2(), 32 | ]) 33 | } 34 | -------------------------------------------------------------------------------- /src/btc_on_eth/utils.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::U256; 2 | 3 | use crate::constants::{BTC_NUM_DECIMALS, PTOKEN_ERC777_NUM_DECIMALS}; 4 | 5 | pub fn convert_satoshis_to_wei(satoshis: u64) -> U256 { 6 | U256::from(satoshis) * U256::from(10u64.pow(PTOKEN_ERC777_NUM_DECIMALS - BTC_NUM_DECIMALS as u32)) 7 | } 8 | 9 | pub fn convert_wei_to_satoshis(ptoken: U256) -> u64 { 10 | match ptoken.checked_div(U256::from( 11 | 10u64.pow(PTOKEN_ERC777_NUM_DECIMALS - BTC_NUM_DECIMALS as u32), 12 | )) { 13 | Some(amount) => amount.as_u64(), 14 | None => 0, 15 | } 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | use super::*; 21 | 22 | #[test] 23 | fn should_convert_satoshis_to_wei() { 24 | let satoshis = 1337; 25 | let expected_result = U256::from_dec_str("13370000000000").unwrap(); 26 | let result = convert_satoshis_to_wei(satoshis); 27 | assert_eq!(result, expected_result); 28 | } 29 | 30 | #[test] 31 | fn should_convert_wei_to_satoshis() { 32 | let ptoken = U256::from_dec_str("13370000000000").unwrap(); 33 | let expected_result = 1337; 34 | let result = convert_wei_to_satoshis(ptoken); 35 | assert_eq!(result, expected_result); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/chains/btc/add_btc_block_to_db.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_database_utils::{btc_block_exists_in_db, put_btc_block_in_db}, 4 | btc_state::BtcState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn maybe_add_btc_block_to_db(state: BtcState) -> Result> { 11 | info!("✔ Checking if BTC block is already in the db..."); 12 | match btc_block_exists_in_db(&state.db, &state.get_btc_block_and_id()?.id) { 13 | true => Err("✘ BTC Block Rejected - it's already in the db!".into()), 14 | false => { 15 | let block = state.get_btc_block_in_db_format()?; 16 | info!("✔ BTC block not in db!"); 17 | info!("✔ Adding BTC block to db: {:?}", block); 18 | put_btc_block_in_db(&state.db, block).map(|_| { 19 | info!("✔ BTC block added to database!"); 20 | state 21 | }) 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/chains/btc/btc_crypto/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod btc_private_key; 2 | -------------------------------------------------------------------------------- /src/chains/btc/btc_test_utils/sequential_block_and_ids/1611097-btc-block-and-txs.json: -------------------------------------------------------------------------------- 1 | {"deposit_address_list":[],"block":{"id":"00000000000001774cb650f15f3d061e23cb99cdd9cfbc11bb0655ef9c3e653d","height":1611097,"version":541065216,"timestamp":1575408645,"tx_count":3,"size":796,"weight":2098,"merkle_root":"d79f8ebc2df7c85819f7c5ed1aa7bc6cb8bef20811c9f81021c022e4d1233ec6","previousblockhash":"00000000000001295eea416fb0c4d9bd07f0164dcae332edf8da2ab97f21042b","nonce":1566897152,"bits":436449004},"transactions":["010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2b035995181a4d696e656420627920416e74506f6f6c206a000120574515f201ff0d0000a38e010000000000ffffffff023d0d54020000000017a914011beb6fb8499e075a57027fb0a58384f2d3f784870000000000000000266a24aa21a9ed78c5b1ee1f228d45ea83a642c2917a27e9076e3b48d15bb0c83161df2818a3b40120000000000000000000000000000000000000000000000000000000000000000000000000","01000000000101203ab9a880bb7c8b826bed92a76a32d4cabbabae013562d97da70a4ac9245a380100000000f0ffffff0340420f000000000017a914c6953614d8b506593cf124730acd9c809c69436187905d0600000000001600140a174b2ac706630c3988e628ed0c6b53910d1bf30000000000000000196a1768747470733a2f2f746274632e6269746170732e636f6d024730440220698e1b748ed2cb7134939042dc54f1d9a5353071df772a0b23aacf9b906b29b502205d3c256c09b47cb488acda8d906d9f754b3ee46e108f00b5ba686ea120e36527012102e3b73642aa42a384ae460e1e88b2243511248fede45d77e8eb58327d98cb2e1c00000000","0200000000010100c99596829f93429891c6b8488a705e69383c2f5dacafeefe8673e2489852c200000000171600147e8582328cc67cc8c690c5f30ede944e3b1fbf8afeffffff02101723000000000017a9142e978588c58574c9dacb1c032c399db32679693e8740420f00000000001976a914cc97e2b7eeb8677eda5c39d379d7f48cb2b66cf188ac0247304402207b5f76ac81af6ba8c073166b35fe1d037e5f4a3da7c5efc7191d3fa440be8f19022035f4290424dcc37aedeb7919c4682907dc0a842a1c0e448c7363461cf80d7b0a0121023d558696c04caf0cbb8202a9b6cf1b0f1880d23778dbcbb6faaf86c60d6a017857951800"]} 2 | -------------------------------------------------------------------------------- /src/chains/btc/btc_test_utils/sequential_block_and_ids/1611100-btc-block-and-txs.json: -------------------------------------------------------------------------------- 1 | {"deposit_address_list":[],"block":{"id":"00000000000002ad34d6b1928dd07d16baa48f21865f556b0e179ea002e3adec","height":1611100,"version":536870912,"timestamp":1575410117,"tx_count":1,"size":292,"weight":736,"merkle_root":"640da7bb732a89f8da83c14ac63b8fe5b1c89fe0f7eb6082fb3656ffd0709cf7","previousblockhash":"00000000000189f4bdb1c077ea400eeff22d697e1152aa98c2f07d0db37e7b8a","nonce":1723920428,"bits":436449004},"transactions":["020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2d035c951804c5d9e65d726567696f6e312f50726f6a65637420425443506f6f6c2f01000001ea65000000000000ffffffff02e40b54020000000017a914f33a3e2101d38c7634d14b06a52ba3974cf02c55870000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000"]} 2 | -------------------------------------------------------------------------------- /src/chains/btc/btc_types.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | pub use bitcoin::{ 4 | blockdata::{ 5 | block::{Block as BtcBlock, BlockHeader as BtcBlockHeader}, 6 | transaction::Transaction as BtcTransaction, 7 | }, 8 | consensus::encode::deserialize as btc_deserialize, 9 | hashes::sha256d, 10 | util::address::Address as BtcAddress, 11 | }; 12 | use serde::{Deserialize, Serialize}; 13 | 14 | use crate::{ 15 | chains::btc::{btc_constants::BTC_PUB_KEY_SLICE_LENGTH, deposit_address_info::DepositAddressInfoJson}, 16 | constants::SAFE_BTC_ADDRESS, 17 | types::{Byte, Bytes, Result}, 18 | }; 19 | 20 | pub type BtcTransactions = Vec; 21 | pub type BtcPubKeySlice = [Byte; BTC_PUB_KEY_SLICE_LENGTH]; 22 | 23 | #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] 24 | pub struct BtcUtxoAndValue { 25 | pub value: u64, 26 | pub serialized_utxo: Bytes, 27 | pub maybe_extra_data: Option, 28 | pub maybe_pointer: Option, 29 | pub maybe_deposit_info_json: Option, 30 | } 31 | 32 | pub type BtcRecipientsAndAmounts = Vec; // TODO Make this a proper type. 33 | 34 | #[derive(Clone, Debug, PartialEq, Eq)] 35 | pub struct BtcRecipientAndAmount { 36 | pub amount: u64, 37 | pub recipient: BtcAddress, 38 | } 39 | 40 | impl BtcRecipientAndAmount { 41 | pub fn new(recipient: &str, amount: u64) -> Result { 42 | Ok(BtcRecipientAndAmount { 43 | amount, 44 | recipient: match BtcAddress::from_str(recipient) { 45 | Ok(address) => address, 46 | Err(error) => { 47 | info!("✔ Error parsing BTC address for recipient: {}", error); 48 | info!("✔ Defaulting to SAFE BTC address: {}", SAFE_BTC_ADDRESS); 49 | BtcAddress::from_str(SAFE_BTC_ADDRESS)? 50 | }, 51 | }, 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/chains/btc/check_btc_parent_exists.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{btc_database_utils::get_btc_block_from_db, btc_state::BtcState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn check_for_parent_of_btc_block_in_state(state: BtcState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Checking BTC block's parent exists in database..."); 12 | match get_btc_block_from_db(&state.db, &state.get_btc_block_and_id()?.block.header.prev_blockhash) { 13 | Ok(_) => { 14 | info!("✔ BTC block's parent exists in database!"); 15 | Ok(state) 16 | }, 17 | _ => Err("✘ BTC block Rejected - no parent exists in database!".into()), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/chains/btc/core_initialization/check_btc_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::btc::btc_database_utils::get_btc_address_from_db, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn is_btc_core_initialized(db: &D) -> bool { 4 | get_btc_address_from_db(db).is_ok() 5 | } 6 | 7 | pub fn check_btc_core_is_initialized(db: &D) -> Result<()> { 8 | info!("✔ Checking BTC core is initialized..."); 9 | match is_btc_core_initialized(db) { 10 | false => Err("✘ BTC side of core not initialized!".into()), 11 | true => Ok(()), 12 | } 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | use crate::{ 19 | chains::btc::{btc_database_utils::put_btc_address_in_db, btc_test_utils::SAMPLE_TARGET_BTC_ADDRESS}, 20 | test_utils::get_test_database, 21 | }; 22 | 23 | #[test] 24 | fn should_return_false_if_btc_core_not_initialized() { 25 | let db = get_test_database(); 26 | assert!(!is_btc_core_initialized(&db)); 27 | } 28 | 29 | #[test] 30 | fn should_return_true_if_btc_core_initialized() { 31 | let db = get_test_database(); 32 | put_btc_address_in_db(&db, &SAMPLE_TARGET_BTC_ADDRESS).unwrap(); 33 | assert!(is_btc_core_initialized(&db)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/chains/btc/core_initialization/get_btc_init_output_json.rs: -------------------------------------------------------------------------------- 1 | use derive_more::Constructor; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::{ 5 | chains::btc::{ 6 | btc_database_utils::{get_btc_address_from_db, get_latest_btc_block_number}, 7 | btc_state::BtcState, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | #[derive(Clone, Debug, Serialize, Deserialize, Constructor)] 14 | pub struct BtcInitializationOutput { 15 | pub btc_address: String, 16 | pub btc_latest_block_num: u64, 17 | } 18 | 19 | fn json_stringify(output: BtcInitializationOutput) -> Result { 20 | match serde_json::to_string(&output) { 21 | Ok(res) => Ok(res), 22 | Err(err) => Err(err.into()), 23 | } 24 | } 25 | 26 | pub fn get_btc_init_output_json(state: BtcState) -> Result { 27 | json_stringify(BtcInitializationOutput::new( 28 | get_btc_address_from_db(&state.db)?, 29 | get_latest_btc_block_number(&state.db)?, 30 | )) 31 | } 32 | -------------------------------------------------------------------------------- /src/chains/btc/core_initialization/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod btc_init_utils; 2 | pub(crate) mod check_btc_core_is_initialized; 3 | pub(crate) mod generate_and_store_btc_keys; 4 | pub(crate) mod get_btc_init_output_json; 5 | pub(crate) mod initialize_btc_core; 6 | -------------------------------------------------------------------------------- /src/chains/btc/filter_minting_params.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::btc::btc_state::BtcState, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn maybe_filter_out_value_too_low_btc_on_eth_minting_params_in_state( 4 | state: BtcState, 5 | ) -> Result> { 6 | state 7 | .btc_on_eth_minting_params 8 | .filter_out_value_too_low() 9 | .and_then(|params| state.replace_btc_on_eth_minting_params(params)) 10 | } 11 | -------------------------------------------------------------------------------- /src/chains/btc/get_btc_block_in_db_format.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{btc_block::BtcBlockInDbFormat, btc_state::BtcState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn create_btc_block_in_db_format_and_put_in_state(state: BtcState) -> Result> { 8 | info!("✔ Creating DB formatted BTC block from block in state..."); 9 | let block = state.get_btc_block_and_id()?.clone(); 10 | let eth_minting_params = state.btc_on_eth_minting_params.clone(); 11 | let eos_minting_params = state.btc_on_eos_minting_params.clone(); 12 | let extra_data = vec![]; 13 | state.add_btc_block_in_db_format(BtcBlockInDbFormat::new( 14 | block.height, 15 | block.id, 16 | extra_data, 17 | Some(eos_minting_params), 18 | Some(eth_minting_params), 19 | block.block.header.prev_blockhash, 20 | )) 21 | } 22 | -------------------------------------------------------------------------------- /src/chains/btc/get_deposit_info_hash_map.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | chains::btc::{ 5 | btc_state::BtcState, 6 | deposit_address_info::{DepositAddressInfo, DepositInfoHashMap}, 7 | }, 8 | traits::DatabaseInterface, 9 | types::Result, 10 | }; 11 | 12 | pub fn create_hash_map_from_deposit_info_list(deposit_info_list: &[DepositAddressInfo]) -> Result { 13 | let mut hash_map = HashMap::new(); 14 | deposit_info_list.iter().for_each(|deposit_info| { 15 | hash_map.insert(deposit_info.btc_deposit_address.clone(), deposit_info.clone()); 16 | }); 17 | Ok(hash_map) 18 | } 19 | 20 | pub fn get_deposit_info_hash_map_and_put_in_state(state: BtcState) -> Result> { 21 | create_hash_map_from_deposit_info_list(&state.get_btc_block_and_id()?.deposit_address_list) 22 | .and_then(|hash_map| state.add_deposit_info_hash_map(hash_map)) 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::*; 28 | use crate::chains::btc::btc_test_utils::get_sample_btc_block_and_id; 29 | 30 | #[test] 31 | fn should_create_hash_map_from_deposit_info_list() { 32 | let address_info_list = get_sample_btc_block_and_id().unwrap().deposit_address_list; 33 | let result = create_hash_map_from_deposit_info_list(&address_info_list).unwrap(); 34 | assert!(!result.is_empty()); 35 | assert_eq!(result.len(), address_info_list.len()); 36 | result 37 | .iter() 38 | .for_each(|(key, value)| assert_eq!(key, &value.btc_deposit_address)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/chains/btc/increment_any_sender_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{btc::btc_state::BtcState, eth::eth_database_utils::increment_any_sender_nonce_in_db}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_increment_any_sender_nonce_in_db(state: BtcState) -> Result> { 8 | if !state.use_any_sender_tx_type() { 9 | info!("✔ Not incrementing AnySender nonce - not an AnySender transaction!"); 10 | return Ok(state); 11 | } 12 | match state.get_eth_signed_txs() { 13 | Err(_) => { 14 | info!("✔ Not incrementing AnySender nonce - no signatures made!"); 15 | Ok(state) 16 | }, 17 | Ok(signed_txs) => { 18 | info!("✔ Incrementing AnySender nonce by {}", signed_txs.len()); 19 | increment_any_sender_nonce_in_db(&state.db, signed_txs.len() as u64).map(|_| state) 20 | }, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/chains/btc/increment_btc_account_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | btc::btc_database_utils::{get_btc_account_nonce_from_db, put_btc_account_nonce_in_db}, 4 | eos::eos_state::EosState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn increment_btc_account_nonce(db: &D, current_nonce: u64, num_signatures: u64) -> Result<()> 11 | where 12 | D: DatabaseInterface, 13 | { 14 | let new_nonce = num_signatures + current_nonce; 15 | info!( 16 | "✔ Incrementing btc account nonce by {} nonce from {} to {}", 17 | num_signatures, current_nonce, new_nonce 18 | ); 19 | put_btc_account_nonce_in_db(db, new_nonce) 20 | } 21 | 22 | pub fn maybe_increment_btc_signature_nonce_and_return_eos_state(state: EosState) -> Result> 23 | where 24 | D: DatabaseInterface, 25 | { 26 | let num_txs = &state.btc_on_eos_signed_txs.len(); 27 | match num_txs { 28 | 0 => { 29 | info!("✔ No signatures in state ∴ not incrementing nonce"); 30 | Ok(state) 31 | }, 32 | _ => increment_btc_account_nonce(&state.db, get_btc_account_nonce_from_db(&state.db)?, *num_txs as u64) 33 | .and(Ok(state)), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/chains/btc/increment_eos_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | btc::btc_state::BtcState, 4 | eos::eos_database_utils::{get_eos_account_nonce_from_db, put_eos_account_nonce_in_db}, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | fn increment_eos_nonce(db: &D, current_nonce: u64, num_signatures: u64) -> Result<()> { 11 | debug!("✔ Incrementing EOS nonce from {} to {}", current_nonce, num_signatures); 12 | put_eos_account_nonce_in_db(db, current_nonce + num_signatures) 13 | } 14 | 15 | pub fn maybe_increment_eos_nonce(state: BtcState) -> Result> { 16 | let num_txs = &state.signed_txs.len(); 17 | match num_txs { 18 | 0 => { 19 | info!("✔ No EOS signatures in state ∴ not incrementing nonce"); 20 | Ok(state) 21 | }, 22 | _ => increment_eos_nonce(&state.db, get_eos_account_nonce_from_db(&state.db)?, *num_txs as u64).and(Ok(state)), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/chains/btc/increment_eth_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{btc::btc_state::BtcState, eth::eth_database_utils::increment_eth_account_nonce_in_db}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_increment_eth_nonce_in_db(state: BtcState) -> Result> { 8 | if state.use_any_sender_tx_type() { 9 | info!("✔ Not incrementing ETH account nonce due to using AnySender transactions instead!"); 10 | return Ok(state); 11 | } 12 | match state.get_eth_signed_txs() { 13 | Err(_) => { 14 | info!("✔ Not incrementing ETH account nonce - no signatures made!"); 15 | Ok(state) 16 | }, 17 | Ok(signed_txs) => { 18 | info!("✔ Incrementing ETH account nonce by {}", signed_txs.len()); 19 | increment_eth_account_nonce_in_db(&state.db, signed_txs.len() as u64).and(Ok(state)) 20 | }, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/chains/btc/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod btc_constants; 2 | pub mod btc_utils; 3 | pub mod deposit_address_info; 4 | pub mod filter_utxos; 5 | pub mod utxo_manager; 6 | 7 | pub(crate) mod add_btc_block_to_db; 8 | pub(crate) mod btc_block; 9 | pub(crate) mod btc_chain_id; 10 | pub(crate) mod btc_crypto; 11 | pub(crate) mod btc_database_utils; 12 | pub(crate) mod btc_debug_functions; 13 | pub(crate) mod btc_enclave_state; 14 | pub(crate) mod btc_state; 15 | pub(crate) mod btc_submission_material; 16 | pub(crate) mod btc_test_utils; 17 | pub(crate) mod btc_transaction; 18 | pub(crate) mod btc_types; 19 | pub(crate) mod check_btc_parent_exists; 20 | pub(crate) mod core_initialization; 21 | pub(crate) mod extract_utxos_from_p2pkh_txs; 22 | pub(crate) mod extract_utxos_from_p2sh_txs; 23 | pub(crate) mod filter_minting_params; 24 | pub(crate) mod filter_p2pkh_deposit_txs; 25 | pub(crate) mod filter_p2sh_deposit_txs; 26 | pub(crate) mod get_btc_block_in_db_format; 27 | pub(crate) mod get_deposit_info_hash_map; 28 | pub(crate) mod increment_any_sender_nonce; 29 | pub(crate) mod increment_btc_account_nonce; 30 | pub(crate) mod increment_eos_nonce; 31 | pub(crate) mod increment_eth_nonce; 32 | pub(crate) mod remove_minting_params_from_canon_block; 33 | pub(crate) mod remove_old_btc_tail_block; 34 | pub(crate) mod save_utxos_to_db; 35 | pub(crate) mod set_btc_anchor_block_hash; 36 | pub(crate) mod set_btc_canon_block_hash; 37 | pub(crate) mod set_btc_latest_block_hash; 38 | pub(crate) mod set_flags; 39 | pub(crate) mod update_btc_canon_block_hash; 40 | pub(crate) mod update_btc_latest_block_hash; 41 | pub(crate) mod update_btc_linker_hash; 42 | pub(crate) mod update_btc_tail_block_hash; 43 | pub(crate) mod validate_btc_block_header; 44 | pub(crate) mod validate_btc_difficulty; 45 | pub(crate) mod validate_btc_merkle_root; 46 | pub(crate) mod validate_btc_proof_of_work; 47 | -------------------------------------------------------------------------------- /src/chains/btc/remove_minting_params_from_canon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_database_utils::{get_btc_canon_block_from_db, put_btc_canon_block_in_db}, 4 | btc_state::BtcState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | fn remove_minting_params_from_canon_block(db: &D) -> Result<()> { 11 | get_btc_canon_block_from_db(db) 12 | .and_then(|canon_block| canon_block.remove_minting_params()) 13 | .and_then(|canon_block| put_btc_canon_block_in_db(db, &canon_block)) 14 | } 15 | 16 | pub fn remove_minting_params_from_canon_block_and_return_state(state: BtcState) -> Result> 17 | where 18 | D: DatabaseInterface, 19 | { 20 | info!("✔ Removing minting params from canon block..."); 21 | remove_minting_params_from_canon_block(&state.db).and(Ok(state)) 22 | } 23 | -------------------------------------------------------------------------------- /src/chains/btc/save_utxos_to_db.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{btc_state::BtcState, utxo_manager::utxo_database_utils::save_utxos_to_db}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_save_utxos_to_db(state: BtcState) -> Result> { 8 | info!("✔ Maybe saving UTXOs..."); 9 | match &state.utxos_and_values.len() { 10 | 0 => { 11 | info!("✔ No UTXOs in state to save!"); 12 | Ok(state) 13 | }, 14 | _ => save_utxos_to_db(&state.db, &state.utxos_and_values).and(Ok(state)), 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/chains/btc/set_btc_anchor_block_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_constants::BTC_ANCHOR_BLOCK_HASH_KEY, 4 | btc_database_utils::{key_exists_in_db, put_btc_anchor_block_hash_in_db}, 5 | btc_state::BtcState, 6 | }, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | pub fn is_btc_anchor_block_hash_set(db: &D) -> bool { 12 | key_exists_in_db(db, &BTC_ANCHOR_BLOCK_HASH_KEY.to_vec(), None) 13 | } 14 | 15 | pub fn maybe_set_btc_anchor_block_hash(state: BtcState) -> Result> { 16 | info!("✔ Checking BTC anchor block hash is set in database..."); 17 | match is_btc_anchor_block_hash_set(&state.db) { 18 | true => { 19 | info!("✔ BTC anchor block hash set in database"); 20 | Ok(state) 21 | }, 22 | false => { 23 | info!("✔ Setting BTC anchor hash from block in state..."); 24 | put_btc_anchor_block_hash_in_db(&state.db, &state.get_btc_block_and_id()?.id).and(Ok(state)) 25 | }, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/chains/btc/set_btc_canon_block_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_constants::BTC_CANON_BLOCK_HASH_KEY, 4 | btc_database_utils::{key_exists_in_db, put_btc_canon_block_hash_in_db}, 5 | btc_state::BtcState, 6 | }, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | pub fn maybe_set_btc_canon_block_hash(state: BtcState) -> Result> { 12 | info!("✔ Checking BTC canon block hash is set in database..."); 13 | match key_exists_in_db(&state.db, &BTC_CANON_BLOCK_HASH_KEY.to_vec(), None) { 14 | true => { 15 | info!("✔ BTC canon block hash set in database!"); 16 | Ok(state) 17 | }, 18 | false => { 19 | info!("✔ Setting BTC canon block hash from block in state..."); 20 | put_btc_canon_block_hash_in_db(&state.db, &state.get_btc_block_and_id()?.id).and(Ok(state)) 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/chains/btc/set_btc_latest_block_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_constants::BTC_LATEST_BLOCK_HASH_KEY, 4 | btc_database_utils::{key_exists_in_db, put_btc_latest_block_hash_in_db}, 5 | btc_state::BtcState, 6 | }, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | pub fn maybe_set_btc_latest_block_hash(state: BtcState) -> Result> { 12 | info!("✔ Checking BTC latest block hash is set in database..."); 13 | match key_exists_in_db(&state.db, &BTC_LATEST_BLOCK_HASH_KEY.to_vec(), None) { 14 | true => { 15 | info!("✔ BTC latest block hash set in database"); 16 | Ok(state) 17 | }, 18 | false => { 19 | info!("✔ Initializing BTC latest block hash from in block..."); 20 | put_btc_latest_block_hash_in_db(&state.db, &state.get_btc_block_and_id()?.id).and(Ok(state)) 21 | }, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/chains/btc/set_flags.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::btc::btc_state::BtcState, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn set_any_sender_flag_in_state(state: BtcState) -> Result> { 4 | info!("✔ Setting `AnySender` flag in BTC state..."); 5 | let any_sender = state.get_btc_submission_json()?.any_sender; 6 | state.add_any_sender_flag(any_sender) 7 | } 8 | -------------------------------------------------------------------------------- /src/chains/btc/update_btc_canon_block_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_block::BtcBlockInDbFormat, 4 | btc_database_utils::{ 5 | get_btc_canon_block_from_db, 6 | get_btc_canon_to_tip_length_from_db, 7 | get_btc_latest_block_from_db, 8 | maybe_get_nth_ancestor_btc_block_and_id, 9 | put_btc_canon_block_hash_in_db, 10 | }, 11 | btc_state::BtcState, 12 | }, 13 | traits::DatabaseInterface, 14 | types::Result, 15 | }; 16 | 17 | fn does_canon_block_require_updating(db: &D, calculated_canon_block: &BtcBlockInDbFormat) -> Result 18 | where 19 | D: DatabaseInterface, 20 | { 21 | info!("✔ Checking if BTC canon block needs updating..."); 22 | get_btc_canon_block_from_db(db) 23 | .map(|db_canon_block_and_receipts| db_canon_block_and_receipts.height < calculated_canon_block.height) 24 | } 25 | 26 | pub fn maybe_update_btc_canon_block_hash(state: BtcState) -> Result> 27 | where 28 | D: DatabaseInterface, 29 | { 30 | info!("✔ Maybe updating BTC canon block hash..."); 31 | let canon_to_tip_length = get_btc_canon_to_tip_length_from_db(&state.db)?; 32 | get_btc_latest_block_from_db(&state.db) 33 | .map(|latest_btc_block| { 34 | maybe_get_nth_ancestor_btc_block_and_id(&state.db, &latest_btc_block.id, canon_to_tip_length) 35 | }) 36 | .and_then(|maybe_ancester_block_and_id| match maybe_ancester_block_and_id { 37 | None => { 38 | info!( 39 | "✔ No {}th ancestor block in db yet ∴ {}", 40 | canon_to_tip_length, "not updating canon block hash!", 41 | ); 42 | Ok(state) 43 | }, 44 | Some(ancestor_block) => { 45 | info!("✔ {}th ancestor block found...", canon_to_tip_length,); 46 | match does_canon_block_require_updating(&state.db, &ancestor_block)? { 47 | false => { 48 | info!("✔ BTC canon block does not require updating"); 49 | Ok(state) 50 | }, 51 | true => { 52 | info!("✔ Updating BTC canon block..."); 53 | put_btc_canon_block_hash_in_db(&state.db, &ancestor_block.id).map(|_| state) 54 | }, 55 | } 56 | }, 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /src/chains/btc/update_btc_latest_block_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_database_utils::{get_btc_latest_block_from_db, put_btc_latest_block_hash_in_db}, 4 | btc_state::BtcState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | fn is_block_subsequent(block_in_question_height: u64, latest_block_from_database_height: u64) -> bool { 11 | latest_block_from_database_height == block_in_question_height + 1 12 | } 13 | 14 | pub fn maybe_update_btc_latest_block_hash(state: BtcState) -> Result> 15 | where 16 | D: DatabaseInterface, 17 | { 18 | get_btc_latest_block_from_db(&state.db).and_then(|latest_block_and_id| { 19 | match is_block_subsequent(latest_block_and_id.height, state.get_btc_block_and_id()?.height) { 20 | false => { 21 | info!("✔ BTC block NOT subsequent {}", "∴ NOT updating latest block hash",); 22 | Ok(state) 23 | }, 24 | true => { 25 | info!("✔ BTC block IS subsequent {}", "∴ updating latest block hash...",); 26 | put_btc_latest_block_hash_in_db(&state.db, &state.get_btc_block_and_id()?.id).map(|_| state) 27 | }, 28 | } 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/chains/btc/utxo_manager/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod debug_utxo_utils; 2 | pub mod utxo_constants; 3 | pub mod utxo_database_utils; 4 | pub mod utxo_types; 5 | pub mod utxo_utils; 6 | -------------------------------------------------------------------------------- /src/chains/btc/utxo_manager/utxo_constants.rs: -------------------------------------------------------------------------------- 1 | pub use serde_json::{json, Value as JsonValue}; 2 | 3 | use crate::utils::get_prefixed_db_key; 4 | 5 | pub fn get_utxo_constants_db_keys() -> JsonValue { 6 | json!({ 7 | "UTXO_LAST": 8 | hex::encode(UTXO_LAST.to_vec()), 9 | "UTXO_FIRST": 10 | hex::encode(UTXO_FIRST.to_vec()), 11 | "UTXO_NONCE": 12 | hex::encode(UTXO_NONCE.to_vec()), 13 | "UTXO_BALANCE": 14 | hex::encode(UTXO_BALANCE.to_vec()), 15 | }) 16 | } 17 | 18 | lazy_static! { 19 | pub static ref UTXO_FIRST: [u8; 32] = get_prefixed_db_key("utxo-first"); 20 | } 21 | 22 | lazy_static! { 23 | pub static ref UTXO_LAST: [u8; 32] = get_prefixed_db_key("utxo-last"); 24 | } 25 | 26 | lazy_static! { 27 | pub static ref UTXO_BALANCE: [u8; 32] = get_prefixed_db_key("utxo-balance"); 28 | } 29 | 30 | lazy_static! { 31 | pub static ref UTXO_NONCE: [u8; 32] = get_prefixed_db_key("utxo-nonce"); 32 | } 33 | -------------------------------------------------------------------------------- /src/chains/btc/validate_btc_merkle_root.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::blockdata::block::Block as BtcBlock; 2 | 3 | use crate::{ 4 | chains::btc::btc_state::BtcState, 5 | constants::{CORE_IS_VALIDATING, DEBUG_MODE, NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR}, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | fn validate_merkle_root(btc_block: &BtcBlock) -> Result<()> { 11 | match btc_block.check_merkle_root() { 12 | true => { 13 | info!("✔ Merkle-root valid!"); 14 | Ok(()) 15 | }, 16 | false => Err("✘ Invalid block! Merkle root doesn't match calculated merkle root!".into()), 17 | } 18 | } 19 | 20 | pub fn validate_btc_merkle_root(state: BtcState) -> Result> 21 | where 22 | D: DatabaseInterface, 23 | { 24 | if CORE_IS_VALIDATING { 25 | info!("✔ Validating merkle-root in BTC block..."); 26 | validate_merkle_root(&state.get_btc_block_and_id()?.block).map(|_| state) 27 | } else { 28 | info!("✔ Skipping BTC merkle-root validation!"); 29 | match DEBUG_MODE { 30 | true => Ok(state), 31 | false => Err(NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR.into()), 32 | } 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | use crate::chains::btc::btc_test_utils::get_sample_btc_block_and_id; 40 | 41 | #[test] 42 | fn should_validate_sample_merkle_root() { 43 | let block = get_sample_btc_block_and_id().unwrap().block; 44 | if let Err(e) = validate_merkle_root(&block) { 45 | panic!("Merkle root should be valid for samle block: {}", e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/chains/btc/validate_btc_proof_of_work.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::blockdata::block::BlockHeader as BtcBlockHeader; 2 | 3 | use crate::{ 4 | chains::btc::btc_state::BtcState, 5 | constants::{CORE_IS_VALIDATING, DEBUG_MODE, NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR}, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | fn validate_proof_of_work_in_block(btc_block_header: &BtcBlockHeader) -> Result<()> { 11 | match btc_block_header.validate_pow(&btc_block_header.target()) { 12 | Ok(_) => { 13 | info!("✔ BTC block's proof-of-work is valid!"); 14 | Ok(()) 15 | }, 16 | Err(_) => Err("✘ Invalid block! PoW validation error: Block hash > target!".into()), 17 | } 18 | } 19 | 20 | pub fn validate_proof_of_work_of_btc_block_in_state(state: BtcState) -> Result> 21 | where 22 | D: DatabaseInterface, 23 | { 24 | if CORE_IS_VALIDATING { 25 | info!("✔ Validating BTC block's proof-of-work..."); 26 | validate_proof_of_work_in_block(&state.get_btc_block_and_id()?.block.header).map(|_| state) 27 | } else { 28 | info!("✔ Skipping BTC proof-of-work validation!"); 29 | match DEBUG_MODE { 30 | true => Ok(state), 31 | false => Err(NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR.into()), 32 | } 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | use crate::chains::btc::btc_test_utils::get_sample_btc_block_and_id; 40 | 41 | #[test] 42 | fn should_validate_proof_of_work_in_valid_block() { 43 | let block_header = get_sample_btc_block_and_id().unwrap().block.header; 44 | if let Err(e) = validate_proof_of_work_in_block(&block_header) { 45 | panic!("PoW should be valid in sample block: {}", e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/chains/eos/add_schedule.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_database_utils::put_eos_schedule_in_db, eos_state::EosState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_add_new_eos_schedule_to_db_and_return_state(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | match &state.get_eos_block_header()?.new_producer_schedule { 12 | None => { 13 | info!("✔ No new schedule in block ∴ nothing to add to db!"); 14 | Ok(state) 15 | }, 16 | Some(new_schedule) => { 17 | info!( 18 | "✔ New producers schedule version {} found in EOS block, adding to db...", 19 | new_schedule.version 20 | ); 21 | put_eos_schedule_in_db(&state.db, new_schedule).and(Ok(state)) 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/chains/eos/append_interim_block_ids.rs: -------------------------------------------------------------------------------- 1 | use eos_chain::Checksum256; 2 | 3 | use crate::{ 4 | chains::eos::{eos_merkle_utils::Incremerkle, eos_state::EosState}, 5 | traits::DatabaseInterface, 6 | types::Result, 7 | }; 8 | 9 | pub fn append_block_ids_to_incremerkle(mut incremerkle: Incremerkle, block_ids: &[Checksum256]) -> Result { 10 | block_ids.iter().for_each(|id| { 11 | incremerkle.append(*id).ok(); 12 | }); 13 | Ok(incremerkle) 14 | } 15 | 16 | pub fn append_interim_block_ids_to_incremerkle_in_state(state: EosState) -> Result> 17 | where 18 | D: DatabaseInterface, 19 | { 20 | info!("✔ Appending interim block IDs to incremerkle..."); 21 | append_block_ids_to_incremerkle(state.incremerkle.clone(), &state.interim_block_ids) 22 | .map(|incremerkle| state.add_incremerkle(incremerkle)) 23 | } 24 | -------------------------------------------------------------------------------- /src/chains/eos/core_initialization/check_eos_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::eos::eos_crypto::eos_private_key::EosPrivateKey, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn is_eos_core_initialized(db: &D) -> bool { 4 | EosPrivateKey::get_from_db(db).is_ok() 5 | } 6 | 7 | pub fn check_eos_core_is_initialized(db: &D) -> Result<()> { 8 | info!("✔ Checking EOS core is initialized..."); 9 | match is_eos_core_initialized(db) { 10 | false => Err("✘ EOS side of core not initialized!".into()), 11 | true => Ok(()), 12 | } 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | use crate::{ 19 | chains::eos::eos_test_utils::get_sample_eos_private_key, 20 | errors::AppError, 21 | test_utils::get_test_database, 22 | }; 23 | 24 | #[test] 25 | fn should_return_true_if_eos_core_initialized() { 26 | let db = get_test_database(); 27 | let pk = get_sample_eos_private_key(); 28 | pk.write_to_db(&db).unwrap(); 29 | let result = is_eos_core_initialized(&db); 30 | assert!(result); 31 | } 32 | 33 | #[test] 34 | fn should_return_false_if_eos_core_not_initialized() { 35 | let db = get_test_database(); 36 | let result = is_eos_core_initialized(&db); 37 | assert!(!result); 38 | } 39 | 40 | #[test] 41 | fn should_be_ok_if_eos_core_initialized() { 42 | let db = get_test_database(); 43 | let pk = get_sample_eos_private_key(); 44 | pk.write_to_db(&db).unwrap(); 45 | let result = check_eos_core_is_initialized(&db); 46 | assert!(result.is_ok()); 47 | } 48 | 49 | #[test] 50 | fn should_err_if_eos_core_not_initialized() { 51 | let db = get_test_database(); 52 | let expected_err = "✘ EOS side of core not initialized!".to_string(); 53 | match check_eos_core_is_initialized(&db) { 54 | Err(AppError::Custom(err)) => assert_eq!(err, expected_err), 55 | Ok(_) => panic!("Should not have succeeded!"), 56 | Err(_) => panic!("Wrong error received!"), 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/chains/eos/core_initialization/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod check_eos_core_is_initialized; 2 | pub(crate) mod eos_init_utils; 3 | pub(crate) mod initialize_eos_core; 4 | -------------------------------------------------------------------------------- /src/chains/eos/disable_protocol_feature.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{ 3 | core_initialization::check_eos_core_is_initialized::check_eos_core_is_initialized, 4 | eos_database_transactions::{ 5 | end_eos_db_transaction_and_return_state, 6 | start_eos_db_transaction_and_return_state, 7 | }, 8 | eos_database_utils::put_eos_enabled_protocol_features_in_db, 9 | eos_state::EosState, 10 | get_enabled_protocol_features::get_enabled_protocol_features_and_add_to_state, 11 | protocol_features::{EnabledFeatures, AVAILABLE_FEATURES}, 12 | }, 13 | traits::DatabaseInterface, 14 | types::{Byte, Result}, 15 | }; 16 | 17 | pub fn disable_protocol_feature( 18 | db: &D, 19 | feature_hash: &[Byte], 20 | enabled_features: &EnabledFeatures, 21 | ) -> Result<()> { 22 | AVAILABLE_FEATURES.check_contains(feature_hash).and_then(|_| { 23 | if enabled_features.is_not_enabled(feature_hash) { 24 | return Err("✘ Feature not enabled, doing nothing!".into()); 25 | } 26 | info!("✔ Disabling feature: {}", hex::encode(feature_hash)); 27 | enabled_features 28 | .clone() 29 | .remove(feature_hash) 30 | .and_then(|new_features| put_eos_enabled_protocol_features_in_db(db, &new_features)) 31 | }) 32 | } 33 | 34 | fn disable_feature_and_return_state(state: EosState, hash: &[Byte]) -> Result> { 35 | disable_protocol_feature(&state.db, hash, &state.enabled_protocol_features).and(Ok(state)) 36 | } 37 | 38 | pub fn disable_eos_protocol_feature(db: D, feature_hash: &str) -> Result { 39 | info!("✔ Maybe disabling EOS protocol feature w/ hash: {}", feature_hash); 40 | let hash = hex::decode(feature_hash)?; 41 | check_eos_core_is_initialized(&db) 42 | .and_then(|_| start_eos_db_transaction_and_return_state(EosState::init(db))) 43 | .and_then(get_enabled_protocol_features_and_add_to_state) 44 | .and_then(|state| disable_feature_and_return_state(state, &hash)) 45 | .and_then(end_eos_db_transaction_and_return_state) 46 | .map(|_| "{feature_disabled_success:true}".to_string()) 47 | } 48 | -------------------------------------------------------------------------------- /src/chains/eos/enable_protocol_feature.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{ 3 | core_initialization::check_eos_core_is_initialized::check_eos_core_is_initialized, 4 | eos_database_transactions::{ 5 | end_eos_db_transaction_and_return_state, 6 | start_eos_db_transaction_and_return_state, 7 | }, 8 | eos_database_utils::put_eos_enabled_protocol_features_in_db, 9 | eos_state::EosState, 10 | get_enabled_protocol_features::get_enabled_protocol_features_and_add_to_state, 11 | protocol_features::{EnabledFeatures, AVAILABLE_FEATURES}, 12 | }, 13 | traits::DatabaseInterface, 14 | types::{Byte, Result}, 15 | }; 16 | 17 | pub fn enable_protocol_feature( 18 | db: &D, 19 | feature_hash: &[Byte], 20 | enabled_features: &EnabledFeatures, 21 | ) -> Result<()> { 22 | AVAILABLE_FEATURES.check_contains(feature_hash).and_then(|_| { 23 | if enabled_features.is_enabled(feature_hash) { 24 | return Err("✘ Feature already enabled, doing nothing!".into()); 25 | } 26 | info!("✔ Enabling new feature: {}", hex::encode(feature_hash)); 27 | enabled_features 28 | .clone() 29 | .add(feature_hash) 30 | .and_then(|new_features| put_eos_enabled_protocol_features_in_db(db, &new_features)) 31 | }) 32 | } 33 | 34 | fn enable_feature_and_return_state(state: EosState, hash: &[Byte]) -> Result> { 35 | enable_protocol_feature(&state.db, hash, &state.enabled_protocol_features).and(Ok(state)) 36 | } 37 | 38 | pub fn enable_eos_protocol_feature(db: D, feature_hash: &str) -> Result { 39 | info!("✔ Maybe enabling EOS protocol feature w/ hash: {}", feature_hash); 40 | let hash = hex::decode(feature_hash)?; 41 | check_eos_core_is_initialized(&db) 42 | .and_then(|_| start_eos_db_transaction_and_return_state(EosState::init(db))) 43 | .and_then(get_enabled_protocol_features_and_add_to_state) 44 | .and_then(|state| enable_feature_and_return_state(state, &hash)) 45 | .and_then(end_eos_db_transaction_and_return_state) 46 | .map(|_| "{feature_enabled_success:true}".to_string()) 47 | } 48 | -------------------------------------------------------------------------------- /src/chains/eos/eos_crypto/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod eos_private_key; 2 | pub mod eos_public_key; 3 | pub mod eos_signature; 4 | pub mod eos_transaction; 5 | -------------------------------------------------------------------------------- /src/chains/eos/eos_database_transactions.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::eos::eos_state::EosState, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn start_eos_db_transaction_and_return_state(state: EosState) -> Result> 4 | where 5 | D: DatabaseInterface, 6 | { 7 | state.db.start_transaction().map(|_| { 8 | info!("✔ Database transaction begun for EOS block submission!"); 9 | state 10 | }) 11 | } 12 | 13 | pub fn end_eos_db_transaction_and_return_state(state: EosState) -> Result> 14 | where 15 | D: DatabaseInterface, 16 | { 17 | state.db.end_transaction().map(|_| { 18 | info!("✔ Database transaction ended for EOS block submission!"); 19 | state 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /src/chains/eos/eos_extension.rs: -------------------------------------------------------------------------------- 1 | use derive_more::{Constructor, Deref}; 2 | use eos_chain::{NumBytes, Read, Write}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive( 6 | Read, Write, Deserialize, Serialize, NumBytes, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash, Default, 7 | )] 8 | #[eosio_core_root_path = "eos_chain"] 9 | pub struct EosExtension(pub u16, pub Vec); 10 | 11 | impl EosExtension { 12 | pub fn from_hex(hex: &str) -> crate::Result { 13 | let bytes = hex::decode(hex)?; 14 | let mut array = [0; 2]; 15 | let u16_bytes = &bytes[..array.len()]; 16 | array.copy_from_slice(u16_bytes); 17 | let u_16 = u16::from_le_bytes(array); 18 | Ok(Self(u_16, bytes[2..].to_vec())) 19 | } 20 | } 21 | 22 | impl core::fmt::Display for EosExtension { 23 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 24 | write!(f, "{}, {}", self.0, hex::encode(&self.1)) 25 | } 26 | } 27 | 28 | #[derive(Debug, Deref, Constructor)] 29 | pub struct EosExtensions(pub Vec); 30 | 31 | impl EosExtensions { 32 | pub fn from_hex_strings(hex_strings: &[String]) -> crate::Result { 33 | Ok(Self( 34 | hex_strings 35 | .iter() 36 | .map(|hex| EosExtension::from_hex(hex)) 37 | .collect::>>()?, 38 | )) 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | 46 | #[test] 47 | fn should_convert_hex_string_to_extension() { 48 | let hex = "01030307"; 49 | let expected_result = EosExtension(769, vec![3u8, 7u8]); 50 | let result = EosExtension::from_hex(&hex).unwrap(); 51 | assert_eq!(result, expected_result); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/chains/eos/eos_metadata.rs: -------------------------------------------------------------------------------- 1 | use derive_more::Constructor; 2 | use eos_chain::{NumBytes, Read, SerializeData, Write}; 3 | 4 | use crate::types::{Byte, Bytes}; 5 | 6 | #[derive(Clone, Debug, Read, Write, NumBytes, PartialEq, Default, Constructor)] 7 | #[eosio_core_root_path = "eos_chain"] 8 | pub struct EosMetadata { 9 | pub version: Byte, 10 | pub user_data: Bytes, 11 | pub metadata_chain_id: Bytes, 12 | pub origin_address: String, 13 | } 14 | 15 | impl SerializeData for EosMetadata {} 16 | 17 | impl EosMetadata { 18 | pub fn to_bytes(&self) -> crate::types::Result { 19 | Ok(self.to_serialize_data()?) 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | use crate::metadata::metadata_chain_id::MetadataChainId; 27 | 28 | #[test] 29 | fn should_serialize_eos_metadata() { 30 | let metadata = EosMetadata::new( 31 | 1u8, 32 | vec![0xde, 0xca, 0xff], 33 | MetadataChainId::EthereumRopsten.to_bytes().unwrap(), 34 | "0xfEDFe2616EB3661CB8FEd2782F5F0cC91D59DCaC".to_string(), 35 | ); 36 | let serialized = metadata.to_bytes().unwrap(); 37 | let result = hex::encode(&serialized); 38 | let expected_result = "0103decaff040069c3222a307866454446653236313645423336363143423846456432373832463546306343393144353944436143"; 39 | assert_eq!(result, expected_result); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/chains/eos/eos_producer_key.rs: -------------------------------------------------------------------------------- 1 | use eos_chain::{AccountName as EosAccountName, NumBytes, PublicKey as EosPublicKey, Read, Write}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | pub type Authority = (u8, EosKeysAndThreshold); 5 | 6 | #[derive(Deserialize, Serialize, Read, Write, NumBytes, Clone, Default, Debug, PartialEq)] 7 | #[eosio_core_root_path = "eos_chain"] 8 | pub struct EosProducerKeyV1 { 9 | pub producer_name: EosAccountName, 10 | pub block_signing_key: EosPublicKey, 11 | } 12 | 13 | impl EosProducerKeyV1 { 14 | pub fn new(producer_name: EosAccountName, block_signing_key: EosPublicKey) -> Self { 15 | EosProducerKeyV1 { 16 | producer_name, 17 | block_signing_key, 18 | } 19 | } 20 | } 21 | 22 | #[derive(Deserialize, Serialize, Read, Write, NumBytes, Clone, Default, Debug, PartialEq)] 23 | #[eosio_core_root_path = "eos_chain"] 24 | pub struct EosProducerKeyV2 { 25 | pub producer_name: EosAccountName, 26 | pub authority: Authority, 27 | } 28 | 29 | #[derive(Read, Write, NumBytes, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] 30 | #[eosio_core_root_path = "eos_chain"] 31 | pub struct EosKeysAndThreshold { 32 | pub threshold: u32, 33 | pub keys: Vec, 34 | } 35 | 36 | #[derive(Read, Write, NumBytes, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] 37 | #[eosio_core_root_path = "eos_chain"] 38 | pub struct EosKey { 39 | pub key: EosPublicKey, 40 | pub weight: u16, 41 | } 42 | -------------------------------------------------------------------------------- /src/chains/eos/eos_test_utils/jungle-3-block-8242000.json: -------------------------------------------------------------------------------- 1 | {"block_header":{"timestamp":"2020-04-07T12:32:42.500","producer":"gorillapower","confirmed":0,"previous":"007dc34fb5e32374081a3a498ebe2ae08d6d7339aab665575e355baa6fbd8337","transaction_mroot":"0000000000000000000000000000000000000000000000000000000000000000","action_mroot":"fd32252549af171b3d6939305c33755fb020ca78c9f4406f109b0fb38b0d5e1a","schedule_version":28,"new_producers":null,"producer_signature":"SIG_K1_Kek7Q58bpQsoAjfNrVsjHnK1QE3MHHxoeqZNJo5SwVuSH79k6aYWqKsBUpipAQYmCjWCV55md8PrYonmPkgtz1NHMs43w9","transactions":[],"block_id":"007dc3507de907e370ec0ba846ee5bc83133d78ee4d9e302e497b3a33bd443e8","block_num":8242000},"interim_block_ids":["007dc34fb5e32374081a3a498ebe2ae08d6d7339aab665575e355baa6fbd8337","b53ee26847b872ff6555998ce61aa0ee9140dc1c566b7de1a432a5fd1ce50a5b","1f5ebfbcee64c93f9a987b35fde84e9c30fb8faa503e052f9f28227f76a39733","d298fcfed7e772ce797e551192db712cfc22dfb1062e964df27d56e7f0f437d0","0dd7789d2c81cd3672ecc5d0eea36f41d248a366147e94fedc98ebe5d60e88dd","bdfef0e6a9aa2d650f65640b939d0d8cb7cf8d30fee00b20053905c59b0daec7","3da425388718832ffa69bbe5c6db6a8565ab12d6d271823e7bf4801f087c6209","6d4a01ed318ba73460c0ee34643d1644434f137e12d77ffdf9db647521ef3763","3579049fe4d02ef6275ae88c0b1cf7cf11ccb7ba4f872f0b0faddd603d1d8be3","05348f63fb888b4aa435a000cb25afd9d4f02a5891c84612c60b725d4a8e0f4f","224ed29f97f9e2f881472489d90eb11b66bf2ecb4ef2c34e4f3c4939fd1ea751","ac4b7a58c8499b1c0aacc21dda0fde40639827fcc39fa598449dffc1b6d1f11f","2651ea8263273936436d0311af478fd3db0a1940648bc7558108794b684475be","797d1cfabfd7e9da73e6726a1a7995e515c2389a4a3ad89f48f8b4f55d7f8bdc","dbe2eb6187de91de39b9ecee82b403cab5b9cf540d6248350ec1c7d7ced989b1","df9d86cd49444d8b22c6a517229343b5900bb82d9309e5c8b15a41d411ecf0c5"],"action_proofs":[]} 2 | -------------------------------------------------------------------------------- /src/chains/eos/eos_test_utils/sample-active-schedule-2.json: -------------------------------------------------------------------------------- 1 | {"version":2,"producers":[{"producer_name":"batinthedark","block_signing_key":"EOS6dwoM8XGMQn49LokUcLiony7JDkbHrsFDvh5svLvPDkXtvM7oR"},{"producer_name":"bighornsheep","block_signing_key":"EOS5xfwWr4UumKm4PqUGnyCrFWYo6j5cLioNGg5yf4GgcTp2WcYxf"},{"producer_name":"bigpolarbear","block_signing_key":"EOS6oZi9WjXUcLionUtSiKRa4iwCW5cT6oTzoWZdENXq1p2pq53Nv"},{"producer_name":"clevermonkey","block_signing_key":"EOS5mp5wmRyL5RH2JUeEh3eoZxkJ2ZZJ9PVd1BcLioNuq4PRCZYxQ"},{"producer_name":"funnyhamster","block_signing_key":"EOS7A9BoRetjpKtE3sqA6HRykRJ955MjQ5XdRmCLionVte2uERL8h"},{"producer_name":"gorillapower","block_signing_key":"EOS8X5NCx1Xqa1xgQgBa9s6EK7M1SjGaDreAcLion4kDVLsjhQr9n"},{"producer_name":"hippopotamus","block_signing_key":"EOS7qDcxm8YtAZUA3t9kxNGuzpCLioNnzpTRigi5Dwsfnszckobwc"},{"producer_name":"hungryolddog","block_signing_key":"EOS6tw3AqqVUsCbchYRmxkPLqGct3vC63cEzKgVzLFcLionoY8YLQ"},{"producer_name":"iliketurtles","block_signing_key":"EOS6itYvNZwhqS7cLion3xp3rLJNJAvKKegxeS7guvbBxG1XX5uwz"},{"producer_name":"jumpingfrogs","block_signing_key":"EOS7oVWG413cLioNG7RU5Kv7NrPZovAdRSP6GZEG4LFUDWkgwNXHW"},{"producer_name":"lioninjungle","block_signing_key":"EOS5BcLionmbgEtcmu7qY6XKWaE1q31qCQSsd89zXij7FDXQnKjwk"},{"producer_name":"littlerabbit","block_signing_key":"EOS65orCLioNFkVT5uDF7J63bNUk97oF8T83iWfuvbSKWYUUq9EWd"},{"producer_name":"ohtigertiger","block_signing_key":"EOS7tigERwXDRuHsok212UDToxFS1joUhAxzvDUhRof8NjuvwtoHX"},{"producer_name":"proudrooster","block_signing_key":"EOS5qBd3T6nmLRsuACLion346Ue8UkCwvsoS5f3EDC1jwbrEiBDMX"},{"producer_name":"pythoncolors","block_signing_key":"EOS8R7GB5CLionUEy8FgGksGAGtc2cbcQWgty3MTAgzJvGTmtqPLz"},{"producer_name":"soaringeagle","block_signing_key":"EOS6iuBqJKqSK82QYCGuM96gduQpQG8xJsPDU1CLionPMGn2bT4Yn"},{"producer_name":"spideronaweb","block_signing_key":"EOS6M4CYEDt3JDKS6nsxMnUcdCLioNcbyEzeAwZsQmDcoJCgaNHT8"},{"producer_name":"ssssssssnake","block_signing_key":"EOS8SDhZ5CLioNLie9mb7kDu1gHfDXLwTvYBSxR1ccYSJERvutLqG"},{"producer_name":"thebluewhale","block_signing_key":"EOS6Wfo1wwTPzzBVT8fe3jpz8vxCnf77YscLionBnw39iGzFWokZm"},{"producer_name":"thesilentowl","block_signing_key":"EOS7y4hU89NJ658H1KmAdZ6A585bEVmSV8xBGJ3SbQM4Pt3pcLion"},{"producer_name":"wealthyhorse","block_signing_key":"EOS5i1HrfxfHLRJqbExgRodhrZwp4dcLioNn4xZWCyhoBK6DNZgZt"}]} 2 | -------------------------------------------------------------------------------- /src/chains/eos/eos_test_utils/sample-active-schedule-389.json: -------------------------------------------------------------------------------- 1 | {"version":389,"producers":[{"producer_name":"junglemorpho","block_signing_key":"EOS796kTMQF9sTjFz6cRftBXpxfH4aRSkSt9XSUV9hRxyxFkVgA2y"},{"producer_name":"eosdacserval","block_signing_key":"EOS5CJJEKDms9UTS7XBv8rb33BENRpnpSGsQkAe6bCfpjHHCKQTgH"},{"producer_name":"five.evil","block_signing_key":"EOS5Cqw76UgK2qMALWRQs7JgsMrttc8FxREViLVwUrYjEucZWhuec"},{"producer_name":"eight.evil","block_signing_key":"EOS5Cqw76UgK2qMALWRQs7JgsMrttc8FxREViLVwUrYjEucZWhuec"},{"producer_name":"ohtigertiger","block_signing_key":"EOS7tigERwXDRuHsok212UDToxFS1joUhAxzvDUhRof8NjuvwtoHX"},{"producer_name":"eosriobrazil","block_signing_key":"EOS69gkEdoQsgY9enzWosFQhj2MPqWtHGyNhsiN1meG6msV4VBoqd"},{"producer_name":"eoscafeloons","block_signing_key":"EOS6XgXr6zGhyk6p2rSHrizUYCnhHCMjz3NuvBFGRATLUueBFC2tv"},{"producer_name":"junglesweden","block_signing_key":"EOS79e4HpvQ1y1HdzRSqE1gCKhdN9kFGjjUU2nKG7xjiVCakt5WSs"},{"producer_name":"lioninjungle","block_signing_key":"EOS5g7gVXGeQVCTNW7U3gxeBJuvzvkmbCLioNqTUPYLjQXFGfQXfo"},{"producer_name":"tokenika4tst","block_signing_key":"EOS6wkp1PpqQUgEA6UtgW21Zo3o1XcQeLXzcLLgKcPJhTz2aSF6fz"},{"producer_name":"nine.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"one.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"two.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"four.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"five.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"eight.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"evilproducer","block_signing_key":"EOS5iR3RpvpSM4Gm2EViLXUzsktHxjsAKRSUu8DZwJvVT1dTGwxfR"},{"producer_name":"jungleswedem","block_signing_key":"EOS79e4HpvQ1y1HdzRSqE1gCKhdN9kFGjjUU2nKG7xjiVCakt5WSs"},{"producer_name":"atticlabjbpn","block_signing_key":"EOS5hVMcN6UVWtrNCxdp5HJwsz4USULmdfNA22UDyjRNdprXEiAP6"},{"producer_name":"ivote4eosusa","block_signing_key":"EOS8WHzxnaVoXek6mwU7BJiBbyugeqZfb2y2SKa7mVUv8atLfbcjK"}]} 2 | -------------------------------------------------------------------------------- /src/chains/eos/eos_test_utils/sample-schedule-1713-v1.json: -------------------------------------------------------------------------------- 1 | {"version":1713,"producers":[{"producer_name":"atticlabeosb","block_signing_key":"EOS7PfA3A4UdfMu2wKbuXdbHn8EWAxbMnFoFWui4X2zsr2oPwdQJP"},{"producer_name":"big.one","block_signing_key":"EOS8MpYyXwn3DLqk9Y9XTHYcd6wGGijNqJefFoQEwEoXTq1awZ42w"},{"producer_name":"bitfinexeos1","block_signing_key":"EOS4tkw7LgtURT3dvG3kQ4D1sg3aAtPDymmoatpuFkQMc7wzZdKxc"},{"producer_name":"blockpooleos","block_signing_key":"EOS61FDJz3GC42GhaPSsmKh7SxuesyZhjm7hBwBKqN52v1HukEqBu"},{"producer_name":"eosasia11111","block_signing_key":"EOS76gG6ATpqfVf5KrVjh3f4JAa4EKzAwWabTucNQ4Xv2TmVAj9bN"},{"producer_name":"eoscannonchn","block_signing_key":"EOS73cTi9V7PNg4ujW5QzoTfRSdhH44MPiUJkUV6m3oGwj7RX7kML"},{"producer_name":"eosdotwikibp","block_signing_key":"EOS7RsdDs8k8GDAdZrETnTjoGwiqAwwdNyxeH8q6fmHgpHjPPnyco"},{"producer_name":"eoseouldotio","block_signing_key":"EOS6SSA4gYCSZ3q9NWpxGsYDv5MWjSwKseyq25RRZexwj8EM6YHDa"},{"producer_name":"eosflytomars","block_signing_key":"EOS6Agpfp38bTyRjJDmB4Qb1EpQSq7wnEAsALXgXE7KFSzKjokkFD"},{"producer_name":"eoshuobipool","block_signing_key":"EOS5XKswW26cR5VQeDGwgNb5aixv1AMcKkdDNrC59KzNSBfnH6TR7"},{"producer_name":"eosiomeetone","block_signing_key":"EOS5gS4ZtanRS2Jx4vpjAQaVNci3v65iZiGCgMr9DNwu67x2pt8Qd"},{"producer_name":"eoslaomaocom","block_signing_key":"EOS8QgURqo875qu3a8vgZ58qBeu2cTehe9zAWRfpdCXAQipicu1Fi"},{"producer_name":"eosnationftw","block_signing_key":"EOS8L12yBrtx7mpewHmjwgJeNb2aLaeQdoDgMW82dzDSu17ec2XNL"},{"producer_name":"eosrapidprod","block_signing_key":"EOS8QEFsgUWj7BscQNkiremtpSoRkzwDqmCPpKKCHYXGNaqxXFQ4h"},{"producer_name":"hashfineosio","block_signing_key":"EOS7jSfvStvbKDmGvQdtrQsCyNkWczXfvh6CHmBVmeypJyHsUrMqj"},{"producer_name":"helloeoscnbp","block_signing_key":"EOS79cHpaEittzgJWgj79tdRhgzLEWy8wXmmQ3fL7kkDjmYYiGNet"},{"producer_name":"newdex.bp","block_signing_key":"EOS688SnH8tQ7NiyhamiCzWXAGPDLF9S7K8ga79UBHKFgjS1MhqhB"},{"producer_name":"okcapitalbp1","block_signing_key":"EOS6NqWZ1i9KSNoeBiby6Nmf1seAbEfhvrDoCbwSi1hV4cuqqnYRP"},{"producer_name":"starteosiobp","block_signing_key":"EOS4wZZXm994byKANLuwHD6tV3R3Mu3ktc41aSVXCBaGnXJZJ4pwF"},{"producer_name":"whaleex.com","block_signing_key":"EOS88EGcFghfQJER1mDaEe4kDJ7MGDoPmXQfA7q2QMTLLqiYP1UQR"},{"producer_name":"zbeosbp11111","block_signing_key":"EOS7rhgVPWWyfMqjSbNdndtCK8Gkza3xnDbUupsPLMZ6gjfQ4nX81"}]} 2 | -------------------------------------------------------------------------------- /src/chains/eos/eos_test_utils/sample-schedule-389-v1.json: -------------------------------------------------------------------------------- 1 | {"version":389,"producers":[{"producer_name":"junglemorpho","block_signing_key":"EOS796kTMQF9sTjFz6cRftBXpxfH4aRSkSt9XSUV9hRxyxFkVgA2y"},{"producer_name":"eosdacserval","block_signing_key":"EOS5CJJEKDms9UTS7XBv8rb33BENRpnpSGsQkAe6bCfpjHHCKQTgH"},{"producer_name":"five.evil","block_signing_key":"EOS5Cqw76UgK2qMALWRQs7JgsMrttc8FxREViLVwUrYjEucZWhuec"},{"producer_name":"eight.evil","block_signing_key":"EOS5Cqw76UgK2qMALWRQs7JgsMrttc8FxREViLVwUrYjEucZWhuec"},{"producer_name":"ohtigertiger","block_signing_key":"EOS7tigERwXDRuHsok212UDToxFS1joUhAxzvDUhRof8NjuvwtoHX"},{"producer_name":"eosriobrazil","block_signing_key":"EOS69gkEdoQsgY9enzWosFQhj2MPqWtHGyNhsiN1meG6msV4VBoqd"},{"producer_name":"eoscafeloons","block_signing_key":"EOS6XgXr6zGhyk6p2rSHrizUYCnhHCMjz3NuvBFGRATLUueBFC2tv"},{"producer_name":"junglesweden","block_signing_key":"EOS79e4HpvQ1y1HdzRSqE1gCKhdN9kFGjjUU2nKG7xjiVCakt5WSs"},{"producer_name":"lioninjungle","block_signing_key":"EOS5g7gVXGeQVCTNW7U3gxeBJuvzvkmbCLioNqTUPYLjQXFGfQXfo"},{"producer_name":"tokenika4tst","block_signing_key":"EOS6wkp1PpqQUgEA6UtgW21Zo3o1XcQeLXzcLLgKcPJhTz2aSF6fz"},{"producer_name":"nine.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"one.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"two.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"four.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"five.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"eight.cartel","block_signing_key":"EOS8RRgah5aash9bBjCzpzGyrsHZqQkKBciiTcWExxq95dYPL4D1K"},{"producer_name":"evilproducer","block_signing_key":"EOS5iR3RpvpSM4Gm2EViLXUzsktHxjsAKRSUu8DZwJvVT1dTGwxfR"},{"producer_name":"jungleswedem","block_signing_key":"EOS79e4HpvQ1y1HdzRSqE1gCKhdN9kFGjjUU2nKG7xjiVCakt5WSs"},{"producer_name":"atticlabjbpn","block_signing_key":"EOS5hVMcN6UVWtrNCxdp5HJwsz4USULmdfNA22UDyjRNdprXEiAP6"},{"producer_name":"ivote4eosusa","block_signing_key":"EOS8WHzxnaVoXek6mwU7BJiBbyugeqZfb2y2SKa7mVUv8atLfbcjK"}]} 2 | -------------------------------------------------------------------------------- /src/chains/eos/eos_unit_conversions.rs: -------------------------------------------------------------------------------- 1 | use crate::types::{NoneError, Result}; 2 | 3 | pub fn convert_eos_asset_to_u64(eos_asset: &str) -> Result { 4 | Ok(eos_asset 5 | .replace(".", "") 6 | .split_whitespace() 7 | .next() 8 | .ok_or(NoneError("Error converting EOS asset to unsigned 64 bit integer!"))? 9 | .parse()?) 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | 16 | #[test] 17 | fn should_convert_eos_asset_to_u64() { 18 | let expected_results = vec![ 19 | 123456789123456789 as u64, 20 | 12345678912345678 as u64, 21 | 1234567891234567 as u64, 22 | 123456789123456 as u64, 23 | 12345678912345 as u64, 24 | 1234567891234 as u64, 25 | 123456789123 as u64, 26 | 12345678912 as u64, 27 | 1234567891 as u64, 28 | 123456789 as u64, 29 | 12345678 as u64, 30 | 1234567 as u64, 31 | 123456 as u64, 32 | 12345 as u64, 33 | 1234 as u64, 34 | 123 as u64, 35 | 12 as u64, 36 | 1 as u64, 37 | 0 as u64, 38 | ]; 39 | vec![ 40 | "123456789.123456789 SAM", 41 | "12345678.912345678 SAM", 42 | "1234567.891234567 SAM", 43 | "123456.789123456 SAM", 44 | "12345.678912345 SAM", 45 | "1234.567891234 SAM", 46 | "123.456789123 SAM", 47 | "12.345678912 SAM", 48 | "1.234567891 SAM", 49 | "0.123456789 SAM", 50 | "0.012345678 SAM", 51 | "0.001234567 SAM", 52 | "0.000123456 SAM", 53 | "0.000012345 SAM", 54 | "0.000001234 SAM", 55 | "0.000000123 SAM", 56 | "0.000000012 SAM", 57 | "0.000000001 SAM", 58 | "0.000000000 SAM", 59 | ] 60 | .iter() 61 | .map(|eos_asset| convert_eos_asset_to_u64(eos_asset).unwrap()) 62 | .zip(expected_results.iter()) 63 | .for_each(|(result, expected_result)| assert_eq!(&result, expected_result)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/chains/eos/extract_utxos_from_btc_txs.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::btc::{ 3 | btc_database_utils::get_btc_address_from_db, 4 | btc_types::BtcTransaction, 5 | btc_utils::get_pay_to_pub_key_hash_script, 6 | extract_utxos_from_p2pkh_txs::extract_utxos_from_txs, 7 | utxo_manager::utxo_types::BtcUtxosAndValues, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | pub fn extract_btc_utxo_from_btc_tx(db: &D, signed_txs: &[BtcTransaction]) -> Result 14 | where 15 | D: DatabaseInterface, 16 | { 17 | get_btc_address_from_db(db) 18 | .and_then(|address| get_pay_to_pub_key_hash_script(&address)) 19 | .map(|target_script| extract_utxos_from_txs(&target_script, signed_txs)) 20 | } 21 | -------------------------------------------------------------------------------- /src/chains/eos/get_active_schedule.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_database_utils::get_eos_schedule_from_db, eos_state::EosState}, 3 | constants::CORE_IS_VALIDATING, 4 | traits::DatabaseInterface, 5 | types::Result, 6 | }; 7 | 8 | pub fn get_active_schedule_from_db_and_add_to_state(state: EosState) -> Result> { 9 | if CORE_IS_VALIDATING { 10 | info!("✔ Getting EOS active-schedule/producer-list and adding to state..."); 11 | get_eos_schedule_from_db(&state.db, state.get_eos_block_header()?.schedule_version) 12 | .and_then(|schedule| state.add_active_schedule(schedule)) 13 | } else { 14 | info!("✔ Not getting EOS active-schedule/producer-list ∵ core is NOT validating!"); 15 | Ok(state) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/chains/eos/get_enabled_protocol_features.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_database_utils::get_eos_enabled_protocol_features_from_db, eos_state::EosState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn get_enabled_protocol_features_and_add_to_state(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Getting enabled EOS protocol features and adding to state..."); 12 | get_eos_enabled_protocol_features_from_db(&state.db) 13 | .and_then(|schedule| state.add_enabled_protocol_features(schedule)) 14 | } 15 | -------------------------------------------------------------------------------- /src/chains/eos/get_eos_incremerkle.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_database_utils::get_incremerkle_from_db, eos_state::EosState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn get_incremerkle_and_add_to_state(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | get_incremerkle_from_db(&state.db).map(|incremerkle| state.add_incremerkle(incremerkle)) 12 | } 13 | -------------------------------------------------------------------------------- /src/chains/eos/increment_eos_account_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::eos::eos_database_utils::put_eos_account_nonce_in_db, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn increment_eos_account_nonce(db: &D, current_nonce: u64, num_signatures: u64) -> Result<()> 4 | where 5 | D: DatabaseInterface, 6 | { 7 | let new_nonce = num_signatures + current_nonce; 8 | info!( 9 | "✔ Incrementing eos account nonce by {} from {} to {}", 10 | num_signatures, current_nonce, new_nonce 11 | ); 12 | put_eos_account_nonce_in_db(db, new_nonce) 13 | } 14 | -------------------------------------------------------------------------------- /src/chains/eos/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod eos_constants; 2 | 3 | pub(crate) mod add_schedule; 4 | pub(crate) mod append_interim_block_ids; 5 | pub(crate) mod core_initialization; 6 | pub(crate) mod disable_protocol_feature; 7 | pub(crate) mod enable_protocol_feature; 8 | pub(crate) mod eos_action_proofs; 9 | pub(crate) mod eos_action_receipt; 10 | pub(crate) mod eos_actions; 11 | pub(crate) mod eos_block_header; 12 | pub(crate) mod eos_chain_id; 13 | pub(crate) mod eos_crypto; 14 | pub(crate) mod eos_database_transactions; 15 | pub(crate) mod eos_database_utils; 16 | pub(crate) mod eos_debug_functions; 17 | pub(crate) mod eos_enclave_state; 18 | pub(crate) mod eos_extension; 19 | pub(crate) mod eos_global_sequences; 20 | pub(crate) mod eos_hash; 21 | pub(crate) mod eos_merkle_utils; 22 | pub(crate) mod eos_metadata; 23 | pub(crate) mod eos_producer_key; 24 | pub(crate) mod eos_producer_schedule; 25 | pub(crate) mod eos_state; 26 | pub(crate) mod eos_submission_material; 27 | pub(crate) mod eos_test_utils; 28 | pub(crate) mod eos_types; 29 | pub(crate) mod eos_unit_conversions; 30 | pub(crate) mod eos_utils; 31 | pub(crate) mod extract_utxos_from_btc_txs; 32 | pub(crate) mod filter_action_proofs; 33 | pub(crate) mod get_active_schedule; 34 | pub(crate) mod get_enabled_protocol_features; 35 | pub(crate) mod get_eos_incremerkle; 36 | pub(crate) mod increment_eos_account_nonce; 37 | pub(crate) mod protocol_features; 38 | pub(crate) mod save_incremerkle; 39 | pub(crate) mod save_latest_block_id; 40 | pub(crate) mod save_latest_block_num; 41 | pub(crate) mod validate_producer_slot; 42 | pub(crate) mod validate_signature; 43 | -------------------------------------------------------------------------------- /src/chains/eos/save_incremerkle.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_database_utils::put_incremerkle_in_db, eos_state::EosState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn save_incremerkle_from_state_to_db(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Saving incremerkle from state to db..."); 12 | put_incremerkle_in_db(&state.db, &state.incremerkle).and(Ok(state)) 13 | } 14 | -------------------------------------------------------------------------------- /src/chains/eos/save_latest_block_id.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_database_utils::put_eos_last_seen_block_id_in_db, eos_state::EosState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn save_latest_block_id_to_db(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Saving latest EOS block ID in db..."); 12 | put_eos_last_seen_block_id_in_db(&state.db, &state.get_eos_block_header()?.id()?).and(Ok(state)) 13 | } 14 | -------------------------------------------------------------------------------- /src/chains/eos/save_latest_block_num.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eos::{eos_database_utils::put_eos_last_seen_block_num_in_db, eos_state::EosState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn save_latest_block_num_to_db(state: EosState) -> Result> 8 | where 9 | D: DatabaseInterface, 10 | { 11 | info!("✔ Saving latest EOS block num in db..."); 12 | put_eos_last_seen_block_num_in_db(&state.db, state.get_eos_block_num()?).and(Ok(state)) 13 | } 14 | -------------------------------------------------------------------------------- /src/chains/eth/any_sender/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod relay_contract; 2 | pub(crate) mod relay_transaction; 3 | pub(crate) mod serde; 4 | -------------------------------------------------------------------------------- /src/chains/eth/any_sender/serde.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use serde::{ 4 | de::{self, Deserializer, Visitor}, 5 | ser::Serializer, 6 | }; 7 | 8 | pub mod data { 9 | use super::*; 10 | use crate::types::{Byte, Bytes}; 11 | 12 | pub fn serialize(value: &[Byte], serializer: S) -> Result 13 | where 14 | S: Serializer, 15 | { 16 | serializer.serialize_str(&format!("0x{}", hex::encode(value))) 17 | } 18 | 19 | struct DataVisitor; 20 | 21 | impl<'de> Visitor<'de> for DataVisitor { 22 | type Value = Bytes; 23 | 24 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 25 | formatter.write_str("a hex string - \"0x...\"") 26 | } 27 | 28 | fn visit_str(self, value: &str) -> Result 29 | where 30 | E: de::Error, 31 | { 32 | hex::decode(&value[2..]).map_err(de::Error::custom) 33 | } 34 | } 35 | 36 | pub fn deserialize<'de, D>(deserializer: D) -> Result 37 | where 38 | D: Deserializer<'de>, 39 | { 40 | deserializer.deserialize_str(DataVisitor) 41 | } 42 | } 43 | 44 | pub mod compensation { 45 | use super::*; 46 | 47 | pub fn serialize(value: &u64, serializer: S) -> Result 48 | where 49 | S: Serializer, 50 | { 51 | serializer.serialize_str(&value.to_string()) 52 | } 53 | 54 | struct CompensationVisitor; 55 | 56 | impl<'de> Visitor<'de> for CompensationVisitor { 57 | type Value = u64; 58 | 59 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 60 | formatter.write_str("an integer between 0 and 2^63 - 1 as a string - stringified base 10") 61 | } 62 | 63 | fn visit_u64(self, value: u64) -> Result 64 | where 65 | E: de::Error, 66 | { 67 | Ok(value) 68 | } 69 | 70 | fn visit_str(self, value: &str) -> Result 71 | where 72 | E: de::Error, 73 | { 74 | value.parse().map_err(de::Error::custom) 75 | } 76 | } 77 | 78 | pub fn deserialize<'de, D>(deserializer: D) -> Result 79 | where 80 | D: Deserializer<'de>, 81 | { 82 | deserializer.deserialize_str(CompensationVisitor) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/chains/eth/calculate_linker_hash.rs: -------------------------------------------------------------------------------- 1 | use tiny_keccak::{Hasher, Keccak}; 2 | 3 | use crate::chains::eth::{eth_types::EthHash, eth_utils::convert_h256_to_bytes}; 4 | 5 | pub fn calculate_linker_hash( 6 | block_hash_to_link_to: EthHash, 7 | anchor_block_hash: EthHash, 8 | linker_hash: EthHash, 9 | ) -> EthHash { 10 | let data = [ 11 | convert_h256_to_bytes(block_hash_to_link_to), 12 | convert_h256_to_bytes(anchor_block_hash), 13 | convert_h256_to_bytes(linker_hash), 14 | ] 15 | .concat(); 16 | let mut keccak = Keccak::v256(); 17 | let mut hashed = [0u8; 32]; 18 | keccak.update(&data); 19 | keccak.finalize(&mut hashed); 20 | EthHash::from(&hashed) 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn should_calculate_linker_hash_correctly() { 29 | let linker_hash = EthHash::from_slice( 30 | &hex::decode("0151d767a2bacda0969c93c9e3ea00e1eb08deb4bbd9cfdb7fe8d2d7c6c30062").unwrap()[..], 31 | ); 32 | let anchor_block_hash = EthHash::from_slice( 33 | &hex::decode("74a17673228252a159f8edb348d2e137c0240596b57281e59453d05c7b1adab8").unwrap()[..], 34 | ); 35 | let block_hash_to_link_to = EthHash::from_slice( 36 | &hex::decode("33f5f89485b53d02b7150436f9ddf44b0c43d047ee9d7793db9bae3ce88988bd").unwrap()[..], 37 | ); 38 | let expected_result = EthHash::from_slice( 39 | &hex::decode("ddd1ddca8da92b1fb4dc36dc39ad038d1fd7acaef8a49316b46752a780956f6a").unwrap()[..], 40 | ); 41 | let result = calculate_linker_hash(block_hash_to_link_to, anchor_block_hash, linker_hash); 42 | assert_eq!(result, expected_result); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/chains/eth/check_parent_exists.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{eth_database_utils::eth_block_exists_in_db, eth_state::EthState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn check_for_parent_of_block_in_state(state: EthState) -> Result> { 8 | info!("✔ Checking block's parent exists in database..."); 9 | match eth_block_exists_in_db(&state.db, &state.get_parent_hash()?) { 10 | true => { 11 | info!("✔ Block's parent exists in database!"); 12 | Ok(state) 13 | }, 14 | false => Err("✘ Block Rejected - no parent exists in database!".into()), 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/chains/eth/core_initialization/check_eth_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::eth_database_utils::get_public_eth_address_from_db, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn is_eth_core_initialized(db: &D) -> bool { 8 | get_public_eth_address_from_db(db).is_ok() 9 | } 10 | 11 | pub fn check_eth_core_is_initialized(db: &D) -> Result<()> { 12 | info!("✔ Checking ETH core is initialized..."); 13 | match is_eth_core_initialized(db) { 14 | false => Err("✘ ETH side of core not initialized!".into()), 15 | true => Ok(()), 16 | } 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | use crate::{ 23 | chains::eth::{eth_database_utils::put_public_eth_address_in_db, eth_test_utils::get_sample_eth_address}, 24 | errors::AppError, 25 | test_utils::get_test_database, 26 | }; 27 | 28 | #[test] 29 | fn should_return_false_if_eth_core_not_initialized() { 30 | let db = get_test_database(); 31 | let result = is_eth_core_initialized(&db); 32 | assert!(!result); 33 | } 34 | 35 | #[test] 36 | fn should_return_true_if_eth_core_initialized() { 37 | let db = get_test_database(); 38 | put_public_eth_address_in_db(&db, &get_sample_eth_address()).unwrap(); 39 | let result = is_eth_core_initialized(&db); 40 | assert!(result); 41 | } 42 | 43 | #[test] 44 | fn should_not_err_if_core_initialized() { 45 | let db = get_test_database(); 46 | put_public_eth_address_in_db(&db, &get_sample_eth_address()).unwrap(); 47 | let result = check_eth_core_is_initialized(&db); 48 | assert!(result.is_ok()); 49 | } 50 | 51 | #[test] 52 | fn should_err_if_core_not_initialized() { 53 | let db = get_test_database(); 54 | let expected_err = "✘ ETH side of core not initialized!".to_string(); 55 | match check_eth_core_is_initialized(&db) { 56 | Err(AppError::Custom(err)) => assert_eq!(err, expected_err), 57 | Ok(_) => panic!("Should not have succeeded!"), 58 | Err(_) => panic!("Wrong error received!"), 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/chains/eth/core_initialization/generate_eth_address.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_database_utils::{get_eth_private_key_from_db, put_public_eth_address_in_db}, 4 | eth_state::EthState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn generate_and_store_eth_address(state: EthState) -> Result> { 11 | info!("✔ Generating ETH address..."); 12 | get_eth_private_key_from_db(&state.db) 13 | .map(|pk| pk.to_public_key().to_address()) 14 | .and_then(|address| put_public_eth_address_in_db(&state.db, &address)) 15 | .and(Ok(state)) 16 | } 17 | -------------------------------------------------------------------------------- /src/chains/eth/core_initialization/generate_eth_contract_address.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::Address as EthAddress; 2 | use rlp::RlpStream; 3 | use tiny_keccak::{Hasher, Keccak}; 4 | 5 | use crate::{ 6 | chains::eth::{ 7 | eth_database_utils::{get_public_eth_address_from_db, put_eos_on_eth_smart_contract_address_in_db}, 8 | eth_state::EthState, 9 | }, 10 | traits::DatabaseInterface, 11 | types::Result, 12 | }; 13 | 14 | const INITIAL_NONCE: usize = 0; 15 | 16 | fn calculate_contract_address(eth_address: EthAddress, nonce: usize) -> EthAddress { 17 | let mut rlp_stream = RlpStream::new(); 18 | rlp_stream.begin_list(2); 19 | rlp_stream.append(ð_address); 20 | rlp_stream.append(&nonce); 21 | let encoded = rlp_stream.out(); 22 | let mut keccak = Keccak::v256(); 23 | let mut hashed = [0u8; 32]; 24 | keccak.update(&encoded); 25 | keccak.finalize(&mut hashed); 26 | EthAddress::from_slice(&hashed[12..]) 27 | } 28 | 29 | fn get_eth_contract_address(db: &D) -> Result { 30 | get_public_eth_address_from_db(db).map(|eth_address| { 31 | info!("✔ Calculating pBTC contract address..."); 32 | calculate_contract_address(eth_address, INITIAL_NONCE) 33 | }) 34 | } 35 | 36 | pub fn generate_and_store_eos_on_eth_contract_address(state: EthState) -> Result> { 37 | info!("✔ Calculating `EOS_ON_ETH` contract address..."); 38 | get_eth_contract_address(&state.db) 39 | .and_then(|ref smart_contract_address| { 40 | info!("✔ Storing `pERC20-on-EOS` contract address in db..."); 41 | put_eos_on_eth_smart_contract_address_in_db(&state.db, smart_contract_address) 42 | }) 43 | .and(Ok(state)) 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | use crate::chains::eth::eth_test_utils::get_sample_eth_address; 50 | 51 | #[test] 52 | fn should_calculate_contract_address() { 53 | let nonce = 2; 54 | let eth_address = get_sample_eth_address(); 55 | // NOTE: The actual contract deployed @ this nonce by this test address: 56 | // https://rinkeby.etherscan.io/address/0xc63b099efb18c8db573981fb64564f1564af4f30 57 | let expected_result = EthAddress::from_slice(&hex::decode("c63b099efB18c8db573981fB64564f1564af4f30").unwrap()); 58 | let result = calculate_contract_address(eth_address, nonce); 59 | assert_eq!(result, expected_result); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/chains/eth/core_initialization/generate_eth_contract_tx.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_chain_id::EthChainId, 4 | eth_crypto::eth_transaction::get_signed_ptoken_smart_contract_tx, 5 | eth_database_utils::get_eth_private_key_from_db, 6 | eth_state::EthState, 7 | }, 8 | traits::DatabaseInterface, 9 | types::Result, 10 | }; 11 | 12 | pub fn generate_eth_contract_tx_and_put_in_state( 13 | chain_id: &EthChainId, 14 | gas_price: u64, 15 | bytecode_path: &str, 16 | state: EthState, 17 | ) -> Result> { 18 | get_eth_private_key_from_db(&state.db) 19 | .and_then(|ref eth_private_key| { 20 | get_signed_ptoken_smart_contract_tx(0, chain_id, eth_private_key, gas_price, bytecode_path) 21 | }) 22 | .and_then(|signed_tx| state.add_misc_string_to_state(signed_tx)) 23 | } 24 | -------------------------------------------------------------------------------- /src/chains/eth/core_initialization/generate_eth_private_key.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_crypto::eth_private_key::EthPrivateKey, 4 | eth_database_utils::put_eth_private_key_in_db, 5 | eth_state::EthState, 6 | }, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | pub fn generate_and_store_eth_private_key(state: EthState) -> Result> { 12 | info!("✔ Generating & storing ETH private key..."); 13 | put_eth_private_key_in_db(&state.db, &EthPrivateKey::generate_random()?).and(Ok(state)) 14 | } 15 | -------------------------------------------------------------------------------- /src/chains/eth/core_initialization/get_eth_core_init_output_json.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::Address as EthAddress; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_json::to_string; 4 | 5 | use crate::{ 6 | chains::eth::{ 7 | eth_database_utils::{get_latest_eth_block_number, get_public_eth_address_from_db}, 8 | eth_state::EthState, 9 | }, 10 | traits::DatabaseInterface, 11 | types::Result, 12 | }; 13 | 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct EthInitializationOutput { 16 | pub eth_address: String, 17 | pub eth_latest_block_num: usize, 18 | pub eth_ptoken_contract_tx: Option, 19 | pub smart_contract_address: Option, 20 | } 21 | 22 | impl EthInitializationOutput { 23 | fn init( 24 | db: &D, 25 | contract_address: Option<&EthAddress>, 26 | contract_tx: Option<&str>, 27 | ) -> Result { 28 | Ok(Self { 29 | eth_address: format!("0x{}", hex::encode(get_public_eth_address_from_db(db)?.as_bytes())), 30 | eth_latest_block_num: get_latest_eth_block_number(db)?, 31 | eth_ptoken_contract_tx: contract_tx.map(|tx| tx.to_string()), 32 | smart_contract_address: contract_address.map(|address| format!("0x{}", hex::encode(address))), 33 | }) 34 | } 35 | 36 | pub fn new_with_no_contract(state: EthState) -> Result { 37 | const CONTRACT_TX: Option<&str> = None; 38 | const CONTRACT_ADDRESS: Option<&EthAddress> = None; 39 | Ok(to_string(&Self::init(&state.db, CONTRACT_ADDRESS, CONTRACT_TX)?)?) 40 | } 41 | 42 | pub fn new_for_eos_on_eth(state: EthState) -> Result { 43 | Self::new_with_no_contract(state) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/chains/eth/core_initialization/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod check_eth_core_is_initialized; 2 | pub(crate) mod eth_core_init_utils; 3 | pub(crate) mod generate_eth_address; 4 | pub(crate) mod generate_eth_contract_address; 5 | pub(crate) mod generate_eth_contract_tx; 6 | pub(crate) mod generate_eth_private_key; 7 | pub(crate) mod get_eth_core_init_output_json; 8 | pub(crate) mod initialize_eth_core; 9 | pub(crate) mod reset_eth_chain; 10 | -------------------------------------------------------------------------------- /src/chains/eth/eip_1559.rs: -------------------------------------------------------------------------------- 1 | use derive_more::Constructor; 2 | use ethereum_types::U256; 3 | 4 | use crate::{chains::eth::eth_chain_id::EthChainId, types::Result}; 5 | 6 | #[derive(Clone, Constructor)] 7 | pub struct Eip1559 {} 8 | 9 | impl Eip1559 { 10 | pub fn get_activation_block_number(&self, eth_chain_id: &EthChainId) -> Result { 11 | match eth_chain_id { 12 | EthChainId::Mainnet => Ok(U256::from(12_965_000)), 13 | EthChainId::Ropsten => Ok(U256::from(10_499_401)), 14 | _ => Err(format!("{} does not have an `EIP1559` activation block number! ", eth_chain_id).into()), 15 | } 16 | } 17 | 18 | pub fn is_active(&self, eth_chain_id: &EthChainId, block_number: U256) -> Result { 19 | match eth_chain_id { 20 | EthChainId::Mainnet | EthChainId::Ropsten => { 21 | Ok(block_number >= self.get_activation_block_number(eth_chain_id)?) 22 | }, 23 | _ => Ok(false), 24 | } 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn should_eip_1559_get_activation_block_number() { 34 | let eip_1559 = Eip1559::new(); 35 | let chain_id = EthChainId::Mainnet; 36 | let result = eip_1559.get_activation_block_number(&chain_id).unwrap(); 37 | let expected_result = U256::from(12_965_000); 38 | assert_eq!(result, expected_result); 39 | } 40 | 41 | #[test] 42 | fn eip_1559_should_be_active() { 43 | let block_number = U256::from(13_000_000); 44 | let eip_1559 = Eip1559::new(); 45 | let chain_id = EthChainId::Mainnet; 46 | let result = eip_1559.is_active(&chain_id, block_number).unwrap(); 47 | assert!(result); 48 | } 49 | 50 | #[test] 51 | fn eip_1559_should_not_be_active() { 52 | let block_number = U256::from(12_000_000); 53 | let eip_1559 = Eip1559::new(); 54 | let chain_id = EthChainId::Mainnet; 55 | eip_1559.is_active(&chain_id, block_number).unwrap(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/chains/eth/eth_contracts/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod erc20_vault; 2 | pub(crate) mod erc777; 3 | pub(crate) mod erc777_proxy; 4 | 5 | use ethabi::{Contract as EthContract, Token}; 6 | 7 | use crate::types::{Bytes, Result}; 8 | 9 | pub fn instantiate_contract_from_abi(abi: &str) -> Result { 10 | Ok(EthContract::load(abi.as_bytes())?) 11 | } 12 | 13 | pub fn encode_fxn_call(abi: &str, fxn_name: &str, param_tokens: &[Token]) -> Result { 14 | Ok(instantiate_contract_from_abi(abi)? 15 | .function(fxn_name)? 16 | .encode_input(param_tokens)?) 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | use crate::chains::eth::eth_contracts::erc777_proxy::ERC777_PROXY_ABI; 23 | 24 | #[test] 25 | fn should_instantiate_pnetwork_contract_from_abi() { 26 | let result = instantiate_contract_from_abi(ERC777_PROXY_ABI); 27 | assert!(result.is_ok()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/chains/eth/eth_crypto/eth_public_key.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::Address as EthAddress; 2 | use secp256k1::key::PublicKey; 3 | 4 | use crate::{crypto_utils::keccak_hash_bytes, types::Bytes}; 5 | 6 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 7 | pub struct EthPublicKey { 8 | pub compressed: bool, 9 | pub public_key: PublicKey, 10 | } 11 | 12 | impl EthPublicKey { 13 | pub fn to_bytes(self) -> Bytes { 14 | self.public_key.serialize_uncompressed().to_vec() 15 | } 16 | 17 | pub fn to_address(self) -> EthAddress { 18 | let mut eth_address = EthAddress::zero(); 19 | eth_address.assign_from_slice(&keccak_hash_bytes(&self.to_bytes()[1..65].to_vec())[12..]); 20 | eth_address 21 | } 22 | } 23 | 24 | #[cfg(test)] 25 | mod tests { 26 | use crate::chains::eth::eth_test_utils::{ 27 | get_sample_eth_address_string, 28 | get_sample_eth_public_key, 29 | get_sample_eth_public_key_bytes, 30 | }; 31 | 32 | #[test] 33 | fn should_convert_public_key_to_bytes() { 34 | let public_key = get_sample_eth_public_key(); 35 | let expected_result = get_sample_eth_public_key_bytes(); 36 | let result = public_key.to_bytes(); 37 | assert_eq!(result, expected_result); 38 | } 39 | 40 | #[test] 41 | fn should_convert_public_key_to_eth_address() { 42 | let public_key = get_sample_eth_public_key(); 43 | let result = public_key.to_address(); 44 | assert_eq!(hex::encode(result.as_bytes()), get_sample_eth_address_string()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/chains/eth/eth_crypto/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod eth_private_key; 2 | pub(crate) mod eth_public_key; 3 | pub(crate) mod eth_transaction; 4 | -------------------------------------------------------------------------------- /src/chains/eth/eth_crypto_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::chains::eth::eth_types::EthSignature; 2 | 3 | pub fn set_eth_signature_recovery_param(signature: &mut EthSignature) { 4 | signature[64] = if signature[64] == 1 { 0x1c } else { 0x1b }; 5 | } 6 | -------------------------------------------------------------------------------- /src/chains/eth/eth_database_transactions.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::eth::eth_state::EthState, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn start_eth_db_transaction_and_return_state(state: EthState) -> Result> { 4 | state.db.start_transaction().map(|_| { 5 | info!("✔ ETH database transaction begun!"); 6 | state 7 | }) 8 | } 9 | 10 | pub fn end_eth_db_transaction_and_return_state(state: EthState) -> Result> { 11 | state.db.end_transaction().map(|_| { 12 | info!("✔ Eth database transaction ended!"); 13 | state 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/chains/eth/eth_receipt_type.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use serde::Deserialize; 4 | use strum_macros::EnumIter; 5 | 6 | use crate::types::{Byte, Bytes}; 7 | 8 | #[derive(Clone, Debug, EnumIter, Eq, PartialEq, Deserialize)] 9 | pub enum EthReceiptType { 10 | Legacy, 11 | EIP2718, 12 | } 13 | 14 | impl EthReceiptType { 15 | pub fn from_byte(byte: &Byte) -> Self { 16 | match byte { 17 | 0x00 => Self::Legacy, 18 | 0x02 => Self::EIP2718, 19 | _ => Self::Legacy, 20 | } 21 | } 22 | 23 | pub fn to_byte(&self) -> Byte { 24 | match self { 25 | Self::Legacy => 0x00, 26 | Self::EIP2718 => 0x02, 27 | } 28 | } 29 | 30 | pub fn to_bytes(&self) -> Bytes { 31 | vec![self.to_byte()] 32 | } 33 | 34 | pub fn is_legacy(&self) -> bool { 35 | matches!(self, Self::Legacy) 36 | } 37 | } 38 | 39 | impl fmt::Display for EthReceiptType { 40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 41 | let s = match self { 42 | Self::Legacy => "0x0", 43 | Self::EIP2718 => "0x2", 44 | }; 45 | write!(f, "{}", s) 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use strum::IntoEnumIterator; 52 | 53 | use super::*; 54 | use crate::types::Bytes; 55 | 56 | #[test] 57 | fn should_make_receipt_types_byte_roundtrip() { 58 | let expected_results = EthReceiptType::iter().collect::>(); 59 | let bytes = EthReceiptType::iter() 60 | .map(|receipt_type| receipt_type.to_byte()) 61 | .collect::(); 62 | let results = bytes 63 | .iter() 64 | .map(|ref byte| EthReceiptType::from_byte(byte)) 65 | .collect::>(); 66 | assert_eq!(results, expected_results); 67 | } 68 | 69 | #[test] 70 | fn legacy_receipt_type_should_be_legacy() { 71 | let legacy_receipt_type = EthReceiptType::Legacy; 72 | let result = legacy_receipt_type.is_legacy(); 73 | assert!(result) 74 | } 75 | 76 | #[test] 77 | fn none_legacy_receipt_type_should_not_be_legacy() { 78 | let legacy_receipt_type = EthReceiptType::EIP2718; 79 | let result = legacy_receipt_type.is_legacy(); 80 | assert!(!result) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/chains/eth/eth_test_utils/sample-receipt-json: -------------------------------------------------------------------------------- 1 | {"blockHash":"0xb626a7546311dd56c6f5e9fd07d00c86074077bbd6d5a4c4f8269a2490aa47c0","blockNumber":8503804,"contractAddress":null,"cumulativeGasUsed":79947,"from":"0x250abfa8bc8371709fa4b601d821b1421667a886","gasUsed":37947,"logs":[{"address":"0x60a640e2D10E020fee94217707bfa9543c8b59E0","blockHash":"0xb626a7546311dd56c6f5e9fd07d00c86074077bbd6d5a4c4f8269a2490aa47c0","blockNumber":8503804,"data":"0x00000000000000000000000000000000000000000000000589ba7ab174d54000","logIndex":0,"removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000250abfa8bc8371709fa4b601d821b1421667a886","0x0000000000000000000000005a7dd68907e103c3239411dae0b0eef968468ef2"],"transactionHash":"0xab8078c9aa8720c5f9206bd2673f25f359d8a01b62212da99ff3b53c1ca3d440","transactionIndex":2,"transactionLogIndex":"0x0","type":"mined","id":"log_c684e04f"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000010000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000800000000000000000000010000000000000000008000000000000000000000000000000000000000000000200000003000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000020000000","root":null,"status":true,"to":"0x60a640e2d10e020fee94217707bfa9543c8b59e0","transactionHash":"0xab8078c9aa8720c5f9206bd2673f25f359d8a01b62212da99ff3b53c1ca3d440","transactionIndex":2} 2 | -------------------------------------------------------------------------------- /src/chains/eth/eth_test_utils/sequential_block_and_receipts_jsons/eth_block_and_receipts_num_8065751.json: -------------------------------------------------------------------------------- 1 | {"refBlockNum":666,"refBlockPrefix":1337,"block":{"author":"0xea674fdde714fd979de3edf0f56aa9716b898ec8","difficulty":"2153085103175105","extraData":"0x505059452d65746865726d696e652d6575312d32","gasLimit":8000000,"gasUsed":0,"hash":"0xac9234580335d5a1145e29aaeda2f5089e51992bfa67bec812fd1b9b2348abe9","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8","mixHash":"0xbabf76ccdb85e9b236881e44d5de8dad8de65cd31719b0d648ae1197705033e0","nonce":"0x51730c440039deba","number":8065751,"parentHash":"0xc18bf6000ba949503d2bec33ca741d1cbbc4bd118e68ba0dcd64f5d5f99cc66d","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0xa0babf76ccdb85e9b236881e44d5de8dad8de65cd31719b0d648ae1197705033e0","0x8851730c440039deba"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":538,"stateRoot":"0x8cd3af9c8a1837e0f649a4abcf2c25e113d88c1fca35f57144be656972a76ef1","timestamp":1561985688,"totalDifficulty":"10829649883666355177920","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]},"receipts":[]} 2 | -------------------------------------------------------------------------------- /src/chains/eth/eth_traits.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::H256 as EthHash; 2 | 3 | use crate::{ 4 | chains::eth::{any_sender::relay_transaction::RelayTransaction, eth_types::EthSignature}, 5 | crypto_utils::keccak_hash_bytes, 6 | types::{Byte, Bytes, Result}, 7 | }; 8 | 9 | pub trait EthTxInfoCompatible { 10 | fn is_any_sender(&self) -> bool; 11 | fn any_sender_tx(&self) -> Option; 12 | fn eth_tx_hex(&self) -> Option; 13 | fn serialize_bytes(&self) -> Bytes; 14 | fn get_tx_hash(&self) -> String { 15 | hex::encode(keccak_hash_bytes(&self.serialize_bytes())) 16 | } 17 | } 18 | 19 | pub trait EthSigningCapabilities { 20 | fn sign_hash(&self, hash: EthHash) -> Result; 21 | fn sign_message_bytes(&self, message: &[Byte]) -> Result; 22 | fn sign_eth_prefixed_msg_bytes(&self, message: &[Byte]) -> Result; 23 | } 24 | 25 | pub trait EthLogCompatible { 26 | fn get_data(&self) -> Bytes; 27 | fn get_topics(&self) -> Vec; 28 | 29 | fn check_has_x_topics(&self, x: usize) -> Result<()> { 30 | if self.get_topics().len() >= x { 31 | Ok(()) 32 | } else { 33 | Err(format!("Log does not have {} topics!", x).into()) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/chains/eth/eth_types.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::{Address, H256}; 2 | 3 | use crate::chains::eth::{ 4 | any_sender::relay_transaction::RelayTransaction, 5 | eth_chain_id::EthChainId, 6 | eth_crypto::eth_private_key::EthPrivateKey, 7 | }; 8 | 9 | pub type EthHash = H256; 10 | pub type EthAddress = Address; 11 | pub type EthSignature = [u8; 65]; 12 | pub type EthSignedTransaction = String; 13 | pub type RelayTransactions = Vec; 14 | 15 | #[derive(Debug)] 16 | pub struct EthSigningParams { 17 | pub chain_id: EthChainId, 18 | pub gas_price: u64, 19 | pub eth_account_nonce: u64, 20 | pub eth_private_key: EthPrivateKey, 21 | pub smart_contract_address: EthAddress, 22 | } 23 | 24 | #[derive(Debug)] 25 | pub struct AnySenderSigningParams { 26 | pub chain_id: EthChainId, 27 | pub any_sender_nonce: u64, 28 | pub eth_private_key: EthPrivateKey, 29 | pub public_eth_address: EthAddress, 30 | pub erc777_proxy_address: EthAddress, 31 | } 32 | -------------------------------------------------------------------------------- /src/chains/eth/get_linker_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_constants::{ETH_LINKER_HASH_KEY, PTOKEN_GENESIS_HASH_KEY}, 4 | eth_database_utils::get_hash_from_db_via_hash_key, 5 | eth_types::EthHash, 6 | }, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | pub fn get_linker_hash_or_genesis_hash(db: &D) -> Result 12 | where 13 | D: DatabaseInterface, 14 | { 15 | match get_hash_from_db_via_hash_key(db, EthHash::from_slice(Ð_LINKER_HASH_KEY[..]))? { 16 | Some(hash) => Ok(hash), 17 | None => { 18 | info!("✔ No linker-hash set yet, using pToken genesis hash..."); 19 | Ok(EthHash::from_slice(&PTOKEN_GENESIS_HASH_KEY[..])) 20 | }, 21 | } 22 | } 23 | 24 | #[cfg(test)] 25 | mod tests { 26 | use super::*; 27 | use crate::{chains::eth::eth_database_utils::put_eth_linker_hash_in_db, test_utils::get_test_database}; 28 | 29 | #[test] 30 | fn get_linker_or_genesis_should_get_linker_hash_from_db_if_extant() { 31 | let db = get_test_database(); 32 | let linker_hash = EthHash::random(); 33 | put_eth_linker_hash_in_db(&db, linker_hash).unwrap(); 34 | let result = get_linker_hash_or_genesis_hash(&db).unwrap(); 35 | assert_eq!(result, linker_hash); 36 | } 37 | 38 | #[test] 39 | fn get_linker_or_genesis_should_get_genesis_hash_if_linker_not_set() { 40 | let db = get_test_database(); 41 | let result = get_linker_hash_or_genesis_hash(&db).unwrap(); 42 | assert_eq!(result, EthHash::from_slice(&PTOKEN_GENESIS_HASH_KEY[..])); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/chains/eth/increment_eos_account_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | eos::{ 4 | eos_database_utils::get_eos_account_nonce_from_db, 5 | increment_eos_account_nonce::increment_eos_account_nonce, 6 | }, 7 | eth::eth_state::EthState, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | pub fn maybe_increment_eos_account_nonce_and_return_state(state: EthState) -> Result> 14 | where 15 | D: DatabaseInterface, 16 | { 17 | let num_txs = &state.get_num_eos_txs(); 18 | match num_txs { 19 | 0 => { 20 | info!("✔ No signatures in state ∴ not incrementing eos account nonce"); 21 | Ok(state) 22 | }, 23 | _ => increment_eos_account_nonce(&state.db, get_eos_account_nonce_from_db(&state.db)?, *num_txs as u64) 24 | .and(Ok(state)), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/chains/eth/increment_eth_account_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::eth::eth_database_utils::put_eth_account_nonce_in_db, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn increment_eth_account_nonce( 4 | db: &D, 5 | current_nonce: u64, 6 | num_signatures: u64, 7 | ) -> Result<()> { 8 | let new_nonce = num_signatures + current_nonce; 9 | info!( 10 | "✔ Incrementing ETH account nonce by {} from {} to {}", 11 | num_signatures, current_nonce, new_nonce 12 | ); 13 | put_eth_account_nonce_in_db(db, new_nonce) 14 | } 15 | -------------------------------------------------------------------------------- /src/chains/eth/increment_evm_account_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | eth::eth_state::EthState, 4 | evm::{ 5 | eth_database_utils::get_eth_account_nonce_from_db as get_evm_account_nonce_from_db, 6 | increment_evm_account_nonce::increment_evm_account_nonce, 7 | }, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | pub fn maybe_increment_evm_account_nonce_and_return_eth_state( 14 | state: EthState, 15 | ) -> Result> { 16 | let num_txs = state.erc20_on_evm_evm_signed_txs.len(); 17 | if num_txs == 0 { 18 | info!("✔ No signatures in state ∴ not incrementing EVM account nonce"); 19 | Ok(state) 20 | } else { 21 | increment_evm_account_nonce(&state.db, get_evm_account_nonce_from_db(&state.db)?, num_txs as u64).and(Ok(state)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/chains/eth/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod eth_message_signer; 2 | 3 | pub(crate) mod add_block_and_receipts_to_db; 4 | pub(crate) mod any_sender; 5 | pub(crate) mod calculate_linker_hash; 6 | pub(crate) mod check_parent_exists; 7 | pub(crate) mod core_initialization; 8 | pub(crate) mod eip_1559; 9 | pub(crate) mod eth_block; 10 | pub(crate) mod eth_chain_id; 11 | pub(crate) mod eth_constants; 12 | pub(crate) mod eth_contracts; 13 | pub(crate) mod eth_crypto; 14 | pub(crate) mod eth_crypto_utils; 15 | pub(crate) mod eth_database_transactions; 16 | pub(crate) mod eth_database_utils; 17 | pub(crate) mod eth_debug_functions; 18 | pub(crate) mod eth_enclave_state; 19 | pub(crate) mod eth_log; 20 | pub(crate) mod eth_receipt; 21 | pub(crate) mod eth_receipt_type; 22 | pub(crate) mod eth_state; 23 | pub(crate) mod eth_submission_material; 24 | pub(crate) mod eth_test_utils; 25 | pub(crate) mod eth_traits; 26 | pub(crate) mod eth_types; 27 | pub(crate) mod eth_utils; 28 | pub(crate) mod get_linker_hash; 29 | pub(crate) mod increment_eos_account_nonce; 30 | pub(crate) mod increment_eth_account_nonce; 31 | pub(crate) mod increment_evm_account_nonce; 32 | pub(crate) mod remove_old_eth_tail_block; 33 | pub(crate) mod remove_receipts_from_canon_block; 34 | pub(crate) mod update_eth_canon_block_hash; 35 | pub(crate) mod update_eth_linker_hash; 36 | pub(crate) mod update_eth_tail_block_hash; 37 | pub(crate) mod update_latest_block_hash; 38 | pub(crate) mod validate_block_in_state; 39 | pub(crate) mod validate_receipts_in_state; 40 | -------------------------------------------------------------------------------- /src/chains/eth/remove_receipts_from_canon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_database_utils::{get_eth_canon_block_from_db, put_eth_canon_block_in_db}, 4 | eth_state::EthState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn remove_receipts_from_canon_block_and_save_in_db(db: &D) -> Result<()> 11 | where 12 | D: DatabaseInterface, 13 | { 14 | get_eth_canon_block_from_db(db).and_then(|block| put_eth_canon_block_in_db(db, &block.remove_receipts())) 15 | } 16 | 17 | pub fn maybe_remove_receipts_from_canon_block_and_return_state(state: EthState) -> Result> 18 | where 19 | D: DatabaseInterface, 20 | { 21 | info!("✔ Removing receipts from canon block..."); 22 | remove_receipts_from_canon_block_and_save_in_db(&state.db).and(Ok(state)) 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::*; 28 | use crate::{chains::eth::eth_test_utils::get_sample_eth_submission_material, test_utils::get_test_database}; 29 | 30 | #[test] 31 | fn should_remove_receipts_from_canon_block() { 32 | let db = get_test_database(); 33 | let canon_block = get_sample_eth_submission_material(); 34 | put_eth_canon_block_in_db(&db, &canon_block).unwrap(); 35 | let num_receipts_before = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 36 | if let Err(e) = remove_receipts_from_canon_block_and_save_in_db(&db) { 37 | panic!("Error maybe removing receipts from canon: {}", e); 38 | } 39 | let num_receipts_after = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 40 | assert!(num_receipts_before > 0); 41 | assert_eq!(num_receipts_after, 0); 42 | } 43 | 44 | #[test] 45 | fn should_not_err_if_canon_has_no_receipts() { 46 | let db = get_test_database(); 47 | let canon_block = get_sample_eth_submission_material().remove_receipts(); 48 | put_eth_canon_block_in_db(&db, &canon_block).unwrap(); 49 | let num_receipts_before = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 50 | if let Err(e) = remove_receipts_from_canon_block_and_save_in_db(&db) { 51 | panic!("Error maybe removing receipts from canon: {}", e); 52 | } 53 | let num_receipts_after = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 54 | assert_eq!(num_receipts_before, 0); 55 | assert_eq!(num_receipts_after, 0); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/chains/eth/validate_block_in_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{eth_database_utils::get_eth_chain_id_from_db, eth_state::EthState}, 3 | constants::{CORE_IS_VALIDATING, DEBUG_MODE, NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR}, 4 | traits::DatabaseInterface, 5 | types::Result, 6 | }; 7 | 8 | pub fn validate_block_in_state(state: EthState) -> Result> { 9 | if CORE_IS_VALIDATING { 10 | info!("✔ Validating block header..."); 11 | match state 12 | .get_eth_submission_material()? 13 | .get_block()? 14 | .is_valid(&get_eth_chain_id_from_db(&state.db)?)? 15 | { 16 | true => Ok(state), 17 | false => Err("✘ Not accepting ETH block - header hash not valid!".into()), 18 | } 19 | } else { 20 | info!("✔ Skipping ETH block header validaton!"); 21 | match DEBUG_MODE { 22 | true => Ok(state), 23 | false => Err(NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR.into()), 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/chains/eth/validate_receipts_in_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::eth_state::EthState, 3 | constants::{CORE_IS_VALIDATING, DEBUG_MODE, NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR}, 4 | traits::DatabaseInterface, 5 | types::Result, 6 | }; 7 | 8 | pub fn validate_receipts_in_state(state: EthState) -> Result> 9 | where 10 | D: DatabaseInterface, 11 | { 12 | if CORE_IS_VALIDATING { 13 | info!("✔ Validating receipts..."); 14 | match state.get_eth_submission_material()?.receipts_are_valid()? { 15 | true => { 16 | info!("✔ Receipts are valid!"); 17 | Ok(state) 18 | }, 19 | false => Err("✘ Not accepting ETH block - receipts root not valid!".into()), 20 | } 21 | } else { 22 | info!("✔ Skipping ETH receipts validation!"); 23 | match DEBUG_MODE { 24 | true => Ok(state), 25 | false => Err(NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR.into()), 26 | } 27 | } 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | use crate::chains::eth::eth_test_utils::get_valid_state_with_block_and_receipts; 34 | #[cfg(not(feature = "non-validating"))] 35 | use crate::{chains::eth::eth_test_utils::get_valid_state_with_invalid_block_and_receipts, errors::AppError}; 36 | 37 | #[test] 38 | fn should_validate_receipts_in_state() { 39 | let state = get_valid_state_with_block_and_receipts().unwrap(); 40 | if validate_receipts_in_state(state).is_err() { 41 | panic!("Receipts should be valid!") 42 | } 43 | } 44 | 45 | #[cfg(not(feature = "non-validating"))] 46 | #[test] 47 | fn should_not_validate_invalid_receipts_in_state() { 48 | let expected_error = "✘ Not accepting ETH block - receipts root not valid!".to_string(); 49 | let state = get_valid_state_with_invalid_block_and_receipts().unwrap(); 50 | match validate_receipts_in_state(state) { 51 | Err(AppError::Custom(e)) => assert_eq!(e, expected_error), 52 | Ok(_) => panic!("Receipts should not be valid!"), 53 | _ => panic!("Wrong error message!"), 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/chains/evm/calculate_linker_hash.rs: -------------------------------------------------------------------------------- 1 | use tiny_keccak::{Hasher, Keccak}; 2 | 3 | use crate::chains::evm::{eth_types::EthHash, eth_utils::convert_h256_to_bytes}; 4 | 5 | pub fn calculate_linker_hash( 6 | block_hash_to_link_to: EthHash, 7 | anchor_block_hash: EthHash, 8 | linker_hash: EthHash, 9 | ) -> EthHash { 10 | let data = [ 11 | convert_h256_to_bytes(block_hash_to_link_to), 12 | convert_h256_to_bytes(anchor_block_hash), 13 | convert_h256_to_bytes(linker_hash), 14 | ] 15 | .concat(); 16 | let mut keccak = Keccak::v256(); 17 | let mut hashed = [0u8; 32]; 18 | keccak.update(&data); 19 | keccak.finalize(&mut hashed); 20 | EthHash::from(&hashed) 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn should_calculate_linker_hash_correctly() { 29 | let linker_hash = EthHash::from_slice( 30 | &hex::decode("0151d767a2bacda0969c93c9e3ea00e1eb08deb4bbd9cfdb7fe8d2d7c6c30062").unwrap()[..], 31 | ); 32 | let anchor_block_hash = EthHash::from_slice( 33 | &hex::decode("74a17673228252a159f8edb348d2e137c0240596b57281e59453d05c7b1adab8").unwrap()[..], 34 | ); 35 | let block_hash_to_link_to = EthHash::from_slice( 36 | &hex::decode("33f5f89485b53d02b7150436f9ddf44b0c43d047ee9d7793db9bae3ce88988bd").unwrap()[..], 37 | ); 38 | let expected_result = EthHash::from_slice( 39 | &hex::decode("ddd1ddca8da92b1fb4dc36dc39ad038d1fd7acaef8a49316b46752a780956f6a").unwrap()[..], 40 | ); 41 | let result = calculate_linker_hash(block_hash_to_link_to, anchor_block_hash, linker_hash); 42 | assert_eq!(result, expected_result); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/chains/evm/check_parent_exists.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::{eth_database_utils::eth_block_exists_in_db, eth_state::EthState}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn check_for_parent_of_block_in_state(state: EthState) -> Result> { 8 | info!("✔ Checking block's parent exists in database..."); 9 | match eth_block_exists_in_db(&state.db, &state.get_parent_hash()?) { 10 | true => { 11 | info!("✔ Block's parent exists in database!"); 12 | Ok(state) 13 | }, 14 | false => Err("✘ Block Rejected - no parent exists in database!".into()), 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/chains/evm/core_initialization/check_eth_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::evm::eth_database_utils::get_public_eth_address_from_db, traits::DatabaseInterface}; 2 | 3 | pub fn is_eth_core_initialized(db: &D) -> bool { 4 | get_public_eth_address_from_db(db).is_ok() 5 | } 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | use super::*; 10 | use crate::{ 11 | chains::evm::{eth_database_utils::put_public_eth_address_in_db, eth_test_utils::get_sample_eth_address}, 12 | test_utils::get_test_database, 13 | }; 14 | 15 | #[test] 16 | fn should_return_false_if_eth_core_not_initialized() { 17 | let db = get_test_database(); 18 | let result = is_eth_core_initialized(&db); 19 | assert!(!result); 20 | } 21 | 22 | #[test] 23 | fn should_return_true_if_eth_core_initialized() { 24 | let db = get_test_database(); 25 | put_public_eth_address_in_db(&db, &get_sample_eth_address()).unwrap(); 26 | let result = is_eth_core_initialized(&db); 27 | assert!(result); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/chains/evm/core_initialization/generate_eth_address.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::{ 3 | eth_database_utils::{get_eth_private_key_from_db, put_public_eth_address_in_db}, 4 | eth_state::EthState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn generate_and_store_eth_address(state: EthState) -> Result> { 11 | info!("✔ Generating ETH address..."); 12 | get_eth_private_key_from_db(&state.db) 13 | .map(|pk| pk.to_public_key().to_address()) 14 | .and_then(|address| put_public_eth_address_in_db(&state.db, &address)) 15 | .and(Ok(state)) 16 | } 17 | -------------------------------------------------------------------------------- /src/chains/evm/core_initialization/generate_eth_contract_tx.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | eth::eth_chain_id::EthChainId, 4 | evm::{ 5 | eth_crypto::eth_transaction::get_signed_ptoken_smart_contract_tx, 6 | eth_database_utils::get_eth_private_key_from_db, 7 | eth_state::EthState, 8 | }, 9 | }, 10 | traits::DatabaseInterface, 11 | types::Result, 12 | }; 13 | 14 | pub fn generate_eth_contract_tx_and_put_in_state( 15 | chain_id: &EthChainId, 16 | gas_price: u64, 17 | bytecode_path: &str, 18 | state: EthState, 19 | ) -> Result> { 20 | get_eth_private_key_from_db(&state.db) 21 | .and_then(|eth_private_key| { 22 | get_signed_ptoken_smart_contract_tx(0, chain_id, eth_private_key, gas_price, bytecode_path) 23 | }) 24 | .and_then(|signed_tx| state.add_misc_string_to_state(signed_tx)) 25 | } 26 | -------------------------------------------------------------------------------- /src/chains/evm/core_initialization/generate_eth_private_key.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::{ 3 | eth_crypto::eth_private_key::EthPrivateKey, 4 | eth_database_utils::put_eth_private_key_in_db, 5 | eth_state::EthState, 6 | }, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | pub fn generate_and_store_eth_private_key(state: EthState) -> Result> { 12 | info!("✔ Generating & storing ETH private key..."); 13 | put_eth_private_key_in_db(&state.db, &EthPrivateKey::generate_random()?).and(Ok(state)) 14 | } 15 | -------------------------------------------------------------------------------- /src/chains/evm/core_initialization/get_eth_core_init_output_json.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::Address as EthAddress; 2 | use serde::{Deserialize, Serialize}; 3 | use serde_json::to_string; 4 | 5 | use crate::{ 6 | chains::evm::{ 7 | eth_database_utils::{get_latest_eth_block_number, get_public_eth_address_from_db}, 8 | eth_state::EthState, 9 | }, 10 | traits::DatabaseInterface, 11 | types::Result, 12 | }; 13 | 14 | #[derive(Clone, Debug, Serialize, Deserialize)] 15 | pub struct EthInitializationOutput { 16 | pub eth_address: String, 17 | pub eth_latest_block_num: usize, 18 | pub eth_ptoken_contract_tx: Option, 19 | pub smart_contract_address: Option, 20 | } 21 | 22 | impl EthInitializationOutput { 23 | fn init( 24 | db: &D, 25 | contract_address: Option<&EthAddress>, 26 | contract_tx: Option<&str>, 27 | ) -> Result { 28 | Ok(Self { 29 | eth_address: format!("0x{}", hex::encode(get_public_eth_address_from_db(db)?.as_bytes())), 30 | eth_latest_block_num: get_latest_eth_block_number(db)?, 31 | eth_ptoken_contract_tx: contract_tx.map(|tx| tx.to_string()), 32 | smart_contract_address: contract_address.map(|address| format!("0x{}", hex::encode(address))), 33 | }) 34 | } 35 | 36 | pub fn new_for_erc20_on_evm(state: EthState) -> Result { 37 | Ok(to_string(&Self::init( 38 | &state.db, 39 | Some(&EthAddress::zero()), 40 | Some(&state.get_misc_string()?), 41 | )?)?) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/chains/evm/core_initialization/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod check_eth_core_is_initialized; 2 | pub(crate) mod eth_core_init_utils; 3 | pub(crate) mod generate_eth_address; 4 | pub(crate) mod generate_eth_contract_tx; 5 | pub(crate) mod generate_eth_private_key; 6 | pub(crate) mod get_eth_core_init_output_json; 7 | pub(crate) mod initialize_eth_core; 8 | pub(crate) mod reset_eth_chain; 9 | -------------------------------------------------------------------------------- /src/chains/evm/eth_crypto/eth_public_key.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::Address as EthAddress; 2 | 3 | use crate::{chains::evm::eth_crypto_utils::keccak_hash_bytes, types::Bytes}; 4 | 5 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 6 | pub struct EthPublicKey { 7 | pub compressed: bool, 8 | pub public_key: secp256k1::key::PublicKey, 9 | } 10 | 11 | impl EthPublicKey { 12 | pub fn to_bytes(self) -> Bytes { 13 | self.public_key.serialize_uncompressed().to_vec() 14 | } 15 | 16 | pub fn to_address(self) -> EthAddress { 17 | let mut eth_address = EthAddress::zero(); 18 | eth_address.assign_from_slice(&keccak_hash_bytes(&self.to_bytes()[1..65].to_vec())[12..]); 19 | eth_address 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use crate::chains::evm::eth_test_utils::{ 26 | get_sample_eth_address_string, 27 | get_sample_eth_public_key, 28 | get_sample_eth_public_key_bytes, 29 | }; 30 | 31 | #[test] 32 | fn should_convert_public_key_to_bytes() { 33 | let public_key = get_sample_eth_public_key(); 34 | let expected_result = get_sample_eth_public_key_bytes(); 35 | let result = public_key.to_bytes(); 36 | assert_eq!(result, expected_result); 37 | } 38 | 39 | #[test] 40 | fn should_convert_public_key_to_eth_address() { 41 | let public_key = get_sample_eth_public_key(); 42 | let result = public_key.to_address(); 43 | assert_eq!(hex::encode(result.as_bytes()), get_sample_eth_address_string()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/chains/evm/eth_crypto/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod eth_private_key; 2 | pub(crate) mod eth_public_key; 3 | pub(crate) mod eth_transaction; 4 | -------------------------------------------------------------------------------- /src/chains/evm/eth_crypto_utils.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::H256; 2 | use tiny_keccak::{Hasher, Keccak}; 3 | 4 | use crate::{chains::evm::eth_types::EthSignature, types::Byte}; 5 | 6 | pub fn keccak_hash_bytes(bytes: &[Byte]) -> H256 { 7 | let mut keccak = Keccak::v256(); 8 | let mut hashed = [0u8; 32]; 9 | keccak.update(bytes); 10 | keccak.finalize(&mut hashed); 11 | H256::from(hashed) 12 | } 13 | 14 | pub fn set_eth_signature_recovery_param(signature: &mut EthSignature) { 15 | signature[64] = if signature[64] == 1 { 0x1c } else { 0x1b }; 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | use super::*; 21 | use crate::chains::eth::eth_utils::convert_hex_to_h256; 22 | 23 | #[test] 24 | fn should_keccak_hash_bytes() { 25 | let bytes = vec![0xc0, 0xff, 0xee]; 26 | let result = keccak_hash_bytes(&bytes); 27 | let expected_result_hex = "7924f890e12acdf516d6278e342cd34550e3bafe0a3dec1b9c2c3e991733711a"; 28 | let expected_result = convert_hex_to_h256(expected_result_hex).unwrap(); 29 | assert_eq!(result, expected_result); 30 | } 31 | 32 | #[test] 33 | fn should_not_set_eth_signature_recovery_param_correctly_if_byte_is_1() { 34 | let sig_before = [1u8; 65]; 35 | let mut sig_after = sig_before.clone(); 36 | set_eth_signature_recovery_param(&mut sig_after); 37 | assert_eq!(sig_after[64], 0x1c) 38 | } 39 | 40 | #[test] 41 | fn should_not_set_eth_signature_recovery_param_correctly_if_byte_is_not_1() { 42 | let sig_before = [0u8; 65]; 43 | let mut sig_after = sig_before.clone(); 44 | set_eth_signature_recovery_param(&mut sig_after); 45 | assert_eq!(sig_after[64], 0x1b) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/chains/evm/eth_database_transactions.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::evm::eth_state::EthState, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn start_eth_db_transaction_and_return_state(state: EthState) -> Result> { 4 | state.db.start_transaction().map(|_| { 5 | info!("✔ EVm database transaction begun!"); 6 | state 7 | }) 8 | } 9 | 10 | pub fn end_eth_db_transaction_and_return_state(state: EthState) -> Result> { 11 | state.db.end_transaction().map(|_| { 12 | info!("✔ EVM database transaction ended!"); 13 | state 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/chains/evm/eth_test_utils/sample-receipt-json: -------------------------------------------------------------------------------- 1 | {"blockHash":"0xb626a7546311dd56c6f5e9fd07d00c86074077bbd6d5a4c4f8269a2490aa47c0","blockNumber":8503804,"contractAddress":null,"cumulativeGasUsed":79947,"from":"0x250abfa8bc8371709fa4b601d821b1421667a886","gasUsed":37947,"logs":[{"address":"0x60a640e2D10E020fee94217707bfa9543c8b59E0","blockHash":"0xb626a7546311dd56c6f5e9fd07d00c86074077bbd6d5a4c4f8269a2490aa47c0","blockNumber":8503804,"data":"0x00000000000000000000000000000000000000000000000589ba7ab174d54000","logIndex":0,"removed":false,"topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000250abfa8bc8371709fa4b601d821b1421667a886","0x0000000000000000000000005a7dd68907e103c3239411dae0b0eef968468ef2"],"transactionHash":"0xab8078c9aa8720c5f9206bd2673f25f359d8a01b62212da99ff3b53c1ca3d440","transactionIndex":2,"transactionLogIndex":"0x0","type":"mined","id":"log_c684e04f"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000010000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000800000000000000000000010000000000000000008000000000000000000000000000000000000000000000200000003000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000020000000","root":null,"status":true,"to":"0x60a640e2d10e020fee94217707bfa9543c8b59e0","transactionHash":"0xab8078c9aa8720c5f9206bd2673f25f359d8a01b62212da99ff3b53c1ca3d440","transactionIndex":2} 2 | -------------------------------------------------------------------------------- /src/chains/evm/eth_test_utils/sequential_block_and_receipts_jsons/eth_block_and_receipts_num_8065751.json: -------------------------------------------------------------------------------- 1 | {"refBlockNum":666,"refBlockPrefix":1337,"block":{"author":"0xea674fdde714fd979de3edf0f56aa9716b898ec8","difficulty":"2153085103175105","extraData":"0x505059452d65746865726d696e652d6575312d32","gasLimit":8000000,"gasUsed":0,"hash":"0xac9234580335d5a1145e29aaeda2f5089e51992bfa67bec812fd1b9b2348abe9","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8","mixHash":"0xbabf76ccdb85e9b236881e44d5de8dad8de65cd31719b0d648ae1197705033e0","nonce":"0x51730c440039deba","number":8065751,"parentHash":"0xc18bf6000ba949503d2bec33ca741d1cbbc4bd118e68ba0dcd64f5d5f99cc66d","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":["0xa0babf76ccdb85e9b236881e44d5de8dad8de65cd31719b0d648ae1197705033e0","0x8851730c440039deba"],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":538,"stateRoot":"0x8cd3af9c8a1837e0f649a4abcf2c25e113d88c1fca35f57144be656972a76ef1","timestamp":1561985688,"totalDifficulty":"10829649883666355177920","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]},"receipts":[]} 2 | -------------------------------------------------------------------------------- /src/chains/evm/eth_traits.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::any_sender::relay_transaction::RelayTransaction, 3 | crypto_utils::keccak_hash_bytes, 4 | types::Bytes, 5 | }; 6 | 7 | pub trait EthTxInfoCompatible { 8 | fn is_any_sender(&self) -> bool; 9 | 10 | fn any_sender_tx(&self) -> Option; 11 | 12 | fn eth_tx_hex(&self) -> Option; 13 | 14 | fn serialize_bytes(&self) -> Bytes; 15 | 16 | fn get_tx_hash(&self) -> String { 17 | hex::encode(keccak_hash_bytes(&self.serialize_bytes())) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/chains/evm/eth_types.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use ethereum_types::{Address, H256}; 4 | 5 | use crate::{ 6 | chains::evm::{eth_crypto::eth_private_key::EthPrivateKey, trie_nodes::Node}, 7 | types::Bytes, 8 | }; 9 | 10 | pub type EthHash = H256; 11 | pub type EthAddress = Address; 12 | pub type NodeStack = Vec; 13 | pub type EthSignature = [u8; 65]; 14 | pub type EthSignedTransaction = String; 15 | pub type ChildNodes = [Option; 16]; 16 | pub type TrieHashMap = HashMap; 17 | 18 | #[derive(Debug)] 19 | pub struct EthSigningParams { 20 | pub chain_id: u8, 21 | pub gas_price: u64, 22 | pub eth_account_nonce: u64, 23 | pub eth_private_key: EthPrivateKey, 24 | pub smart_contract_address: EthAddress, 25 | } 26 | 27 | #[derive(Debug)] 28 | pub struct AnySenderSigningParams { 29 | pub chain_id: u8, 30 | pub any_sender_nonce: u64, 31 | pub eth_private_key: EthPrivateKey, 32 | pub public_eth_address: EthAddress, 33 | pub erc777_proxy_address: EthAddress, 34 | } 35 | -------------------------------------------------------------------------------- /src/chains/evm/get_linker_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::{ 3 | eth_constants::PTOKEN_GENESIS_HASH_KEY, 4 | eth_database_utils::get_eth_linker_hash_from_db, 5 | eth_types::EthHash, 6 | }, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | pub fn get_linker_hash_or_genesis_hash(db: &D) -> Result { 12 | match get_eth_linker_hash_from_db(db) { 13 | Ok(hash) => Ok(hash), 14 | Err(_) => { 15 | info!("✔ No linker-hash set yet, using pToken genesis hash..."); 16 | Ok(EthHash::from_slice(&PTOKEN_GENESIS_HASH_KEY[..])) 17 | }, 18 | } 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | use crate::{chains::evm::eth_database_utils::put_eth_linker_hash_in_db, test_utils::get_test_database}; 25 | 26 | #[test] 27 | fn get_linker_or_genesis_should_get_linker_hash_from_db_if_extant() { 28 | let db = get_test_database(); 29 | let linker_hash = EthHash::random(); 30 | put_eth_linker_hash_in_db(&db, linker_hash).unwrap(); 31 | let result = get_linker_hash_or_genesis_hash(&db).unwrap(); 32 | assert_eq!(result, linker_hash); 33 | } 34 | 35 | #[test] 36 | fn get_linker_or_genesis_should_get_genesis_hash_if_linker_not_set() { 37 | let db = get_test_database(); 38 | let result = get_linker_hash_or_genesis_hash(&db).unwrap(); 39 | assert_eq!(result, EthHash::from_slice(&PTOKEN_GENESIS_HASH_KEY[..])); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/chains/evm/increment_eth_account_nonce_and_return_evm_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | eth::{ 4 | eth_database_utils::get_eth_account_nonce_from_db, 5 | increment_eth_account_nonce::increment_eth_account_nonce, 6 | }, 7 | evm::eth_state::EthState as EvmState, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | pub fn maybe_increment_eth_account_nonce_and_return_evm_state( 14 | state: EvmState, 15 | ) -> Result> { 16 | let num_txs = state.erc20_on_evm_eth_signed_txs.len(); 17 | if num_txs == 0 { 18 | info!("✔ No signatures in state ∴ not incrementing ETH account nonce"); 19 | Ok(state) 20 | } else { 21 | increment_eth_account_nonce(&state.db, get_eth_account_nonce_from_db(&state.db)?, num_txs as u64).and(Ok(state)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/chains/evm/increment_evm_account_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{chains::evm::eth_database_utils::put_eth_account_nonce_in_db, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn increment_evm_account_nonce( 4 | db: &D, 5 | current_nonce: u64, 6 | num_signatures: u64, 7 | ) -> Result<()> { 8 | let new_nonce = num_signatures + current_nonce; 9 | info!( 10 | "✔ Incrementing EVM account nonce by {} from {} to {}", 11 | num_signatures, current_nonce, new_nonce 12 | ); 13 | put_eth_account_nonce_in_db(db, new_nonce) 14 | } 15 | -------------------------------------------------------------------------------- /src/chains/evm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod eth_message_signer; 2 | 3 | pub(crate) mod add_block_and_receipts_to_db; 4 | pub(crate) mod calculate_linker_hash; 5 | pub(crate) mod check_parent_exists; 6 | pub(crate) mod core_initialization; 7 | pub(crate) mod eth_block; 8 | pub(crate) mod eth_constants; 9 | pub(crate) mod eth_crypto; 10 | pub(crate) mod eth_crypto_utils; 11 | pub(crate) mod eth_database_transactions; 12 | pub(crate) mod eth_database_utils; 13 | pub(crate) mod eth_enclave_state; 14 | pub(crate) mod eth_log; 15 | pub(crate) mod eth_receipt; 16 | pub(crate) mod eth_state; 17 | pub(crate) mod eth_submission_material; 18 | pub(crate) mod eth_test_utils; 19 | pub(crate) mod eth_traits; 20 | pub(crate) mod eth_types; 21 | pub(crate) mod eth_utils; 22 | pub(crate) mod evm_debug_functions; 23 | pub(crate) mod get_linker_hash; 24 | pub(crate) mod get_trie_hash_map; 25 | pub(crate) mod increment_eth_account_nonce_and_return_evm_state; 26 | pub(crate) mod increment_evm_account_nonce; 27 | pub(crate) mod nibble_utils; 28 | pub(crate) mod path_codec; 29 | pub(crate) mod remove_old_eth_tail_block; 30 | pub(crate) mod remove_receipts_from_canon_block; 31 | pub(crate) mod trie; 32 | pub(crate) mod trie_nodes; 33 | pub(crate) mod update_eth_canon_block_hash; 34 | pub(crate) mod update_eth_linker_hash; 35 | pub(crate) mod update_eth_tail_block_hash; 36 | pub(crate) mod update_latest_block_hash; 37 | pub(crate) mod validate_block_in_state; 38 | pub(crate) mod validate_receipts_in_state; 39 | -------------------------------------------------------------------------------- /src/chains/evm/remove_receipts_from_canon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::{ 3 | eth_database_utils::{get_eth_canon_block_from_db, put_eth_canon_block_in_db}, 4 | eth_state::EthState, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn remove_receipts_from_canon_block_and_save_in_db(db: &D) -> Result<()> 11 | where 12 | D: DatabaseInterface, 13 | { 14 | get_eth_canon_block_from_db(db).and_then(|block| put_eth_canon_block_in_db(db, &block.remove_receipts())) 15 | } 16 | 17 | pub fn maybe_remove_receipts_from_canon_block_and_return_state(state: EthState) -> Result> 18 | where 19 | D: DatabaseInterface, 20 | { 21 | info!("✔ Removing receipts from canon block..."); 22 | remove_receipts_from_canon_block_and_save_in_db(&state.db).and(Ok(state)) 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::*; 28 | use crate::{chains::evm::eth_test_utils::get_sample_eth_submission_material, test_utils::get_test_database}; 29 | 30 | #[test] 31 | fn should_remove_receipts_from_canon_block() { 32 | let db = get_test_database(); 33 | let canon_block = get_sample_eth_submission_material(); 34 | put_eth_canon_block_in_db(&db, &canon_block).unwrap(); 35 | let num_receipts_before = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 36 | if let Err(e) = remove_receipts_from_canon_block_and_save_in_db(&db) { 37 | panic!("Error maybe removing receipts from canon: {}", e); 38 | } 39 | let num_receipts_after = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 40 | assert!(num_receipts_before > 0); 41 | assert_eq!(num_receipts_after, 0); 42 | } 43 | 44 | #[test] 45 | fn should_not_err_if_canon_has_no_receipts() { 46 | let db = get_test_database(); 47 | let canon_block = get_sample_eth_submission_material().remove_receipts(); 48 | put_eth_canon_block_in_db(&db, &canon_block).unwrap(); 49 | let num_receipts_before = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 50 | if let Err(e) = remove_receipts_from_canon_block_and_save_in_db(&db) { 51 | panic!("Error maybe removing receipts from canon: {}", e); 52 | } 53 | let num_receipts_after = get_eth_canon_block_from_db(&db).unwrap().receipts.len(); 54 | assert_eq!(num_receipts_before, 0); 55 | assert_eq!(num_receipts_after, 0); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/chains/evm/validate_block_in_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::{eth_database_utils::get_eth_chain_id_from_db, eth_state::EthState}, 3 | constants::{CORE_IS_VALIDATING, DEBUG_MODE, NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR}, 4 | traits::DatabaseInterface, 5 | types::Result, 6 | }; 7 | 8 | pub fn validate_block_in_state(state: EthState) -> Result> 9 | where 10 | D: DatabaseInterface, 11 | { 12 | if CORE_IS_VALIDATING { 13 | info!("✔ Validating block header..."); 14 | match state 15 | .get_eth_submission_material()? 16 | .get_block()? 17 | .is_valid(&get_eth_chain_id_from_db(&state.db)?)? 18 | { 19 | true => Ok(state), 20 | false => Err("✘ Not accepting ETH block - header hash not valid!".into()), 21 | } 22 | } else { 23 | info!("✔ Skipping ETH block header validaton!"); 24 | match DEBUG_MODE { 25 | true => Ok(state), 26 | false => Err(NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR.into()), 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/chains/evm/validate_receipts_in_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::eth_state::EthState, 3 | constants::{CORE_IS_VALIDATING, DEBUG_MODE, NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR}, 4 | traits::DatabaseInterface, 5 | types::Result, 6 | }; 7 | 8 | pub fn validate_receipts_in_state(state: EthState) -> Result> 9 | where 10 | D: DatabaseInterface, 11 | { 12 | if CORE_IS_VALIDATING { 13 | info!("✔ Validating receipts..."); 14 | match state.get_eth_submission_material()?.receipts_are_valid()? { 15 | true => { 16 | info!("✔ Receipts are valid!"); 17 | Ok(state) 18 | }, 19 | false => Err("✘ Not accepting ETH block - receipts root not valid!".into()), 20 | } 21 | } else { 22 | info!("✔ Skipping ETH receipts validation!"); 23 | match DEBUG_MODE { 24 | true => Ok(state), 25 | false => Err(NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR.into()), 26 | } 27 | } 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | use super::*; 33 | use crate::chains::evm::eth_test_utils::get_valid_state_with_block_and_receipts; 34 | #[cfg(not(feature = "non-validating"))] 35 | use crate::{chains::evm::eth_test_utils::get_valid_state_with_invalid_block_and_receipts, errors::AppError}; 36 | 37 | #[test] 38 | fn should_validate_receipts_in_state() { 39 | let state = get_valid_state_with_block_and_receipts().unwrap(); 40 | if validate_receipts_in_state(state).is_err() { 41 | panic!("Receipts should be valid!") 42 | } 43 | } 44 | 45 | #[cfg(not(feature = "non-validating"))] 46 | #[test] 47 | fn should_not_validate_invalid_receipts_in_state() { 48 | let expected_error = "✘ Not accepting ETH block - receipts root not valid!".to_string(); 49 | let state = get_valid_state_with_invalid_block_and_receipts().unwrap(); 50 | match validate_receipts_in_state(state) { 51 | Err(AppError::Custom(e)) => assert_eq!(e, expected_error), 52 | Ok(_) => panic!("Receipts should not be valid!"), 53 | _ => panic!("Wrong error message!"), 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/chains/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod btc; 2 | pub(crate) mod eos; 3 | pub(crate) mod eth; 4 | pub(crate) mod evm; 5 | -------------------------------------------------------------------------------- /src/check_debug_mode.rs: -------------------------------------------------------------------------------- 1 | use crate::{constants::DEBUG_MODE, types::Result}; 2 | 3 | pub fn check_debug_mode() -> Result<()> { 4 | match DEBUG_MODE { 5 | true => { 6 | info!("✔ Application is in debug mode! Continuing..."); 7 | Ok(()) 8 | }, 9 | false => Err("✘ Application NOT in debug mode - exiting!".into()), 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::Address as EthAddress; 2 | 3 | pub const CORE_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); 4 | 5 | #[cfg(feature = "debug")] 6 | pub const DEBUG_MODE: bool = true; 7 | 8 | #[cfg(not(feature = "debug"))] 9 | pub const DEBUG_MODE: bool = false; 10 | 11 | #[cfg(feature = "non-validating")] 12 | pub const CORE_IS_VALIDATING: bool = false; 13 | 14 | #[cfg(not(feature = "non-validating"))] 15 | pub const CORE_IS_VALIDATING: bool = true; 16 | 17 | pub const NOT_VALIDATING_WHEN_NOT_IN_DEBUG_MODE_ERROR: &str = 18 | "✘ Not allowed to skip validation when core is not built in `DEBUG` mode!`"; 19 | 20 | pub const U64_NUM_BYTES: usize = 8; 21 | pub const BTC_NUM_DECIMALS: usize = 8; 22 | pub const ETH_HASH_LENGTH: usize = 32; 23 | pub const PTOKEN_ERC777_NUM_DECIMALS: u32 = 18; 24 | pub const SUCCESS_JSON: &str = "{success:true}"; 25 | pub const FIELD_NOT_SET_MSG: &str = "Not set!"; 26 | pub const SAFE_EOS_ADDRESS: &str = "safu.ptokens"; 27 | pub const FEE_BASIS_POINTS_DIVISOR: u64 = 10_000; 28 | pub const MIN_DATA_SENSITIVITY_LEVEL: Option = None; 29 | pub const DEBUG_OUTPUT_MARKER: &str = "DEBUG_OUTPUT_MARKER"; 30 | pub const PRIVATE_KEY_DATA_SENSITIVITY_LEVEL: Option = Some(255); 31 | pub const SAFE_BTC_ADDRESS: &str = "136CTERaocm8dLbEtzCaFtJJX9jfFhnChK"; 32 | const SAFE_ETH_ADDRESS_HEX: &str = "71A440EE9Fa7F99FB9a697e96eC7839B8A1643B8"; 33 | const SAFE_EVM_ADDRESS_HEX: &str = SAFE_ETH_ADDRESS_HEX; 34 | 35 | lazy_static! { 36 | pub static ref THIRTY_TWO_ZERO_BYTES: Vec = vec![0; 32]; 37 | pub static ref DB_KEY_PREFIX: &'static str = option_env!("DB_KEY_PREFIX").unwrap_or(""); 38 | pub static ref SAFE_ETH_ADDRESS: EthAddress = EthAddress::from_slice(&hex::decode(SAFE_ETH_ADDRESS_HEX).unwrap()); 39 | pub static ref SAFE_EVM_ADDRESS: EthAddress = EthAddress::from_slice(&hex::decode(SAFE_EVM_ADDRESS_HEX).unwrap()); 40 | } 41 | -------------------------------------------------------------------------------- /src/core_type.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use std::fmt; 3 | 4 | use strum_macros::EnumIter; 5 | 6 | #[derive(Clone, Copy, EnumIter)] 7 | pub enum CoreType { 8 | BtcOnEth, 9 | BtcOnEos, 10 | EosOnEth, 11 | Erc20OnEos, 12 | Erc20OnEvm, 13 | } 14 | 15 | impl CoreType { 16 | pub fn as_db_key_prefix(&self) -> String { 17 | self.to_string().to_lowercase().replace("_", "-") 18 | } 19 | } 20 | 21 | impl fmt::Display for CoreType { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | let s = match self { 24 | Self::BtcOnEth => "BTC_ON_ETH", 25 | Self::BtcOnEos => "BTC_ON_EOS", 26 | Self::EosOnEth => "EOS_ON_ETH", 27 | Self::Erc20OnEos => "ERC20_ON_EOS", 28 | Self::Erc20OnEvm => "ERC20_ON_EVM", 29 | }; 30 | write!(f, "{}", s) 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use strum::IntoEnumIterator; 37 | 38 | use super::*; 39 | 40 | #[test] 41 | fn should_get_core_type_as_db_key_prefix() { 42 | let expected_results = vec!["btc-on-eth", "btc-on-eos", "eos-on-eth", "erc20-on-eos", "erc20-on-evm"]; 43 | CoreType::iter() 44 | .zip(expected_results.iter()) 45 | .for_each(|(core_type, expected_result)| assert_eq!(&core_type.as_db_key_prefix(), *expected_result)) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/crypto_utils.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::H256 as KeccakHash; 2 | use rand::{thread_rng, RngCore}; 3 | use secp256k1::key::SecretKey; 4 | use tiny_keccak::{Hasher, Keccak}; 5 | 6 | use crate::types::{Byte, Result}; 7 | 8 | pub fn keccak_hash_bytes(bytes: &[Byte]) -> KeccakHash { 9 | let mut keccak = Keccak::v256(); 10 | let mut hashed = [0u8; 32]; 11 | keccak.update(bytes); 12 | keccak.finalize(&mut hashed); 13 | KeccakHash::from(hashed) 14 | } 15 | 16 | fn get_x_random_bytes(num_bytes: usize) -> Vec { 17 | let mut bytes = vec![0u8; num_bytes]; 18 | thread_rng().fill_bytes(&mut bytes); 19 | bytes 20 | } 21 | 22 | fn get_32_random_bytes_arr() -> [u8; 32] { 23 | let mut arr = [0; 32]; 24 | arr.copy_from_slice(&get_x_random_bytes(32)); 25 | arr 26 | } 27 | 28 | pub fn generate_random_private_key() -> Result { 29 | Ok(SecretKey::from_slice(&get_32_random_bytes_arr())?) 30 | } 31 | 32 | #[cfg(test)] 33 | mod test { 34 | use super::*; 35 | 36 | #[test] 37 | fn should_generate_32_random_bytes() { 38 | let result = get_32_random_bytes_arr(); 39 | assert_eq!(result.len(), 32); 40 | } 41 | 42 | #[test] 43 | fn should_generate_x_random_bytes() { 44 | let x: usize = 100; 45 | let result = get_x_random_bytes(x); 46 | assert_eq!(result.len(), x); 47 | } 48 | 49 | #[test] 50 | fn should_generate_random_private_key() { 51 | generate_random_private_key().unwrap(); 52 | } 53 | 54 | #[test] 55 | fn should_keccak_hash_bytes() { 56 | let bytes = vec![0xc0, 0xff, 0xee]; 57 | let result = keccak_hash_bytes(&bytes); 58 | let expected_result = KeccakHash::from_slice( 59 | &hex::decode("7924f890e12acdf516d6278e342cd34550e3bafe0a3dec1b9c2c3e991733711a").unwrap(), 60 | ); 61 | assert_eq!(result, expected_result); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/database_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | traits::DatabaseInterface, 3 | types::{Byte, Result}, 4 | }; 5 | 6 | pub fn put_string_in_db(db: &D, key: &[Byte], string: &str) -> Result<()> 7 | where 8 | D: DatabaseInterface, 9 | { 10 | debug!("✔ Putting `string` of {} in db under key {}", string, hex::encode(key)); 11 | db.put(key.to_vec(), string.as_bytes().to_vec(), None) 12 | } 13 | 14 | pub fn get_string_from_db(db: &D, key: &[Byte]) -> Result 15 | where 16 | D: DatabaseInterface, 17 | { 18 | debug!("✔ Getting `string` from db under key: {}", hex::encode(key)); 19 | db.get(key.to_vec(), None) 20 | .map(|bytes| bytes.iter().map(|byte| *byte as char).collect::()) 21 | } 22 | 23 | pub fn put_u64_in_db(db: &D, key: &[Byte], u_64: u64) -> Result<()> 24 | where 25 | D: DatabaseInterface, 26 | { 27 | trace!("✔ Putting `u64` of {} in db...", u_64); 28 | db.put(key.to_vec(), u_64.to_le_bytes().to_vec(), None) 29 | } 30 | 31 | pub fn get_u64_from_db(db: &D, key: &[Byte]) -> Result 32 | where 33 | D: DatabaseInterface, 34 | { 35 | trace!("✔ Getting `u64` from db..."); 36 | db.get(key.to_vec(), None).and_then(|bytes| match bytes.len() <= 8 { 37 | true => { 38 | let mut array = [0; 8]; 39 | let bytes = &bytes[..array.len()]; 40 | array.copy_from_slice(bytes); 41 | Ok(u64::from_le_bytes(array)) 42 | }, 43 | false => Err("✘ Too many bytes to convert to u64!".into()), 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /src/debug_database_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::{check_debug_mode::check_debug_mode, traits::DatabaseInterface, types::Result}; 2 | 3 | pub fn set_key_in_db_to_value(db: D, key: &str, value: &str, data_sensitivity: Option) -> Result 4 | where 5 | D: DatabaseInterface, 6 | { 7 | info!("✔ Setting key: {} in DB to value: {}", key, value); 8 | check_debug_mode() 9 | .and_then(|_| db.start_transaction()) 10 | .and_then(|_| db.put(hex::decode(key)?, hex::decode(value)?, data_sensitivity)) 11 | .and_then(|_| db.end_transaction()) 12 | .map(|_| "{putting_value_in_database_suceeded:true}".to_string()) 13 | } 14 | 15 | pub fn get_key_from_db(db: D, key: &str, data_sensitivity: Option) -> Result { 16 | info!("✔ Maybe getting key: {} from DB...", key); 17 | check_debug_mode() 18 | .and_then(|_| db.start_transaction()) 19 | .and_then(|_| db.get(hex::decode(key)?, data_sensitivity)) 20 | .and_then(|value| { 21 | db.end_transaction()?; 22 | Ok(format!("{{key:{},value:{}}}", key, hex::encode(value))) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/dictionaries/dictionary_constants.rs: -------------------------------------------------------------------------------- 1 | pub use serde_json::{json, Value as JsonValue}; 2 | 3 | use crate::utils::get_prefixed_db_key; 4 | 5 | lazy_static! { 6 | // NOTE: The actual string hashed remains as it was originally for backwards compatibility. 7 | pub static ref EOS_ETH_DICTIONARY_KEY: [u8; 32] = get_prefixed_db_key("eos-erc20-dictionary"); 8 | } 9 | 10 | lazy_static! { 11 | pub static ref ETH_EVM_DICTIONARY_KEY: [u8; 32] = get_prefixed_db_key("eth-evm-dictionary"); 12 | } 13 | -------------------------------------------------------------------------------- /src/dictionaries/eos_eth/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use ethereum_types::Address as EthAddress; 3 | 4 | use crate::dictionaries::eos_eth::{EosEthTokenDictionary, EosEthTokenDictionaryEntry, EosEthTokenDictionaryJson}; 5 | 6 | pub fn get_sample_eos_eth_token_dictionary_entry_1() -> EosEthTokenDictionaryEntry { 7 | let token_address_hex = "9f57CB2a4F462a5258a49E88B4331068a391DE66".to_string(); 8 | EosEthTokenDictionaryEntry::new( 9 | 18, 10 | 9, 11 | "SAM1".to_string(), 12 | "SAM1".to_string(), 13 | "SampleToken_1".to_string(), 14 | EthAddress::from_slice(&hex::decode(&token_address_hex).unwrap()), 15 | ) 16 | } 17 | 18 | pub fn get_sample_eos_eth_token_dictionary_entry_2() -> EosEthTokenDictionaryEntry { 19 | let token_address_hex = "9e57CB2a4F462a5258a49E88B4331068a391DE66".to_string(); 20 | EosEthTokenDictionaryEntry::new( 21 | 18, 22 | 9, 23 | "SAM2".to_string(), 24 | "SAM2".to_string(), 25 | "sampletokens".to_string(), 26 | EthAddress::from_slice(&hex::decode(&token_address_hex).unwrap()), 27 | ) 28 | } 29 | 30 | pub fn get_sample_eos_eth_token_dictionary() -> EosEthTokenDictionary { 31 | EosEthTokenDictionary::new(vec![ 32 | get_sample_eos_eth_token_dictionary_entry_1(), 33 | get_sample_eos_eth_token_dictionary_entry_2(), 34 | ]) 35 | } 36 | 37 | pub fn get_sample_eos_eth_token_dictionary_json() -> EosEthTokenDictionaryJson { 38 | get_sample_eos_eth_token_dictionary().to_json().unwrap() 39 | } 40 | -------------------------------------------------------------------------------- /src/dictionaries/eth_evm/test_utils/eth-evm-sample-dictionary.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "eth_symbol": "PNT", 4 | "evm_symbol": "PNT", 5 | "evm_address": "0xdaacb0ab6fb34d24e8a67bfa14bf4d95d4c7af92", 6 | "eth_address": "0x89ab32156e46f46d02ade3fecbe5fc4243b9aaed", 7 | "eth_fee_basis_points": 10, 8 | "evm_fee_basis_points": 20 9 | }, 10 | { 11 | "eth_symbol": "OPIUM", 12 | "evm_symbol": "pOPIUM", 13 | "evm_address": "0x566cedd201f67e542a6851a2959c1a449a041945", 14 | "eth_address": "0x888888888889c00c67689029d7856aac1065ec11" 15 | }, 16 | { 17 | "eth_symbol": "PTERIA", 18 | "evm_symbol": "PTERIA", 19 | "evm_address": "0x9f5377fa03dcd4016a33669b385be4d0e02f27bc", 20 | "eth_address": "0x02eca910cb3a7d43ebc7e8028652ed5c6b70259b" 21 | }, 22 | { 23 | "eth_symbol": "BCP", 24 | "evm_symbol": "pBCP", 25 | "evm_address": "0xa114f89b49d6a58416bb07dbe09502c4f3a19e2f", 26 | "eth_address": "0xe4f726adc8e89c6a6017f01eada77865db22da14" 27 | }, 28 | { 29 | "eth_symbol": "DEFI++", 30 | "evm_symbol": "pDEFI++", 31 | "evm_address": "0xae22e27d1f727b585549c10e26192b2bc01082ca", 32 | "eth_address": "0x8d1ce361eb68e9e05573443c407d4a3bed23b033" 33 | }, 34 | { 35 | "eth_symbol": "CGG", 36 | "evm_symbol": "CGG", 37 | "evm_address": "0x1613957159e9b0ac6c80e824f7eea748a32a0ae2", 38 | "eth_address": "0x1fe24f25b1cf609b9c4e7e12d802e3640dfa5e43" 39 | }, 40 | { 41 | "eth_symbol": "GALA", 42 | "evm_symbol": "GALA", 43 | "evm_address": "0xbe807dddb074639cd9fa61b47676c064fc50d62c", 44 | "eth_address": "0x15D4c048F83bd7e37d49eA4C83a07267Ec4203dA", 45 | "eth_token_decimals": 8, 46 | "evm_token_decimals": 18 47 | } 48 | ] 49 | -------------------------------------------------------------------------------- /src/dictionaries/eth_evm/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use std::fs::read_to_string; 3 | 4 | use crate::{dictionaries::eth_evm::EthEvmTokenDictionary, types::Result}; 5 | 6 | pub fn get_sample_eth_evm_dictionary_json_str() -> Result { 7 | Ok(read_to_string( 8 | "src/dictionaries/eth_evm/test_utils/eth-evm-sample-dictionary.json", 9 | )?) 10 | } 11 | 12 | pub fn get_sample_eth_evm_dictionary() -> EthEvmTokenDictionary { 13 | EthEvmTokenDictionary::from_str(&get_sample_eth_evm_dictionary_json_str().unwrap()).unwrap() 14 | } 15 | 16 | mod tests { 17 | use super::*; 18 | 19 | #[test] 20 | fn should_get_sample_eth_evm_dictionary_json_str() { 21 | let result = get_sample_eth_evm_dictionary_json_str(); 22 | assert!(result.is_ok()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/dictionaries/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod dictionary_constants; 2 | pub(crate) mod eos_eth; 3 | pub(crate) mod eth_evm; 4 | -------------------------------------------------------------------------------- /src/enclave_info.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | constants::{CORE_IS_VALIDATING, DB_KEY_PREFIX, DEBUG_MODE}, 5 | fees::fee_constants::MAX_FEE_BASIS_POINTS, 6 | utils::get_core_version, 7 | }; 8 | 9 | #[derive(Serialize, Deserialize)] 10 | pub struct EnclaveInfo { 11 | debug_mode: bool, 12 | db_key_prefix: String, 13 | core_is_validating: bool, 14 | core_version: String, 15 | max_fee_basis_points: u64, 16 | } 17 | 18 | impl EnclaveInfo { 19 | pub fn new() -> Self { 20 | Self { 21 | debug_mode: DEBUG_MODE, 22 | core_version: get_core_version(), 23 | core_is_validating: CORE_IS_VALIDATING, 24 | db_key_prefix: DB_KEY_PREFIX.to_string(), 25 | max_fee_basis_points: MAX_FEE_BASIS_POINTS, 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/eos_on_eth/check_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | eos::{core_initialization::check_eos_core_is_initialized::check_eos_core_is_initialized, eos_state::EosState}, 4 | eth::{core_initialization::check_eth_core_is_initialized::check_eth_core_is_initialized, eth_state::EthState}, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn check_core_is_initialized(db: &D) -> Result<()> { 11 | check_eth_core_is_initialized(db).and_then(|_| check_eos_core_is_initialized(db)) 12 | } 13 | 14 | pub fn check_core_is_initialized_and_return_eos_state(state: EosState) -> Result> { 15 | check_core_is_initialized(&state.db).and(Ok(state)) 16 | } 17 | 18 | pub fn check_core_is_initialized_and_return_eth_state(state: EthState) -> Result> { 19 | check_core_is_initialized(&state.db).and(Ok(state)) 20 | } 21 | -------------------------------------------------------------------------------- /src/eos_on_eth/constants.rs: -------------------------------------------------------------------------------- 1 | pub const MINIMUM_WEI_AMOUNT: &str = "100000000000000"; 2 | -------------------------------------------------------------------------------- /src/eos_on_eth/eos/increment_eth_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{eos::eos_state::EosState, eth::eth_database_utils::increment_eth_account_nonce_in_db}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_increment_eth_nonce_in_db_and_return_eos_state( 8 | state: EosState, 9 | ) -> Result> { 10 | let num_txs = state.eth_signed_txs.len() as u64; 11 | match num_txs { 12 | 0 => { 13 | info!("✔ Not incrementing ETH account nonce - no signatures made!"); 14 | Ok(state) 15 | }, 16 | _ => { 17 | info!("✔ Incrementing ETH account nonce by {}", num_txs); 18 | increment_eth_account_nonce_in_db(&state.db, num_txs).and(Ok(state)) 19 | }, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/eos_on_eth/eos/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod eos_tx_info; 2 | pub(crate) mod get_eos_output; 3 | pub(crate) mod increment_eth_nonce; 4 | pub(crate) mod submit_eos_block; 5 | -------------------------------------------------------------------------------- /src/eos_on_eth/eth/filter_receipts_in_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_contracts::erc777::{ 4 | ERC_777_REDEEM_EVENT_TOPIC_WITHOUT_USER_DATA, 5 | ERC_777_REDEEM_EVENT_TOPIC_WITH_USER_DATA, 6 | }, 7 | eth_state::EthState, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | pub fn filter_receipts_for_eos_on_eth_eth_tx_info_in_state( 14 | state: EthState, 15 | ) -> Result> { 16 | info!("✔ Filtering receipts for those containing `eos-on-eth` tx info..."); 17 | state 18 | .get_eth_submission_material()? 19 | .get_receipts_containing_log_from_addresses_and_with_topics( 20 | &state.get_eos_eth_token_dictionary()?.to_eth_addresses(), 21 | &[ 22 | *ERC_777_REDEEM_EVENT_TOPIC_WITHOUT_USER_DATA, 23 | *ERC_777_REDEEM_EVENT_TOPIC_WITH_USER_DATA, 24 | ], 25 | ) 26 | .and_then(|filtered_submission_material| state.update_eth_submission_material(filtered_submission_material)) 27 | } 28 | -------------------------------------------------------------------------------- /src/eos_on_eth/eth/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod eth_tx_info; 2 | pub(crate) mod filter_receipts_in_state; 3 | pub(crate) mod get_output_json; 4 | pub(crate) mod initialize_eth_core; 5 | pub(crate) mod submit_eth_block; 6 | -------------------------------------------------------------------------------- /src/eos_on_eth/get_enclave_state/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | chains::{eos::eos_enclave_state::EosEnclaveState, eth::eth_enclave_state::EthEnclaveState}, 5 | enclave_info::EnclaveInfo, 6 | eos_on_eth::check_core_is_initialized::check_core_is_initialized, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | #[derive(Serialize, Deserialize)] 12 | struct EnclaveState { 13 | info: EnclaveInfo, 14 | eos: EosEnclaveState, 15 | eth: EthEnclaveState, 16 | } 17 | 18 | impl EnclaveState { 19 | pub fn new(db: &D) -> Result { 20 | Ok(Self { 21 | info: EnclaveInfo::new(), 22 | eos: EosEnclaveState::new(db)?, 23 | eth: EthEnclaveState::new_for_eos_on_eth(db)?, 24 | }) 25 | } 26 | 27 | pub fn to_string(&self) -> Result { 28 | Ok(serde_json::to_string(self)?) 29 | } 30 | } 31 | 32 | /// # Get Enclave State 33 | /// 34 | /// This function returns a JSON containing the enclave state, including state relevant to each 35 | /// blockchain controlled by this instance. 36 | pub fn get_enclave_state(db: D) -> Result { 37 | info!("✔ Getting core state..."); 38 | check_core_is_initialized(&db).and_then(|_| EnclaveState::new(&db)?.to_string()) 39 | } 40 | -------------------------------------------------------------------------------- /src/eos_on_eth/get_latest_block_numbers/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | chains::{ 5 | eos::eos_database_utils::get_latest_eos_block_number, 6 | eth::eth_database_utils::get_latest_eth_block_number, 7 | }, 8 | eos_on_eth::check_core_is_initialized::check_core_is_initialized, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | #[derive(Serialize, Deserialize)] 14 | struct BlockNumbers { 15 | eth_latest_block_number: usize, 16 | eos_latest_block_number: u64, 17 | } 18 | 19 | /// # Get Latest Block Numbers 20 | /// 21 | /// This function returns a JSON containing the last processed block number of each of the 22 | /// blockchains this instance manages. 23 | pub fn get_latest_block_numbers(db: D) -> Result { 24 | info!("✔ Getting latest block numbers..."); 25 | check_core_is_initialized(&db).and_then(|_| { 26 | Ok(serde_json::to_string(&BlockNumbers { 27 | eth_latest_block_number: get_latest_eth_block_number(&db)?, 28 | eos_latest_block_number: get_latest_eos_block_number(&db)?, 29 | })?) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/erc20_on_eos/add_vault_contract_address_to_db.rs: -------------------------------------------------------------------------------- 1 | use serde_json::json; 2 | 3 | use crate::{ 4 | chains::eth::{ 5 | eth_database_utils::put_erc20_on_eos_smart_contract_address_in_db, 6 | eth_utils::get_eth_address_from_str, 7 | }, 8 | check_debug_mode::check_debug_mode, 9 | erc20_on_eos::check_core_is_initialized::check_core_is_initialized, 10 | traits::DatabaseInterface, 11 | types::Result, 12 | }; 13 | 14 | /// # Maybe Add ERC777 Contract Address 15 | /// 16 | /// This function will add a passed in ETH address to the encrypted database since the ETH 17 | /// initialization no longer creates one. Until this step has been carried out after ETH core 18 | /// initialization, the `get_enclave_state` command will error with a message telling you to call 19 | /// this function. 20 | /// 21 | /// ### BEWARE: 22 | /// The vault contract can only be set ONCE. Further attempts to do so will not succeed. 23 | pub fn maybe_add_vault_contract_address_to_db(db: &D, address: &str) -> Result { 24 | check_debug_mode() 25 | .and_then(|_| db.start_transaction()) 26 | .and_then(|_| check_core_is_initialized(db)) 27 | .and_then(|_| put_erc20_on_eos_smart_contract_address_in_db(db, &get_eth_address_from_str(address)?)) 28 | .and_then(|_| db.end_transaction()) 29 | .map(|_| json!({"success":true, "vaultAddress:": address}).to_string()) 30 | } 31 | -------------------------------------------------------------------------------- /src/erc20_on_eos/check_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | eos::{core_initialization::check_eos_core_is_initialized::is_eos_core_initialized, eos_state::EosState}, 4 | eth::{core_initialization::check_eth_core_is_initialized::is_eth_core_initialized, eth_state::EthState}, 5 | }, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn check_core_is_initialized(db: &D) -> Result<()> { 11 | info!("✔ Checking pERC20 core is initialized..."); 12 | match is_eos_core_initialized(db) { 13 | false => Err("EOS core not initialized!".into()), 14 | true => match is_eth_core_initialized(db) { 15 | false => Err("ETH core not initialized!".into()), 16 | true => Ok(()), 17 | }, 18 | } 19 | } 20 | 21 | pub fn check_core_is_initialized_and_return_eth_state(state: EthState) -> Result> { 22 | check_core_is_initialized(&state.db).and(Ok(state)) 23 | } 24 | 25 | pub fn check_core_is_initialized_and_return_eos_state(state: EosState) -> Result> { 26 | check_core_is_initialized(&state.db).and(Ok(state)) 27 | } 28 | -------------------------------------------------------------------------------- /src/erc20_on_eos/eos/increment_eth_nonce.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{eos::eos_state::EosState, eth::eth_database_utils::increment_eth_account_nonce_in_db}, 3 | traits::DatabaseInterface, 4 | types::Result, 5 | }; 6 | 7 | pub fn maybe_increment_eth_nonce_in_db_and_return_eos_state( 8 | state: EosState, 9 | ) -> Result> { 10 | let num_txs = state.eth_signed_txs.len() as u64; 11 | match num_txs { 12 | 0 => { 13 | info!("✔ Not incrementing ETH account nonce - no signatures made!"); 14 | Ok(state) 15 | }, 16 | _ => { 17 | info!("✔ Incrementing ETH account nonce by {}", num_txs); 18 | increment_eth_account_nonce_in_db(&state.db, num_txs).and(Ok(state)) 19 | }, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/erc20_on_eos/eos/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod submit_eos_block; 2 | 3 | pub(crate) mod get_eos_output; 4 | pub(crate) mod increment_eth_nonce; 5 | pub(crate) mod redeem_info; 6 | pub(crate) mod sign_normal_eth_txs; 7 | -------------------------------------------------------------------------------- /src/erc20_on_eos/eth/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod submit_eth_block; 2 | 3 | pub(crate) mod get_output_json; 4 | pub(crate) mod initialize_eth_core; 5 | pub(crate) mod peg_in_info; 6 | -------------------------------------------------------------------------------- /src/erc20_on_eos/get_enclave_state/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | chains::{eos::eos_enclave_state::EosEnclaveState, eth::eth_enclave_state::EthEnclaveState}, 5 | enclave_info::EnclaveInfo, 6 | erc20_on_eos::check_core_is_initialized::check_core_is_initialized, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | #[derive(Serialize, Deserialize)] 12 | struct EnclaveState { 13 | info: EnclaveInfo, 14 | eth: EthEnclaveState, 15 | eos: EosEnclaveState, 16 | } 17 | 18 | impl EnclaveState { 19 | pub fn new(db: &D) -> Result { 20 | Ok(Self { 21 | info: EnclaveInfo::new(), 22 | eth: EthEnclaveState::new_for_erc20_on_eos(db)?, 23 | eos: EosEnclaveState::new_without_account_name(db)?, 24 | }) 25 | } 26 | 27 | pub fn to_string(&self) -> Result { 28 | Ok(serde_json::to_string(self)?) 29 | } 30 | } 31 | 32 | /// # Get Enclave State 33 | /// 34 | /// This function returns a JSON containing the enclave state, including state relevant to each 35 | /// blockchain controlled by this instance. 36 | pub fn get_enclave_state(db: D) -> Result { 37 | info!("✔ Getting enclave state..."); 38 | check_core_is_initialized(&db).and_then(|_| EnclaveState::new(&db)?.to_string()) 39 | } 40 | -------------------------------------------------------------------------------- /src/erc20_on_eos/get_latest_block_numbers/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | chains::{ 5 | eos::eos_database_utils::get_latest_eos_block_number, 6 | eth::eth_database_utils::get_latest_eth_block_number, 7 | }, 8 | erc20_on_eos::check_core_is_initialized::check_core_is_initialized, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | #[derive(Serialize, Deserialize)] 14 | struct BlockNumbers { 15 | eth_latest_block_number: usize, 16 | eos_latest_block_number: u64, 17 | } 18 | 19 | /// # Get Latest Block Numbers 20 | /// 21 | /// This function returns a JSON containing the last processed block number of each of the 22 | /// blockchains this instance manages. 23 | pub fn get_latest_block_numbers(db: D) -> Result { 24 | info!("✔ Getting latest block numbers..."); 25 | check_core_is_initialized(&db).and_then(|_| { 26 | Ok(serde_json::to_string(&BlockNumbers { 27 | eth_latest_block_number: get_latest_eth_block_number(&db)?, 28 | eos_latest_block_number: get_latest_eos_block_number(&db)?, 29 | })?) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/erc20_on_evm/check_core_is_initialized.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::{ 3 | eth::{core_initialization::check_eth_core_is_initialized::is_eth_core_initialized, eth_state::EthState}, 4 | evm::{ 5 | core_initialization::check_eth_core_is_initialized::is_eth_core_initialized as is_evm_core_initialized, 6 | eth_state::EthState as EvmState, 7 | }, 8 | }, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | pub fn check_core_is_initialized(db: &D) -> Result<()> { 14 | info!("✔ Checking `erc20-on-evm` core is initialized..."); 15 | match is_evm_core_initialized(db) { 16 | false => Err("EVM core not initialized!".into()), 17 | true => match is_eth_core_initialized(db) { 18 | false => Err("ETH core not initialized!".into()), 19 | true => Ok(()), 20 | }, 21 | } 22 | } 23 | 24 | pub fn check_core_is_initialized_and_return_eth_state(state: EthState) -> Result> { 25 | check_core_is_initialized(&state.db).and(Ok(state)) 26 | } 27 | 28 | pub fn check_core_is_initialized_and_return_evm_state(state: EvmState) -> Result> { 29 | check_core_is_initialized(&state.db).and(Ok(state)) 30 | } 31 | -------------------------------------------------------------------------------- /src/erc20_on_evm/eth/account_for_fees.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::eth_state::EthState, 3 | dictionaries::eth_evm::EthEvmTokenDictionary, 4 | erc20_on_evm::traits::FeesCalculator, 5 | fees::fee_constants::DISABLE_FEES, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn update_accrued_fees_in_dictionary_and_return_state( 11 | state: EthState, 12 | ) -> Result> { 13 | if DISABLE_FEES { 14 | info!("✔ Fees are disabled ∴ not accounting for any in `EthOnEvmEvmTxInfos`!"); 15 | Ok(state) 16 | } else if state.erc20_on_evm_evm_tx_infos.is_empty() { 17 | info!("✔ No `EthOnEvmEvmTxInfos` in state during ETH block submission ∴ not taking any fees!"); 18 | Ok(state) 19 | } else { 20 | info!("✔ Accruing fees during ETH block submission..."); 21 | EthEvmTokenDictionary::get_from_db(&state.db) 22 | .and_then(|dictionary| { 23 | dictionary.increment_accrued_fees_and_save_in_db( 24 | &state.db, 25 | state.erc20_on_evm_evm_tx_infos.get_fees(&dictionary)?, 26 | ) 27 | }) 28 | .and(Ok(state)) 29 | } 30 | } 31 | 32 | pub fn account_for_fees_in_evm_tx_infos_in_state(state: EthState) -> Result> { 33 | if DISABLE_FEES { 34 | info!("✔ Fees are disabled ∴ not accounting for any in `EthOnEvmEvmTxInfos`!"); 35 | Ok(state) 36 | } else if state.erc20_on_evm_evm_tx_infos.is_empty() { 37 | info!("✔ No `EthOnEvmEvmTxInfos` in state during ETH block submission ∴ not taking any fees!"); 38 | Ok(state) 39 | } else { 40 | info!("✔ Accounting for fees in `EthOnEvmEvmTxInfos` during ETH block submission..."); 41 | EthEvmTokenDictionary::get_from_db(&state.db).and_then(|ref dictionary| { 42 | let tx_infos = state.erc20_on_evm_evm_tx_infos.clone(); 43 | state.replace_erc20_on_evm_evm_tx_infos(tx_infos.subtract_fees(dictionary)?) 44 | }) 45 | } 46 | } 47 | 48 | pub fn maybe_account_for_fees(state: EthState) -> Result> { 49 | info!("✔ Accounting for fees in `EthOnEvmEvmTxInfos` during ETH block submission..."); 50 | update_accrued_fees_in_dictionary_and_return_state(state).and_then(account_for_fees_in_evm_tx_infos_in_state) 51 | } 52 | -------------------------------------------------------------------------------- /src/erc20_on_evm/eth/add_vault_contract_address.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::eth::{ 3 | eth_database_utils::put_erc20_on_evm_smart_contract_address_in_db, 4 | eth_utils::convert_hex_to_address, 5 | }, 6 | erc20_on_evm::check_core_is_initialized::check_core_is_initialized, 7 | traits::DatabaseInterface, 8 | types::Result, 9 | }; 10 | 11 | /// # Maybe Add Vault Contract Address 12 | /// 13 | /// This function will add a passed in ETH contract address to the encrypted database since the ETH 14 | /// initialization no longer creates one. Until this step has been carried out after ETH core 15 | /// initialization, the `get_enclave_state` command will error with a message telling you to call 16 | /// this function. 17 | /// 18 | /// ### BEWARE: 19 | /// This vault contract setter can only be set ONCE. Further attempts to do so will not succeed. 20 | pub fn maybe_add_vault_contract_address(db: D, hex_address: &str) -> Result { 21 | check_core_is_initialized(&db) 22 | .and_then(|_| db.start_transaction()) 23 | .and_then(|_| convert_hex_to_address(hex_address)) 24 | .and_then(|ref address| put_erc20_on_evm_smart_contract_address_in_db(&db, address)) 25 | .and_then(|_| db.end_transaction()) 26 | .map(|_| "{add_vault_address_success:true}".to_string()) 27 | } 28 | -------------------------------------------------------------------------------- /src/erc20_on_evm/eth/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod account_for_fees; 2 | pub(crate) mod add_vault_contract_address; 3 | pub(crate) mod evm_tx_info; 4 | pub(crate) mod get_eth_output_json; 5 | pub(crate) mod initialize_eth_core; 6 | pub(crate) mod submit_eth_block; 7 | -------------------------------------------------------------------------------- /src/erc20_on_evm/evm/account_for_fees.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chains::evm::eth_state::EthState as EvmState, 3 | dictionaries::eth_evm::EthEvmTokenDictionary, 4 | erc20_on_evm::traits::FeesCalculator, 5 | fees::fee_constants::DISABLE_FEES, 6 | traits::DatabaseInterface, 7 | types::Result, 8 | }; 9 | 10 | pub fn update_accrued_fees_in_dictionary_and_return_state( 11 | state: EvmState, 12 | ) -> Result> { 13 | if DISABLE_FEES { 14 | info!("✔ Fees are disabled ∴ not accounting for any in `EthOnEvmEthTxInfos`!"); 15 | Ok(state) 16 | } else if state.erc20_on_evm_eth_tx_infos.is_empty() { 17 | info!("✔ Not `EthOnEvmEthTxInfos` in state ∴ not taking any fees!"); 18 | Ok(state) 19 | } else { 20 | info!("✔ Accruing fees during EVM block submission..."); 21 | EthEvmTokenDictionary::get_from_db(&state.db) 22 | .and_then(|ref dictionary| { 23 | dictionary.increment_accrued_fees_and_save_in_db( 24 | &state.db, 25 | state.erc20_on_evm_eth_tx_infos.get_fees(dictionary)?, 26 | ) 27 | }) 28 | .and(Ok(state)) 29 | } 30 | } 31 | 32 | pub fn account_for_fees_in_eth_tx_infos_in_state(state: EvmState) -> Result> { 33 | if DISABLE_FEES { 34 | info!("✔ Fees are disabled ∴ not accounting for any in `EthOnEvmEthTxInfos`!"); 35 | Ok(state) 36 | } else if state.erc20_on_evm_eth_tx_infos.is_empty() { 37 | info!("✔ Not `EthOnEvmEthTxInfos` in state ∴ not taking any fees!"); 38 | Ok(state) 39 | } else { 40 | info!("✔ Accounting for fees in `EthOnEvmEthTxInfos` during EVM block submission..."); 41 | EthEvmTokenDictionary::get_from_db(&state.db).and_then(|ref dictionary| { 42 | let tx_infos = state.erc20_on_evm_eth_tx_infos.clone(); 43 | state.replace_erc20_on_evm_eth_tx_infos(tx_infos.subtract_fees(dictionary)?) 44 | }) 45 | } 46 | } 47 | 48 | pub fn maybe_account_for_fees(state: EvmState) -> Result> { 49 | info!("✔ Accounting for fees in `EthOnEvmEthTxInfos` during EVM block submission..."); 50 | update_accrued_fees_in_dictionary_and_return_state(state).and_then(account_for_fees_in_eth_tx_infos_in_state) 51 | } 52 | -------------------------------------------------------------------------------- /src/erc20_on_evm/evm/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod account_for_fees; 2 | pub(crate) mod eth_tx_info; 3 | pub(crate) mod get_evm_output_json; 4 | pub(crate) mod initialize_evm_core; 5 | pub(crate) mod submit_evm_block; 6 | -------------------------------------------------------------------------------- /src/erc20_on_evm/get_enclave_state/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | chains::{eth::eth_enclave_state::EthEnclaveState, evm::eth_enclave_state::EthEnclaveState as EvmEnclaveState}, 5 | dictionaries::eth_evm::EthEvmTokenDictionary, 6 | enclave_info::EnclaveInfo, 7 | erc20_on_evm::check_core_is_initialized::check_core_is_initialized, 8 | traits::DatabaseInterface, 9 | types::Result, 10 | }; 11 | 12 | #[derive(Serialize, Deserialize)] 13 | struct EnclaveState { 14 | info: EnclaveInfo, 15 | eth: EthEnclaveState, 16 | evm: EvmEnclaveState, 17 | token_dictionary: EthEvmTokenDictionary, 18 | } 19 | 20 | impl EnclaveState { 21 | pub fn new(db: &D) -> Result { 22 | Ok(Self { 23 | info: EnclaveInfo::new(), 24 | evm: EvmEnclaveState::new_for_erc20_on_evm(db)?, 25 | eth: EthEnclaveState::new_for_erc20_on_evm(db)?, 26 | token_dictionary: EthEvmTokenDictionary::get_from_db(db)?, 27 | }) 28 | } 29 | 30 | pub fn to_string(&self) -> Result { 31 | Ok(serde_json::to_string(self)?) 32 | } 33 | } 34 | 35 | /// # Get Enclave State 36 | /// 37 | /// This function returns a JSON containing the enclave state, including state relevant to each 38 | /// blockchain controlled by this instance. 39 | pub fn get_enclave_state(db: D) -> Result { 40 | info!("✔ Getting enclave state..."); 41 | check_core_is_initialized(&db).and_then(|_| EnclaveState::new(&db)?.to_string()) 42 | } 43 | -------------------------------------------------------------------------------- /src/erc20_on_evm/get_latest_block_numbers/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | chains::{ 5 | eth::eth_database_utils::get_latest_eth_block_number, 6 | evm::eth_database_utils::get_latest_eth_block_number as get_latest_evm_block_number, 7 | }, 8 | erc20_on_evm::check_core_is_initialized::check_core_is_initialized, 9 | traits::DatabaseInterface, 10 | types::Result, 11 | }; 12 | 13 | #[derive(Serialize, Deserialize)] 14 | struct BlockNumbers { 15 | eth_latest_block_number: usize, 16 | evm_latest_block_number: usize, 17 | } 18 | 19 | /// # Get Latest Block Numbers 20 | /// 21 | /// This function returns a JSON containing the last processed block number of each of the 22 | /// blockchains this instance manages. 23 | pub fn get_latest_block_numbers(db: D) -> Result { 24 | info!("✔ Getting latest `ERC20-on-EVM` block numbers..."); 25 | check_core_is_initialized(&db).and_then(|_| { 26 | Ok(serde_json::to_string(&BlockNumbers { 27 | eth_latest_block_number: get_latest_eth_block_number(&db)?, 28 | evm_latest_block_number: get_latest_evm_block_number(&db)?, 29 | })?) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/erc20_on_evm/traits.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::{Address as EthAddress, U256}; 2 | 3 | use crate::{ 4 | constants::FEE_BASIS_POINTS_DIVISOR, 5 | dictionaries::eth_evm::EthEvmTokenDictionary, 6 | fees::fee_utils::sanity_check_basis_points_value, 7 | types::Result, 8 | }; 9 | 10 | pub trait FeeCalculator { 11 | fn get_amount(&self) -> U256; 12 | 13 | fn get_token_address(&self) -> EthAddress; 14 | 15 | fn subtract_amount(&self, subtrahend: U256) -> Result 16 | where 17 | Self: Sized; 18 | 19 | fn calculate_fee(&self, fee_basis_points: u64) -> U256 { 20 | if fee_basis_points > 0 { 21 | debug!("Calculating fee using `fee_basis_points` of {}", fee_basis_points); 22 | (self.get_amount() * U256::from(fee_basis_points)) / U256::from(FEE_BASIS_POINTS_DIVISOR) 23 | } else { 24 | debug!("Not calculating fee because `fee_basis_points` are zero!"); 25 | U256::zero() 26 | } 27 | } 28 | 29 | fn calculate_fee_via_dictionary(&self, dictionary: &EthEvmTokenDictionary) -> Result<(EthAddress, U256)> { 30 | let token_address = self.get_token_address(); 31 | Ok(( 32 | token_address, 33 | self.calculate_fee(sanity_check_basis_points_value( 34 | dictionary.get_fee_basis_points(&token_address)?, 35 | )?), 36 | )) 37 | } 38 | } 39 | 40 | pub trait FeesCalculator { 41 | fn get_fees(&self, dictionary: &EthEvmTokenDictionary) -> Result>; 42 | 43 | fn subtract_fees(&self, dictionary: &EthEvmTokenDictionary) -> Result 44 | where 45 | Self: Sized; 46 | } 47 | -------------------------------------------------------------------------------- /src/fees/fee_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fees::fee_constants::MAX_FEE_BASIS_POINTS, 3 | types::Result, 4 | utils::convert_unix_timestamp_to_human_readable, 5 | }; 6 | 7 | pub fn get_last_withdrawal_date_as_human_readable_string(timestamp: u64) -> String { 8 | if timestamp == 0 { 9 | "Fees have not yet been withdrawn!".to_string() 10 | } else { 11 | convert_unix_timestamp_to_human_readable(timestamp) 12 | } 13 | } 14 | 15 | pub fn sanity_check_basis_points_value(basis_points: u64) -> Result { 16 | if basis_points <= MAX_FEE_BASIS_POINTS { 17 | Ok(basis_points) 18 | } else { 19 | Err(format!("Error! Basis points exceeds maximum of {}!", MAX_FEE_BASIS_POINTS).into()) 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | use crate::{errors::AppError, fees::fee_constants::MAX_FEE_BASIS_POINTS}; 27 | 28 | #[test] 29 | fn should_pass_basis_points_sanity_check() { 30 | let basis_points = MAX_FEE_BASIS_POINTS - 1; 31 | let result = sanity_check_basis_points_value(basis_points).unwrap(); 32 | assert_eq!(result, basis_points) 33 | } 34 | 35 | #[test] 36 | fn should_fail_basis_points_sanity_check() { 37 | let expected_err = format!("Error! Basis points exceeds maximum of {}!", MAX_FEE_BASIS_POINTS); 38 | let basis_points = MAX_FEE_BASIS_POINTS + 1; 39 | match sanity_check_basis_points_value(basis_points) { 40 | Err(AppError::Custom(err)) => assert_eq!(err, expected_err), 41 | Ok(_) => panic!("Should not have succeeded!"), 42 | Err(_) => panic!("Wrong error received!"), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/fees/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod fee_constants; 2 | pub(crate) mod fee_database_utils; 3 | pub(crate) mod fee_enclave_state; 4 | pub(crate) mod fee_utils; 5 | pub(crate) mod fee_withdrawals; 6 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # The __`pToken`__ Core 2 | //! 3 | //! Herein lies the functionality required for the cross-chain conversions 4 | //! between various blockchains allowing for decentalized swaps between a native 5 | //! asset and a host chain's pTokenized version of that asset. 6 | //! 7 | //! __Note:__ When compiling the core, you may provide an optional environment 8 | //! variable __`DB_KEY_PREFIX`__, which when used will prefix all database keys 9 | //! with the provided argument. Via this, database key clashes can be avoided 10 | //! if running multiple instances on one machine. 11 | 12 | #![allow(clippy::match_bool)] 13 | #![allow(clippy::too_many_arguments)] 14 | 15 | pub use errors::AppError; 16 | pub use traits::DatabaseInterface; 17 | pub use types::{Bytes, Result}; 18 | pub use utils::get_core_version; 19 | 20 | pub mod btc_on_eos; 21 | pub mod btc_on_eth; 22 | pub mod chains; 23 | pub(crate) mod core_type; 24 | pub(crate) mod dictionaries; 25 | pub mod eos_on_eth; 26 | pub mod erc20_on_eos; 27 | pub mod erc20_on_evm; 28 | pub mod errors; 29 | pub(crate) mod fees; 30 | pub(crate) mod metadata; 31 | pub mod traits; 32 | pub mod types; 33 | 34 | mod check_debug_mode; 35 | mod constants; 36 | mod crypto_utils; 37 | mod database_utils; 38 | mod debug_database_utils; 39 | mod enclave_info; 40 | mod utils; 41 | 42 | #[cfg(test)] 43 | mod test_utils; 44 | #[cfg(test)] 45 | extern crate simple_logger; 46 | 47 | #[macro_use] 48 | extern crate log; 49 | #[macro_use] 50 | extern crate lazy_static; 51 | #[macro_use] 52 | extern crate quick_error; 53 | -------------------------------------------------------------------------------- /src/metadata/metadata_protocol_id.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[cfg(test)] 4 | use strum::IntoEnumIterator; 5 | use strum_macros::EnumIter; 6 | 7 | use crate::types::Byte; 8 | #[cfg(test)] 9 | use crate::types::Result; 10 | 11 | #[derive(Clone, Debug, PartialEq, Eq, EnumIter)] 12 | pub enum MetadataProtocolId { 13 | Bitcoin, 14 | Ethereum, 15 | Eos, 16 | } 17 | 18 | impl MetadataProtocolId { 19 | pub fn to_byte(&self) -> Byte { 20 | match self { 21 | MetadataProtocolId::Ethereum => 0x00, 22 | MetadataProtocolId::Bitcoin => 0x01, 23 | MetadataProtocolId::Eos => 0x02, 24 | } 25 | } 26 | 27 | #[cfg(test)] 28 | fn from_byte(byte: &Byte) -> Result { 29 | match byte { 30 | 0u8 => Ok(MetadataProtocolId::Ethereum), 31 | 1u8 => Ok(MetadataProtocolId::Bitcoin), 32 | 2u8 => Ok(MetadataProtocolId::Eos), 33 | _ => Err(format!("✘ Unrecognized version byte for `MetadataProtocolId`: {:?}", byte).into()), 34 | } 35 | } 36 | 37 | pub fn to_symbol(&self) -> String { 38 | let s = match self { 39 | MetadataProtocolId::Ethereum => "ETH", 40 | MetadataProtocolId::Bitcoin => "BTC", 41 | MetadataProtocolId::Eos => "EOS", 42 | }; 43 | s.to_string() 44 | } 45 | 46 | #[cfg(test)] 47 | fn get_all() -> Vec { 48 | Self::iter().collect() 49 | } 50 | } 51 | 52 | impl fmt::Display for MetadataProtocolId { 53 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 54 | match self { 55 | MetadataProtocolId::Ethereum => write!(f, "Ethereum"), 56 | MetadataProtocolId::Bitcoin => write!(f, "Bitcoin"), 57 | MetadataProtocolId::Eos => write!(f, "Eos"), 58 | } 59 | } 60 | } 61 | 62 | #[cfg(test)] 63 | mod tests { 64 | use super::*; 65 | 66 | #[test] 67 | fn should_perform_metadata_protocol_ids_bytes_round_trip() { 68 | MetadataProtocolId::get_all().iter().for_each(|id| { 69 | let byte = id.to_byte(); 70 | let result = MetadataProtocolId::from_byte(&byte).unwrap(); 71 | assert_eq!(&result, id); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/metadata/metadata_traits.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | metadata::{metadata_chain_id::MetadataChainId, Metadata}, 3 | types::{Bytes, Result}, 4 | }; 5 | 6 | pub trait ToMetadata { 7 | fn to_metadata(&self) -> Result; 8 | fn to_metadata_bytes(&self) -> Result; 9 | } 10 | 11 | pub trait ToMetadataChainId { 12 | fn to_metadata_chain_id(&self) -> MetadataChainId; 13 | } 14 | -------------------------------------------------------------------------------- /src/metadata/metadata_version.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | use strum::IntoEnumIterator; 3 | use strum_macros::EnumIter; 4 | 5 | #[cfg(test)] 6 | use crate::types::Result; 7 | use crate::types::{Byte, Bytes}; 8 | 9 | #[derive(Clone, Debug, PartialEq, Eq, EnumIter)] 10 | pub enum MetadataVersion { 11 | V1, 12 | } 13 | 14 | impl MetadataVersion { 15 | pub fn to_byte(&self) -> Byte { 16 | match self { 17 | Self::V1 => 0x01, 18 | } 19 | } 20 | 21 | pub fn to_bytes(&self) -> Bytes { 22 | vec![self.to_byte()] 23 | } 24 | 25 | #[cfg(test)] 26 | pub fn from_byte(byte: &Byte) -> Result { 27 | match byte { 28 | 1u8 => Ok(Self::V1), 29 | _ => Err(format!("✘ Unrecognized version byte for `MetadataVersion`: {:?}", byte).into()), 30 | } 31 | } 32 | 33 | #[cfg(test)] 34 | pub fn from_bytes(bytes: &[Byte]) -> Result { 35 | if bytes.is_empty() { 36 | Err("Not enough bytes to get `MetadataVersion` from bytes!".into()) 37 | } else { 38 | Self::from_byte(&bytes[0]) 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | fn get_all() -> Vec { 44 | Self::iter().collect() 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod tests { 50 | use super::*; 51 | 52 | #[test] 53 | fn should_make_metadata_version_bytes_roundtrip() { 54 | MetadataVersion::get_all().iter().for_each(|id| { 55 | let byte = id.to_byte(); 56 | let result = MetadataVersion::from_byte(&byte).unwrap(); 57 | assert_eq!(&result, id); 58 | }); 59 | } 60 | 61 | #[test] 62 | fn should_get_metadata_versiokn_from_bytes() { 63 | let bytes = vec![0x01, 0xc0, 0xff, 0xee]; 64 | let result = MetadataVersion::from_bytes(&bytes).unwrap(); 65 | assert_eq!(result, MetadataVersion::V1); 66 | } 67 | 68 | #[test] 69 | fn should_err_when_getting_version_from_too_few_bytes() { 70 | let too_few_bytes = vec![]; 71 | let result = MetadataVersion::from_bytes(&too_few_bytes); 72 | assert!(result.is_err()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/metadata/test_utils.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use std::str::FromStr; 3 | 4 | use bitcoin::util::address::Address as BtcAddress; 5 | use eos_chain::AccountName as EosAddress; 6 | use ethereum_types::Address as EthAddress; 7 | 8 | use crate::{ 9 | metadata::{metadata_chain_id::MetadataChainId, metadata_origin_address::MetadataOriginAddress, Metadata}, 10 | types::Bytes, 11 | }; 12 | 13 | pub fn get_sample_eos_address() -> EosAddress { 14 | EosAddress::from_str("aneosaddress").unwrap() 15 | } 16 | 17 | pub fn get_sample_btc_address() -> BtcAddress { 18 | BtcAddress::from_str("12dRugNcdxK39288NjcDV4GX7rMsKCGn6B").unwrap() 19 | } 20 | 21 | pub fn get_sample_eth_address() -> EthAddress { 22 | EthAddress::from_slice(&hex::decode("5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c").unwrap()) 23 | } 24 | 25 | fn get_sample_user_data() -> Bytes { 26 | vec![0xc0, 0xff, 0xee] 27 | } 28 | 29 | pub fn get_sample_eth_origin_address() -> MetadataOriginAddress { 30 | MetadataOriginAddress::new_from_eth_address(&get_sample_eth_address(), &MetadataChainId::EthereumMainnet).unwrap() 31 | } 32 | 33 | pub fn get_sample_eos_origin_address() -> MetadataOriginAddress { 34 | MetadataOriginAddress::new_from_eos_address(&get_sample_eos_address(), &MetadataChainId::EosMainnet).unwrap() 35 | } 36 | 37 | pub fn get_sample_btc_origin_address() -> MetadataOriginAddress { 38 | MetadataOriginAddress::new_from_btc_address(&get_sample_btc_address(), &MetadataChainId::BitcoinMainnet).unwrap() 39 | } 40 | 41 | pub fn get_sample_eth_metadata() -> Metadata { 42 | Metadata::new(&get_sample_user_data(), &get_sample_eth_origin_address()) 43 | } 44 | 45 | pub fn get_sample_eos_metadata() -> Metadata { 46 | Metadata::new(&get_sample_user_data(), &get_sample_eos_origin_address()) 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use super::*; 52 | 53 | #[test] 54 | fn should_get_sample_eth_metadata() { 55 | get_sample_eth_metadata(); 56 | } 57 | 58 | #[test] 59 | fn should_get_sample_eos_metadata() { 60 | get_sample_eos_metadata(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/notes: -------------------------------------------------------------------------------- 1 | Need to pull out ALL shared behaviour: 2 | error enum 3 | types 4 | utxo manager 5 | Also, grep for ETH in the btc-on-eos & remove all that eventually! 6 | 7 | Also EOS doesn't need a satoshi conversion! 8 | Also, need to fix the failing tests on the extractors! 9 | -------------------------------------------------------------------------------- /src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, sync::Mutex}; 2 | 3 | use rand::Rng; 4 | 5 | use crate::{ 6 | traits::DatabaseInterface, 7 | types::{Bytes, DataSensitivity, Result}, 8 | }; 9 | 10 | pub static DB_LOCK_ERRROR: &str = "✘ Cannot get lock on DB!"; 11 | 12 | pub struct TestDB(pub Mutex>); 13 | 14 | impl TestDB { 15 | pub fn new() -> Self { 16 | Self(Mutex::new(HashMap::new())) 17 | } 18 | } 19 | 20 | impl DatabaseInterface for TestDB { 21 | fn end_transaction(&self) -> Result<()> { 22 | Ok(()) 23 | } 24 | 25 | fn start_transaction(&self) -> Result<()> { 26 | Ok(()) 27 | } 28 | 29 | fn put(&self, key: Bytes, value: Bytes, _sensitivity: DataSensitivity) -> Result<()> { 30 | self.0.lock().expect(DB_LOCK_ERRROR).insert(key, value); 31 | Ok(()) 32 | } 33 | 34 | fn delete(&self, key: Bytes) -> Result<()> { 35 | self.0.lock().expect(DB_LOCK_ERRROR).remove(&key); 36 | Ok(()) 37 | } 38 | 39 | fn get(&self, key: Bytes, _sensitivity: DataSensitivity) -> Result { 40 | match self.0.lock().expect(DB_LOCK_ERRROR).get(&key) { 41 | Some(value) => Ok(value.to_vec()), 42 | None => Err("✘ Cannot find item in database!".into()), 43 | } 44 | } 45 | } 46 | 47 | pub fn get_test_database() -> TestDB { 48 | TestDB::new() 49 | } 50 | 51 | pub fn get_random_num_between(min: usize, max: usize) -> usize { 52 | rand::thread_rng().gen_range(min..max) 53 | } 54 | 55 | pub fn get_sample_message_to_sign_bytes() -> &'static [u8] { 56 | b"Provable pToken!" 57 | } 58 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | //! # pToken-specific traits. 2 | use ethereum_types::H256 as KeccakHash; 3 | 4 | use crate::types::{Bytes, Result}; 5 | 6 | pub trait DatabaseInterface { 7 | fn end_transaction(&self) -> Result<()>; 8 | 9 | fn start_transaction(&self) -> Result<()>; 10 | 11 | fn delete(&self, key: Bytes) -> Result<()>; 12 | 13 | fn get(&self, key: Bytes, data_sensitivity: Option) -> Result; 14 | 15 | fn put(&self, key: Bytes, value: Bytes, data_sensitivity: Option) -> Result<()>; 16 | } 17 | 18 | pub trait ChainId { 19 | fn keccak_hash(&self) -> Result; 20 | } 21 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | //! # pToken types. 2 | use std::result; 3 | 4 | use crate::errors::AppError; 5 | // NOTE: Temporary, until try_trait is stabilized 6 | pub(crate) use crate::errors::AppError::NoneError; 7 | 8 | pub type Bytes = Vec; 9 | pub type Result = result::Result; 10 | 11 | pub(crate) type Byte = u8; 12 | pub(crate) type DataSensitivity = Option; 13 | --------------------------------------------------------------------------------