diff --git a/CMakeLists.txt b/CMakeLists.txt index bfb519022..920755b13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,14 +8,12 @@ execute_process( OUTPUT_VARIABLE SPVSDK_VERSION ) -execute_process( - COMMAND git describe --dirty --always --tags --exclude "*-pre" - COMMAND tr -d "\n" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE SPVSDK_VERSION_MESSAGE -) +if(SPVSDK_VERSION MATCHES "^[0-9]*\\.[0-9]*\\.[0-9]*$") + project(Elastos-spvsdk VERSION ${SPVSDK_VERSION}) +else() + project(Elastos-spvsdk) +endif() -project(Elastos-spvsdk VERSION ${SPVSDK_VERSION}) set(CMAKE_CXX_STANDARD 11) set(CMAKE_C_STANDARD 11) @@ -58,6 +56,13 @@ option(SPV_CONSOLE_LOG "Enable console log" OFF) option(SPV_BUILD_TEST_CASES "Build test cases" OFF) option(SPV_BUILD_APPS "Build command line elawallet" ON) +execute_process( + COMMAND git describe --dirty --always --tags --exclude "*-pre" + COMMAND tr -d "\n" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE SPVSDK_VERSION_MESSAGE +) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/CMakeConfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeConfig.h diff --git a/Interface/IEthSidechainSubWallet.h b/Interface/IEthSidechainSubWallet.h index d701c7b59..5a773fd48 100644 --- a/Interface/IEthSidechainSubWallet.h +++ b/Interface/IEthSidechainSubWallet.h @@ -26,10 +26,69 @@ namespace Elastos { namespace ElaWallet { + enum EthereumAmountUnit { + TOKEN_DECIMAL = 0, + TOKEN_INTEGER = 1, + + ETHER_WEI = 0, + ETHER_GWEI = 3, + ETHER_ETHER = 6, + }; + class IEthSidechainSubWallet : public virtual ISubWallet { public: virtual ~IEthSidechainSubWallet() noexcept {} + /** + * + * @param targetAddress + * @param amount Decimal string in unit. The `number` must be either an integer or have + * a single decimal point with at least one preceeding characters. Thus: 0.001, 1.0000, 12 + * and 12.100 are all valid. But .1 is invalid (required 0.1). + * @param amountUnit + * @return + */ + virtual nlohmann::json CreateTransfer(const std::string &targetAddress, + const std::string &amount, + EthereumAmountUnit amountUnit) const = 0; + + /** + * + * @param targetAddress "" is different from "0x0000000000000000000000000000000000000000". "" means + * address is null. + * @param amount Decimal string in unit. The `number` must be either an integer or have + * a single decimal point with at least one preceeding characters. Thus: 0.001, 1.0000, 12 + * and 12.100 are all valid. But .1 is invalid (required 0.1). + * @param amountUnit + * @param gasPrice Decimal string in unit. The `number` must be either an integer or have + * a single decimal point with at least one preceeding characters. Thus: 0.001, 1.0000, 12 + * and 12.100 are all valid. But .1 is invalid (required 0.1). + * @param gasPriceUnit + * @param gasLimit + * @param data + * @return + */ + virtual nlohmann::json CreateTransferGeneric(const std::string &targetAddress, + const std::string &amount, + EthereumAmountUnit amountUnit, + const std::string &gasPrice, + EthereumAmountUnit gasPriceUnit, + const std::string &gasLimit, + const std::string &data) const = 0; + + /** + * + * @param tx + */ + virtual void DeleteTransfer(const nlohmann::json &tx) = 0; + + /** + * @param tokenSymbol + * @return + */ + virtual nlohmann::json GetTokenTransactions(uint32_t start, uint32_t count, const std::string &txid, + const std::string &tokenSymbol) const = 0; + }; } diff --git a/Interface/IIDChainSubWallet.h b/Interface/IIDChainSubWallet.h index 87985617c..adb990500 100644 --- a/Interface/IIDChainSubWallet.h +++ b/Interface/IIDChainSubWallet.h @@ -40,11 +40,13 @@ namespace Elastos { * Create a id transaction and return the content of transaction in json format, this is a special transaction to register id related information on id chain. * @param payloadJson is payload for register id related information in json format, the content of payload should have Id, Path, DataHash, Proof, and Sign. * @param memo input memo attribute for describing. + * @param fee transaction fee set by user. * @return If success return the content of transaction in json format. */ virtual nlohmann::json CreateIDTransaction( const nlohmann::json &payloadJson, - const std::string &memo = "") = 0; + const std::string &memo = "", + const std::string &fee = "10000") = 0; /** * Get all DID derived of current subwallet. diff --git a/Interface/IMainchainSubWallet.h b/Interface/IMainchainSubWallet.h index e60d9e93c..501c4ef45 100644 --- a/Interface/IMainchainSubWallet.h +++ b/Interface/IMainchainSubWallet.h @@ -517,6 +517,28 @@ namespace Elastos { */ virtual nlohmann::json GetRegisteredCRInfo() const = 0; + /** + * Generate digest for signature of CR council members + * @param payload + * { + * "NodePublicKey": "...", + * "CRCouncilMemberDID": "...", + * } + * @return + */ + virtual std::string CRCouncilMemberClaimNodeDigest(const nlohmann::json &payload) const = 0; + + /** + * @param payload + * { + * "NodePublicKey": "...", + * "CRCouncilMemberDID": "...", + * "CRCouncilMemberSignature": "..." + * } + * @return + */ + virtual nlohmann::json CreateCRCouncilMemberClaimNodeTransaction(const nlohmann::json &payload, const std::string &memo = "") = 0; + public: ////////////////////////////////////////////////// @@ -531,6 +553,7 @@ namespace Elastos { * "CategoryData": "testdata", // limit: 4096 bytes * "OwnerPublicKey": "031f7a5a6bf3b2450cd9da4048d00a8ef1cb4912b5057535f65f3cc0e0c36f13b4", * "DraftHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "DraftData": "", // Optional, string format, limit 1 Mbytes * "Budgets": [{"Type":0,"Stage":0,"Amount":"300"},{"Type":1,"Stage":1,"Amount":"33"},{"Type":2,"Stage":2,"Amount":"344"}], * "Recipient": "EPbdmxUVBzfNrVdqJzZEySyWGYeuKAeKqv", // address * } @@ -561,6 +584,7 @@ namespace Elastos { * "CategoryData": "testdata", // limit: 4096 bytes * "OwnerPublicKey": "031f7a5a6bf3b2450cd9da4048d00a8ef1cb4912b5057535f65f3cc0e0c36f13b4", // Owner DID public key * "DraftHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "DraftData": "", // Optional, string format, limit 1 Mbytes * "Budgets": [ // same as mention on method ProposalOwnerDigest() * {"Type":0,"Stage":0,"Amount":"300"},{"Type":1,"Stage":1,"Amount":"33"},{"Type":2,"Stage":2,"Amount":"344"} * ], @@ -593,6 +617,7 @@ namespace Elastos { * "CategoryData": "testdata", // limit: 4096 bytes * "OwnerPublicKey": "031f7a5a6bf3b2450cd9da4048d00a8ef1cb4912b5057535f65f3cc0e0c36f13b4", // Owner DID public key * "DraftHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "DraftData": "", // Optional, string format, limit 1 Mbytes * "Budgets": [ // same as mention on method ProposalOwnerDigest() * {"Type":0,"Stage":0,"Amount":"300"},{"Type":1,"Stage":1,"Amount":"33"},{"Type":2,"Stage":2,"Amount":"344"} * ], @@ -622,6 +647,7 @@ namespace Elastos { * "ProposalHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", * "VoteResult": 1, // approve = 0, reject = 1, abstain = 2 * "OpinionHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "OpinionData": "", // Optional, string format, limit 1 Mbytes * "DID": "icwTktC5M6fzySQ5yU7bKAZ6ipP623apFY", // did of CR council member * } * @@ -637,6 +663,7 @@ namespace Elastos { * "ProposalHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", * "VoteResult": 1, // approve = 0, reject = 1, abstain = 2 * "OpinionHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "OpinionData": "", // Optional, string format, limit 1 Mbytes * "DID": "icwTktC5M6fzySQ5yU7bKAZ6ipP623apFY", // did of CR council member's did * // signature of CR council member * "Signature": "ff0ff9f45478f8f9fcd50b15534c9a60810670c3fb400d831cd253370c42a0af79f7f4015ebfb4a3791f5e45aa1c952d40408239dead3d23a51314b339981b76" @@ -659,6 +686,7 @@ namespace Elastos { * { * "ProposalHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", * "MessageHash": "0b5ee188b455ab5605cd452d7dda5c205563e1b30c56e93c6b9fda133f8cc4d4", + * "MessageData": "", // Optional, string format, limit 800 Kbytes * "Stage": 0, // value can be [0, 128) * "OwnerPublicKey": "02c632e27b19260d80d58a857d2acd9eb603f698445cc07ba94d52296468706331", * // If this proposal tracking is not use for changing owner, will be empty. Otherwise not empty. @@ -676,6 +704,7 @@ namespace Elastos { * { * "ProposalHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", * "MessageHash": "0b5ee188b455ab5605cd452d7dda5c205563e1b30c56e93c6b9fda133f8cc4d4", + * "MessageData": "", // Optional, string format, limit 800 Kbytes * "Stage": 0, // value can be [0, 128) * "OwnerPublicKey": "02c632e27b19260d80d58a857d2acd9eb603f698445cc07ba94d52296468706331", * // If this proposal tracking is not use for changing owner, will be empty. Otherwise not empty. @@ -694,6 +723,7 @@ namespace Elastos { * { * "ProposalHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", * "MessageHash": "0b5ee188b455ab5605cd452d7dda5c205563e1b30c56e93c6b9fda133f8cc4d4", + * "MessageData": "", // Optional, string format, limit 800 Kbytes * "Stage": 0, // value can be [0, 128) * "OwnerPublicKey": "02c632e27b19260d80d58a857d2acd9eb603f698445cc07ba94d52296468706331", * // If this proposal tracking is not use for changing owner, will be empty. Otherwise not empty. @@ -703,6 +733,7 @@ namespace Elastos { * "NewOwnerSignature": "9a24a084a6f599db9906594800b6cb077fa7995732c575d4d125c935446c93bbe594ee59e361f4d5c2142856c89c5d70c8811048bfb2f8620fbc18a06cb58109", * "Type": 0, // common = 0, progress = 1, rejected = 2, terminated = 3, changeOwner = 4, finalized = 5 * "SecretaryGeneralOpinionHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", + * "SecretaryGeneralOpinionData": "", // Optional, string format, limit 200 Kbytes * } * * @return Digest of payload @@ -717,6 +748,7 @@ namespace Elastos { * { * "ProposalHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", * "MessageHash": "0b5ee188b455ab5605cd452d7dda5c205563e1b30c56e93c6b9fda133f8cc4d4", + * "MessageData": "", // Optional, string format, limit 800 Kbytes * "Stage": 0, // value can be [0, 128) * "OwnerPublicKey": "02c632e27b19260d80d58a857d2acd9eb603f698445cc07ba94d52296468706331", * // If this proposal tracking is not use for changing owner, will be empty. Otherwise not empty. @@ -726,6 +758,7 @@ namespace Elastos { * "NewOwnerSignature": "9a24a084a6f599db9906594800b6cb077fa7995732c575d4d125c935446c93bbe594ee59e361f4d5c2142856c89c5d70c8811048bfb2f8620fbc18a06cb58109", * "Type": 0, // common = 0, progress = 1, rejected = 2, terminated = 3, changeOwner = 4, finalized = 5 * "SecretaryGeneralOpinionHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", + * "SecretaryGeneralOpinionData": "", // Optional, string format, limit 200 Kbytes * "SecretaryGeneralSignature": "9a24a084a6f599db9906594800b6cb077fa7995732c575d4d125c935446c93bbe594ee59e361f4d5c2142856c89c5d70c8811048bfb2f8620fbc18a06cb58109" * } * @@ -735,6 +768,169 @@ namespace Elastos { virtual nlohmann::json CreateProposalTrackingTransaction(const nlohmann::json &payload, const std::string &memo = "") = 0; + ////////////////////////////////////////////////// + /* Proposal Secretary General Election */ + ////////////////////////////////////////////////// + /** + * @param payload Proposal secretary election payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "031f7a5a6bf3b2450cd9da4048d00a8ef1cb4912b5057535f65f3cc0e0c36f13b4", + * "DraftHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "SecretaryGeneralPublicKey": "...", + * "SecretaryGeneralDID": "...", + * } + * @return + */ + virtual std::string ProposalSecretaryGeneralElectionDigest( + const nlohmann::json &payload) const = 0; + + /** + * @param payload Proposal secretary election payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "031f7a5a6bf3b2450cd9da4048d00a8ef1cb4912b5057535f65f3cc0e0c36f13b4", + * "DraftHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "SecretaryGeneralPublicKey": "...", + * "SecretaryGeneralDID": "...", + * "Signature": "...", + * "SecretaryGeneralSignature": "...", + * "CRCouncilMemberDID": "...", + * } + * @return + */ + virtual std::string ProposalSecretaryGeneralElectionCRCouncilMemberDigest( + const nlohmann::json &payload) const = 0; + + /** + * @param payload Proposal secretary election payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "031f7a5a6bf3b2450cd9da4048d00a8ef1cb4912b5057535f65f3cc0e0c36f13b4", + * "DraftHash": "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "SecretaryGeneralPublicKey": "...", + * "SecretaryGeneralDID": "...", + * "Signature": "...", + * "SecretaryGeneralSignature": "...", + * "CRCouncilMemberDID": "...", + * "CRCouncilMemberSignature": "..." + * } + * @param memo Remarks string. + * @return + */ + virtual nlohmann::json CreateSecretaryGeneralElectionTransaction( + const nlohmann::json &payload, const std::string &memo = "") = 0; + + ////////////////////////////////////////////////// + /* Proposal Change Owner */ + ////////////////////////////////////////////////// + /** + * Use for owner & new owner sign + * @param payload Proposal change owner payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "...", + * "DraftHash": "...", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "TargetProposalHash": "...", + * "NewRecipient": "...", + * "NewOwnerPublicKey": "...", + * } + * @return + */ + virtual std::string ProposalChangeOwnerDigest(const nlohmann::json &payload) const = 0; + + /** + * @param payload Proposal change owner payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "...", + * "DraftHash": "...", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "TargetProposalHash": "...", + * "NewRecipient": "...", + * "NewOwnerPublicKey": "...", + * "Signature": "...", + * "NewOwnerSignature": "...", + * "CRCouncilMemberDID": "..." + * } + * @return + */ + virtual std::string ProposalChangeOwnerCRCouncilMemberDigest(const nlohmann::json &payload) const = 0; + + /** + * @param payload Proposal change owner payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "...", + * "DraftHash": "...", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "TargetProposalHash": "...", + * "NewRecipient": "...", + * "NewOwnerPublicKey": "...", + * "Signature": "...", + * "NewOwnerSignature": "...", + * "CRCouncilMemberDID": "...", + * "CRCouncilMemberSignature": "...", + * } + * @param memo Remark string. + * @return + */ + virtual nlohmann::json CreateProposalChangeOwnerTransaction( + const nlohmann::json &payload, const std::string &memo = "") = 0; + + ////////////////////////////////////////////////// + /* Proposal Terminate Proposal */ + ////////////////////////////////////////////////// + /** + * @param payload Terminate proposal payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "...", + * "DraftHash": "...", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "TargetProposalHash": "...", + * } + * @return + */ + virtual std::string TerminateProposalOwnerDigest(const nlohmann::json &payload) const = 0; + + /** + * @param payload Terminate proposal payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "...", + * "DraftHash": "...", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "TargetProposalHash": "...", + * "Signature": "...", + * "CRCouncilMemberDID": "...", + * } + * @return + */ + virtual std::string TerminateProposalCRCouncilMemberDigest(const nlohmann::json &payload) const = 0; + + /** + * @param payload Terminate proposal payload + * { + * "CategoryData": "testdata", // limit: 4096 bytes + * "OwnerPublicKey": "...", + * "DraftHash": "...", + * "DraftData": "", // Optional, string format, limit 1 Mbytes + * "TargetProposalHash": "...", + * "Signature": "...", + * "CRCouncilMemberDID": "...", + * "CRCouncilMemberSignature": "...", + * } + * @param memo Remark string. + * @return + */ + virtual nlohmann::json CreateTerminateProposalTransaction( + const nlohmann::json &payload, const std::string &memo = "") = 0; + ////////////////////////////////////////////////// /* Proposal Withdraw */ ////////////////////////////////////////////////// @@ -745,6 +941,8 @@ namespace Elastos { * { * "ProposalHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", * "OwnerPublicKey": "02c632e27b19260d80d58a857d2acd9eb603f698445cc07ba94d52296468706331", + * "Recipient": "EPbdmxUVBzfNrVdqJzZEySyWGYeuKAeKqv", // address + * "Amount": "100000000", // 1 ela = 100000000 sela * } * * @return Digest of payload. @@ -753,24 +951,12 @@ namespace Elastos { /** * Create proposal withdraw transaction. - * Note: This tx does not need to be signed. - * - * @param recipient Recipient of proposal. - * @param amount Withdraw amount. - * @param utxo UTXO json array of address CREXPENSESXXXXXXXXXXXXXXXXXX4UdT6b. - * [{ - * "Hash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", - * "Index": 0, - * "Amount": "100000000", // 1 ela = 100000000 sela - * },{ - * "Hash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", - * "Index": 2, - * "Amount": "200000000", // 2 ela = 200000000 sela - * }] * @param payload Proposal payload. * { * "ProposalHash": "7c5d2e7cfd7d4011414b5ddb3ab43e2aca247e342d064d1091644606748d7513", * "OwnerPublicKey": "02c632e27b19260d80d58a857d2acd9eb603f698445cc07ba94d52296468706331", + * "Recipient": "EPbdmxUVBzfNrVdqJzZEySyWGYeuKAeKqv", // address + * "Amount": "100000000", // 1 ela = 100000000 sela * "Signature": "9a24a084a6f599db9906594800b6cb077fa7995732c575d4d125c935446c93bbe594ee59e361f4d5c2142856c89c5d70c8811048bfb2f8620fbc18a06cb58109" * } * @@ -778,10 +964,7 @@ namespace Elastos { * * @return Transaction in JSON format. */ - virtual nlohmann::json CreateProposalWithdrawTransaction(const std::string &recipient, - const std::string &amount, - const nlohmann::json &utxo, - const nlohmann::json &payload, + virtual nlohmann::json CreateProposalWithdrawTransaction(const nlohmann::json &payload, const std::string &memo = "") = 0; }; diff --git a/Interface/IMasterWallet.h b/Interface/IMasterWallet.h index 5c5d68e53..d5769678e 100644 --- a/Interface/IMasterWallet.h +++ b/Interface/IMasterWallet.h @@ -146,6 +146,14 @@ namespace Elastos { */ virtual bool IsAddressValid(const std::string &address) const = 0; + /** + * + * @param chainID chain id of subwallet + * @param address address of subwallet + * @return + */ + virtual bool IsSubWalletAddressValid(const std::string &chainID, const std::string &address) const = 0; + /** * Get all chain ids of supported chains. * @return a list of chain id. @@ -159,6 +167,15 @@ namespace Elastos { */ virtual void ChangePassword(const std::string &oldPassword, const std::string &newPassword) = 0; + /** + * Reset payment password of current wallet + * @param mnemonic mnemonic + * @param passphrase passphrase + * @param newPassword New password will be set. + */ + virtual void ResetPassword(const std::string &mnemonic, const std::string &passphrase, + const std::string &newPassword) = 0; + }; } diff --git a/Interface/IMasterWalletManager.h b/Interface/IMasterWalletManager.h index 73ad5f24a..5a32796d9 100644 --- a/Interface/IMasterWalletManager.h +++ b/Interface/IMasterWalletManager.h @@ -209,6 +209,12 @@ namespace Elastos { */ virtual void FlushData() = 0; + /** + * + * @param level can be value of: "trace", "debug", "info", "warning", "error", "critical", "off" + */ + virtual void SetLogLevel(const std::string &level) = 0; + }; } diff --git a/Interface/ISubWallet.h b/Interface/ISubWallet.h index ee8fdf325..7ca48b912 100644 --- a/Interface/ISubWallet.h +++ b/Interface/ISubWallet.h @@ -245,6 +245,12 @@ namespace Elastos { virtual nlohmann::json GetAssetInfo( const std::string &assetID) const = 0; + /** + * Get last block information including "Height", "Timestamp", "Hash" + * @return information in json format. + */ + virtual nlohmann::json GetLastBlockInfo() const = 0; + /** * Use fixed peer to sync * @param address IP or domain name. diff --git a/Interface/ISubWalletCallback.h b/Interface/ISubWalletCallback.h index 9989de6b8..8820c3c56 100644 --- a/Interface/ISubWalletCallback.h +++ b/Interface/ISubWalletCallback.h @@ -88,8 +88,168 @@ namespace Elastos { */ virtual void OnConnectStatusChanged(const std::string &status) = 0; + ////////////////////////////////////////////////// + /* eth sidechain callback */ + ////////////////////////////////////////////////// virtual void OnETHSCEventHandled(const nlohmann::json &event) = 0; + /** + * @param id request id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": "0x1dfd14000" // 8049999872 Wei + * } + */ + virtual nlohmann::json GasPrice(int id) = 0; + + /** + * @param from + * @param to + * @param amount + * @param gasPrice + * @param data + * @param id request id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": "0x5208" // 21000 + * } + */ + virtual nlohmann::json EstimateGas(const std::string &from, + const std::string &to, + const std::string &amount, + const std::string &gasPrice, + const std::string &data, + int id) = 0; + + /** + * @param address + * @param id + * @return + * { + * "id": 0, + * "result": "0x0234c8a3397aab58" // 158972490234375000 + * } + */ + virtual nlohmann::json GetBalance(const std::string &address, int id) = 0; + + /** + * @param tx Signed raw transaction. + * @param id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" + * } + */ + virtual nlohmann::json SubmitTransaction(const std::string &tx, int id) = 0; + + /** + * @param begBlockNumber + * @param endBlockNumber + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": [{ + * "hash":"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b", + * "from":"0xa7d9ddbe1f17865597fbd27ec712455208b6b76d", + * "to":"0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb", + * "contract":"0xb60e8dd61c5d32be8058bb8eb970870f07233155", // or "", if none was created + * "amount":"0xf3dbb76162000", // 4290000000000000 + * "gasLimit":"0x1388", + * "gasPrice":"0x4a817c800", // 20000000000 + * "data":"0x68656c6c6f21", // input + * "nonce":"0x15", // 21 + * "gasUsed":"0xc350", // 50000 + * "blockNumber":"0x5daf3b", // 6139707 + * "blockHash":"0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2", + * "blockConfirmations":"0x100", // 256 + * "blockTransactionIndex":"0x41", // 65 + * "blockTimestamp":"0x55ba467c", + * "isError": "0" + * }, + * { + * ... + * }] + * } + */ + virtual nlohmann::json GetTransactions(const std::string &address, + uint64_t begBlockNumber, + uint64_t endBlockNumber, + int id) = 0; + + /** + * @param contract + * @param address + * @param event + * @param begBlockNumber + * @param endBlockNumber + * @param id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": [{ + * "hash":"0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf", + * "contract":"0xb60e8dd61c5d32be8058bb8eb970870f07233155", // or "", if none was created + * "topics":["0x59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a5"] + * "data":"0x0000000000000000000000000000000000000000000000000000000000000000", + * "gasPrice":"0x4a817c800", // 20000000000 + * "gasUsed":"0x4dc", // 1244 + * "logIndex":"0x1", // 1 + * "blockNumber":"0x1b4", // 436 + * "blockTransactionIndex":"0x0", // 0 + * "blockTimestamp":"0x55ba467c", + * },{ + * ... + * }] + * } + */ + virtual nlohmann::json GetLogs(const std::string &contract, + const std::string &address, + const std::string &event, + uint64_t begBlockNumber, + uint64_t endBlockNumber, + int id) = 0; + + /** + * @param id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * [{ + * "address": "0x407d73d8a49eeb85d32cf465507dd71d507100c1", + * "symbol": "ELA", + * "name": "elastos", + * "decimals": 18, + * "description": "desc", // Optional + * "defaultGasLimit": "0x1388", // Optional + * "defaultGasPrice": "0x1dfd14000" // Optional 8049999872 Wei + * },{ + * ... + * }] + */ + virtual nlohmann::json GetTokens(int id) = 0; + + /** + * @param id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id":0, + * "result": "0x4b7" // 1207 + * } + */ + virtual nlohmann::json GetBlockNumber(int id) = 0; + + /** + * @param address + * @param id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": "0x1" // 1 + * } + */ + virtual nlohmann::json GetNonce(const std::string &address, int id) = 0; + }; } diff --git a/Interface/MasterWalletManager.h b/Interface/MasterWalletManager.h index 6fcd470fe..95d4e3988 100644 --- a/Interface/MasterWalletManager.h +++ b/Interface/MasterWalletManager.h @@ -146,6 +146,8 @@ namespace Elastos { virtual void FlushData(); + virtual void SetLogLevel(const std::string &level); + protected: typedef std::map MasterWalletMap; diff --git a/SDK/Account/Account.cpp b/SDK/Account/Account.cpp index 0bc90098e..c8b595fa6 100644 --- a/SDK/Account/Account.cpp +++ b/SDK/Account/Account.cpp @@ -468,6 +468,32 @@ namespace Elastos { } } + void Account::ResetPassword(const std::string &mnemonic, const std::string &passphrase, const std::string &newPassword) { + if (!_localstore->Readonly()) { + ErrorChecker::CheckPassword(newPassword, "New"); + + uint512 seed = BIP39::DeriveSeed(mnemonic, passphrase); + HDSeed hdseed(seed.bytes()); + HDKeychain rootkey(hdseed.getExtendedKey(true)); + std::string xPubKey = Base58::CheckEncode(rootkey.getChild("44'/0'/0'").getPublic().extkey()); + if (xPubKey != _localstore->GetxPubKey()) + ErrorChecker::ThrowParamException(Error::InvalidArgument, "xpub not match"); + + std::string encryptedSeed = AES::EncryptCCM(bytes_t(seed.begin(), seed.size()), newPassword); + std::string encryptedMnemonic = AES::EncryptCCM(bytes_t(mnemonic.data(), mnemonic.size()), newPassword); + std::string encryptedxPrvKey = AES::EncryptCCM(rootkey.extkey(), newPassword); + HDKeychain requestKey = rootkey.getChild("1'/0"); + std::string encryptedRequestPrvKey = AES::EncryptCCM(requestKey.privkey(), newPassword); + + _localstore->SetSeed(encryptedSeed); + _localstore->SetMnemonic(encryptedMnemonic); + _localstore->SetxPrivKey(encryptedxPrvKey); + _localstore->SetRequestPrivKey(encryptedRequestPrvKey); + + _localstore->Save(); + } + } + nlohmann::json Account::GetBasicInfo() const { nlohmann::json j; if (GetSignType() == MultiSign) @@ -577,8 +603,8 @@ namespace Elastos { _localstore->SetSubWalletInfoList(info); } - void Account::RemoveSubWalletInfo(const CoinInfoPtr &info) { - _localstore->RemoveSubWalletInfo(info); + void Account::RemoveSubWalletInfo(const std::string &chainID) { + _localstore->RemoveSubWalletInfo(chainID); } KeyStore Account::ExportKeystore(const std::string &payPasswd) const { @@ -926,7 +952,8 @@ namespace Elastos { bytes = rootkey.getChild("44'/0'/1'/0/0").pubkey(); _localstore->SetOwnerPubKey(bytes.getHex()); - if (_localstore->GetSeed().empty() && !_localstore->HasPassPhrase() && !_localstore->GetMnemonic().empty()) { + if ((_localstore->GetSeed().empty() || _localstore->GetETHSCPrimaryPubKey().empty()) && + !_localstore->HasPassPhrase() && !_localstore->GetMnemonic().empty()) { if (mnemonic.empty()) { bytes = AES::DecryptCCM(_localstore->GetMnemonic(), payPasswd); mnemonic = std::string((char *) &bytes[0], bytes.size()); diff --git a/SDK/Account/Account.h b/SDK/Account/Account.h index 8f14181c7..8d6881553 100644 --- a/SDK/Account/Account.h +++ b/SDK/Account/Account.h @@ -88,6 +88,8 @@ namespace Elastos { void ChangePassword(const std::string &oldPassword, const std::string &newPassword); + void ResetPassword(const std::string &mnemonic, const std::string &passphrase, const std::string &newPassword); + nlohmann::json GetBasicInfo() const; SignType GetSignType() const; @@ -118,7 +120,7 @@ namespace Elastos { void SetSubWalletInfoList(const std::vector &info); - void RemoveSubWalletInfo(const CoinInfoPtr &info); + void RemoveSubWalletInfo(const std::string &chainID); KeyStore ExportKeystore(const std::string &payPasswd) const; diff --git a/SDK/Account/IAccount.h b/SDK/Account/IAccount.h index b076d513c..778deeee8 100644 --- a/SDK/Account/IAccount.h +++ b/SDK/Account/IAccount.h @@ -71,6 +71,8 @@ namespace Elastos { virtual void ChangePassword(const std::string &oldPassword, const std::string &newPassword) = 0; + virtual void ResetPassword(const std::string &mnemonic, const std::string &passphrase, const std::string &newPassword) = 0; + virtual nlohmann::json GetBasicInfo() const = 0; virtual SignType GetSignType() const = 0; @@ -101,7 +103,7 @@ namespace Elastos { virtual void SetSubWalletInfoList(const std::vector &info) = 0; - virtual void RemoveSubWalletInfo(const CoinInfoPtr &info) = 0; + virtual void RemoveSubWalletInfo(const std::string &chainID) = 0; virtual KeyStore ExportKeystore(const std::string &payPasswd) const = 0; diff --git a/SDK/Account/SideAccount.h b/SDK/Account/SideAccount.h index 46521c621..28629a3e4 100644 --- a/SDK/Account/SideAccount.h +++ b/SDK/Account/SideAccount.h @@ -57,6 +57,8 @@ namespace Elastos { void ChangePassword(const std::string &, const std::string &) {} + void ResetPassword(const std::string &mnemonic, const std::string &passphrase, const std::string &newPassword) {} + nlohmann::json GetBasicInfo() const { nlohmann::json j; j["Type"] = "MultiSign"; @@ -96,7 +98,7 @@ namespace Elastos { void SetSubWalletInfoList(const std::vector &) {} - void RemoveSubWalletInfo(const CoinInfoPtr &) {} + void RemoveSubWalletInfo(const std::string &) {} KeyStore ExportKeystore(const std::string &) const { return KeyStore(); } diff --git a/SDK/Account/SubAccount.cpp b/SDK/Account/SubAccount.cpp index 5bccac94a..3cdf2a1f3 100644 --- a/SDK/Account/SubAccount.cpp +++ b/SDK/Account/SubAccount.cpp @@ -416,7 +416,7 @@ namespace Elastos { } } - ErrorChecker::ThrowLogicException(Error::Address, "Can't found code and path for address " + addr->String()); + Log::error("Can't found code and path for address {}", addr->String()); return false; } diff --git a/SDK/CMakeLists.txt b/SDK/CMakeLists.txt index 12e1aa073..25bf04e52 100644 --- a/SDK/CMakeLists.txt +++ b/SDK/CMakeLists.txt @@ -27,6 +27,7 @@ add_custom_target(libspvsdk) include_directories( . ../ThirdParty/breadwallet-core + ../ThirdParty/breadwallet-core/include ../Interface ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/../ThirdParty/secp256k1/${PROJECT_DEPS_BUILD_PREFIX}/src @@ -37,6 +38,15 @@ include_directories( link_directories( ${PROJECT_INT_DIST_DIR}/lib) +if(MSVC) + add_definitions(/FI"${CMAKE_CURRENT_SOURCE_DIR}/Common/BRNameFix.h") + add_definitions(/FI"${CMAKE_CURRENT_SOURCE_DIR}/Common/secp256k1_name_fix.h") +else() + # GCC or Clang + add_definitions(-include ${CMAKE_CURRENT_SOURCE_DIR}/Common/BRNameFix.h) + add_definitions(-include ${CMAKE_CURRENT_SOURCE_DIR}/Common/secp256k1_name_fix.h) +endif() + if(SPV_ENABLE_STATIC) add_library(spvsdk-static STATIC ${SPVSDK_SOURCE_FILES}) add_dependencies(spvsdk-static ${SPVSDK_DEPENDS}) diff --git a/SDK/Common/BRNameFix.h b/SDK/Common/BRNameFix.h new file mode 100644 index 000000000..a87549ae1 --- /dev/null +++ b/SDK/Common/BRNameFix.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ELASTOS_SDK_BRNAME_FIX_H__ +#define __ELASTOS_SDK_BRNAME_FIX_H__ + +#define BRTxInputSetAddress SPV_BRTxInputSetAddress +#define BRTxInputSetScript SPV_BRTxInputSetScript +#define BRTxInputSetSignature SPV_BRTxInputSetSignature +#define BRTxInputSetWitness SPV_BRTxInputSetWitness +#define BRTxOutputSetAddress SPV_BRTxOutputSetAddress +#define BRTxOutputSetScript SPV_BRTxOutputSetScript +#define BRTransactionFree SPV_BRTransactionFree +#define BRTransactionNew SPV_BRTransactionNew +#define BRTransactionCopy SPV_BRTransactionCopy +#define BRTransactionParse SPV_BRTransactionParse +#define BRTransactionSerialize SPV_BRTransactionSerialize +#define BRTransactionAddInput SPV_BRTransactionAddInput +#define BRTransactionAddOutput SPV_BRTransactionAddOutput +#define BRTransactionShuffleOutputs SPV_BRTransactionShuffleOutputs +#define BRTransactionSize SPV_BRTransactionSize +#define BRTransactionVSize SPV_BRTransactionVSize +#define BRTransactionStandardFee SPV_BRTransactionStandardFee +#define BRTransactionIsSigned SPV_BRTransactionIsSigned +#define BRTransactionSign SPV_BRTransactionSign +#define BRTransactionIsStandard SPV_BRTransactionIsStandard +#define BRTransactionHash SPV_BRTransactionHash +#define BRTransactionEq SPV_BRTransactionEq +#define BRMerkleBlockFree SPV_BRMerkleBlockFree +#define BRMerkleBlockNew SPV_BRMerkleBlockNew +#define BRMerkleBlockCopy SPV_BRMerkleBlockCopy +#define BRMerkleBlockParse SPV_BRMerkleBlockParse +#define BRMerkleBlockSerialize SPV_BRMerkleBlockSerialize +#define BRMerkleBlockTxHashes SPV_BRMerkleBlockTxHashes +#define BRMerkleBlockSetTxHashes SPV_BRMerkleBlockSetTxHashes +#define BRMerkleBlockVerifyDifficulty SPV_BRMerkleBlockVerifyDifficulty +#define BRMerkleBlockIsValid SPV_BRMerkleBlockIsValid +#define BRMerkleBlockContainsTxHash SPV_BRMerkleBlockContainsTxHash +#define BRMerkleBlockHash SPV_BRMerkleBlockHash +#define BRMerkleBlockEq SPV_BRMerkleBlockEq +#define BRBIP38KeyIsValid SPV_BRBIP38KeyIsValid +#define BRKeySetBIP38Key SPV_BRKeySetBIP38Key +#define BRKeyBIP38ItermediateCode SPV_BRKeyBIP38ItermediateCode +#define BRKeyBIP38ItermediateCodeLS SPV_BRKeyBIP38ItermediateCodeLS +#define BRKeySetBIP38ItermediateCode SPV_BRKeySetBIP38ItermediateCode +#define BRKeyBIP38Key SPV_BRKeyBIP38Key +#define BRBIP39Encode SPV_BRBIP39Encode +#define BRBIP39Decode SPV_BRBIP39Decode +#define BRBIP39PhraseIsValid SPV_BRBIP39PhraseIsValid +#define BRBIP39DeriveKey SPV_BRBIP39DeriveKey +#define BRSHA1 SPV_BRSHA1 +#define BRSHA256 SPV_BRSHA256 +#define BRSHA224 SPV_BRSHA224 +#define BRSHA256_2 SPV_BRSHA256_2 +#define BRSHA384 SPV_BRSHA384 +#define BRSHA512 SPV_BRSHA512 +#define BRRMD160 SPV_BRRMD160 +#define BRHash160 SPV_BRHash160 +#define BRSHA3_256 SPV_BRSHA3_256 +#define BRKeccak256 SPV_BRKeccak256 +#define BRMD5 SPV_BRMD5 +#define BRMurmur3_32 SPV_BRMurmur3_32 +#define BRSip64 SPV_BRSip64 +#define BRHMAC SPV_BRHMAC +#define BRHMACDRBG SPV_BRHMACDRBG +#define BRPoly1305 SPV_BRPoly1305 +#define BRChacha20 SPV_BRChacha20 +#define BRChacha20Poly1305AEADEncrypt SPV_BRChacha20Poly1305AEADEncrypt +#define BRChacha20Poly1305AEADDecrypt SPV_BRChacha20Poly1305AEADDecrypt +#define BRAESECBEncrypt SPV_BRAESECBEncrypt +#define BRAESECBDecrypt SPV_BRAESECBDecrypt +#define BRAESCTR SPV_BRAESCTR +#define BRAESCTR_OFFSET SPV_BRAESCTR_OFFSET +#define BRPBKDF2 SPV_BRPBKDF2 +#define BRScrypt SPV_BRScrypt +#define BRRand SPV_BRRand +#define BRSecp256k1ModAdd SPV_BRSecp256k1ModAdd +#define BRSecp256k1ModMul SPV_BRSecp256k1ModMul +#define BRSecp256k1PointGen SPV_BRSecp256k1PointGen +#define BRSecp256k1PointAdd SPV_BRSecp256k1PointAdd +#define BRSecp256k1PointMul SPV_BRSecp256k1PointMul +#define BRPrivKeyIsValid SPV_BRPrivKeyIsValid +#define BRKeySetSecret SPV_BRKeySetSecret +#define BRKeySetPrivKey SPV_BRKeySetPrivKey +#define BRKeySetPubKey SPV_BRKeySetPubKey +#define BRKeyPrivKey SPV_BRKeyPrivKey +#define BRKeyPubKey SPV_BRKeyPubKey +#define BRKeyHash160 SPV_BRKeyHash160 +#define BRKeyAddress SPV_BRKeyAddress +#define BRKeyLegacyAddr SPV_BRKeyLegacyAddr +#define BRKeySign SPV_BRKeySign +#define BRKeyVerify SPV_BRKeyVerify +#define BRKeyClean SPV_BRKeyClean +#define BRKeyCompactSign SPV_BRKeyCompactSign +#define BRKeyRecoverPubKey SPV_BRKeyRecoverPubKey +#define BRKeyECDH SPV_BRKeyECDH +#define BRKeyCompactSignEthereum SPV_BRKeyCompactSignEthereum +#define BRKeyRecoverPubKeyEthereum SPV_BRKeyRecoverPubKeyEthereum +#define BRBase58Encode SPV_BRBase58Encode +#define BRBase58Decode SPV_BRBase58Decode +#define BRBase58CheckEncode SPV_BRBase58CheckEncode +#define BRBase58CheckDecode SPV_BRBase58CheckDecode +#define BRAssertInstall SPV_BRAssertInstall +#define BRAssertUninstall SPV_BRAssertUninstall +#define BRAssertRemoveRecovery SPV_BRAssertRemoveRecovery +#define BRPeerManagerDisconnect SPV_BRPeerManagerDisconnect +#define BREthereumEWMDisconnect SPV_BREthereumEWMDisconnect +#define BRAssertDefineRecover SPV_BRAssertDefineRecover +#define BRBech32Decode SPV_BRBech32Decode +#define BRBech32Encode SPV_BRBech32Encode +#define BRBIP32MasterPubKey SPV_BRBIP32MasterPubKey +#define BRBIP32PubKey SPV_BRBIP32PubKey +#define BRBIP32PrivKey SPV_BRBIP32PrivKey +#define BRBIP32PrivKeyList SPV_BRBIP32PrivKeyList +#define BRBIP32PrivKeyPath SPV_BRBIP32PrivKeyPath +#define BRBIP32vPrivKeyPath SPV_BRBIP32vPrivKeyPath +#define BRBIP32SerializeMasterPrivKey SPV_BRBIP32SerializeMasterPrivKey +#define BRBIP32ParseMasterPrivKey SPV_BRBIP32ParseMasterPrivKey +#define BRBIP32SerializeMasterPubKey SPV_BRBIP32SerializeMasterPubKey +#define BRBIP32ParseMasterPubKey SPV_BRBIP32ParseMasterPubKey +#define BRBIP32APIAuthKey SPV_BRBIP32APIAuthKey +#define BRBIP32BitIDKey SPV_BRBIP32BitIDKey +#define BRVarInt SPV_BRVarInt +#define BRVarIntSet SPV_BRVarIntSet +#define BRVarIntSize SPV_BRVarIntSize +#define BRScriptElements SPV_BRScriptElements +#define BRScriptData SPV_BRScriptData +#define BRScriptPushData SPV_BRScriptPushData +#define BRScriptPKH SPV_BRScriptPKH +#define BRAddressFromScriptPubKey SPV_BRAddressFromScriptPubKey +#define BRAddressFromScriptSig SPV_BRAddressFromScriptSig +#define BRAddressFromWitness SPV_BRAddressFromWitness +#define BRAddressFromHash160 SPV_BRAddressFromHash160 +#define BRAddressScriptPubKey SPV_BRAddressScriptPubKey +#define BRAddressHash160 SPV_BRAddressHash160 +#define BRAddressIsValid SPV_BRAddressIsValid +#define BRAddressHash SPV_BRAddressHash +#define BRSetFree SPV_BRSetFree +#define BRSetNew SPV_BRSetNew +#define BRSetAdd SPV_BRSetAdd +#define BRSetRemove SPV_BRSetRemove +#define BRSetClear SPV_BRSetClear +#define BRSetCount SPV_BRSetCount +#define BRSetContains SPV_BRSetContains +#define BRSetIntersects SPV_BRSetIntersects +#define BRSetGet SPV_BRSetGet +#define BRSetIterate SPV_BRSetIterate +#define BRSetAll SPV_BRSetAll +#define BRSetApply SPV_BRSetApply +#define BRSetUnion SPV_BRSetUnion +#define BRSetMinus SPV_BRSetMinus +#define BRSetIntersect SPV_BRSetIntersect +#define BRKeyECIESAES128SHA256Encrypt SPV_BRKeyECIESAES128SHA256Encrypt +#define BRKeyECIESAES128SHA256Decrypt SPV_BRKeyECIESAES128SHA256Decrypt +#define BRKeyPigeonPairingKey SPV_BRKeyPigeonPairingKey +#define BRKeyPigeonEncrypt SPV_BRKeyPigeonEncrypt +#define BRKeyPigeonDecrypt SPV_BRKeyPigeonDecrypt + +#endif diff --git a/SDK/Common/ErrorChecker.h b/SDK/Common/ErrorChecker.h index 86ed96f19..b8d48627a 100644 --- a/SDK/Common/ErrorChecker.h +++ b/SDK/Common/ErrorChecker.h @@ -95,6 +95,8 @@ namespace Elastos { DepositNotFound = 20059, TooMuchInputs = 20060, LastVoteConfirming = 20061, + ProposalContentTooLarge = 20062, + ProposalHashNotMatch = 20063, // ethereum side chain error code InvalidUnitType = 31000, InvalidEthereumAddress = 32000, diff --git a/SDK/Common/Log.cpp b/SDK/Common/Log.cpp index 1c25b1ac4..e26360062 100644 --- a/SDK/Common/Log.cpp +++ b/SDK/Common/Log.cpp @@ -29,7 +29,23 @@ namespace Elastos { } } -extern "C" void ElastosElaWalletLog(const char *s) { +extern "C" void ElastosElaWalletLogDebug(const char *s) { + Elastos::ElaWallet::Log::debug(s); +} + +extern "C" void ElastosElaWalletLogInfo(const char *s) { Elastos::ElaWallet::Log::info(s); } +extern "C" void ElastosElaWalletLogWarn(const char *s) { + Elastos::ElaWallet::Log::warn(s); +} + +extern "C" void ElastosElaWalletLogError(const char *s) { + Elastos::ElaWallet::Log::error(s); +} + +extern "C" void ElastosElaWalletLogCritical(const char *s) { + Elastos::ElaWallet::Log::critical(s); +} + diff --git a/SDK/Common/secp256k1_name_fix.h b/SDK/Common/secp256k1_name_fix.h new file mode 100644 index 000000000..f87d4d452 --- /dev/null +++ b/SDK/Common/secp256k1_name_fix.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ELASTOS_SDK_SECP256K1_NAME_FIX_H__ +#define __ELASTOS_SDK_SECP256K1_NAME_FIX_H__ + +#define secp256k1_context_create SPV_secp256k1_context_create +#define secp256k1_context_clone SPV_secp256k1_context_clone +#define secp256k1_context_destroy SPV_secp256k1_context_destroy +#define secp256k1_context_set_illegal_callback SPV_secp256k1_context_set_illegal_callback +#define secp256k1_context_set_error_callback SPV_secp256k1_context_set_error_callback +#define secp256k1_scratch_space_create SPV_secp256k1_scratch_space_create +#define secp256k1_scratch_space_destroy SPV_secp256k1_scratch_space_destroy +#define secp256k1_ec_pubkey_parse SPV_secp256k1_ec_pubkey_parse +#define secp256k1_ec_pubkey_serialize SPV_secp256k1_ec_pubkey_serialize +#define secp256k1_ecdsa_signature_parse_compact SPV_secp256k1_ecdsa_signature_parse_compact +#define secp256k1_ecdsa_signature_parse_der SPV_secp256k1_ecdsa_signature_parse_der +#define secp256k1_ecdsa_signature_serialize_der SPV_secp256k1_ecdsa_signature_serialize_der +#define secp256k1_ecdsa_signature_serialize_compact SPV_secp256k1_ecdsa_signature_serialize_compact +#define secp256k1_ecdsa_verify SPV_secp256k1_ecdsa_verify +#define secp256k1_ecdsa_signature_normalize SPV_secp256k1_ecdsa_signature_normalize +#define secp256k1_ecdsa_sign SPV_secp256k1_ecdsa_sign +#define secp256k1_ec_seckey_verify SPV_secp256k1_ec_seckey_verify +#define secp256k1_ec_pubkey_create SPV_secp256k1_ec_pubkey_create +#define secp256k1_ec_privkey_negate SPV_secp256k1_ec_privkey_negate +#define secp256k1_ec_pubkey_negate SPV_secp256k1_ec_pubkey_negate +#define secp256k1_ec_privkey_tweak_add SPV_secp256k1_ec_privkey_tweak_add +#define secp256k1_ec_pubkey_tweak_add SPV_secp256k1_ec_pubkey_tweak_add +#define secp256k1_ec_privkey_tweak_mul SPV_secp256k1_ec_privkey_tweak_mul +#define secp256k1_ec_pubkey_tweak_mul SPV_secp256k1_ec_pubkey_tweak_mul +#define secp256k1_context_randomize SPV_secp256k1_context_randomize +#define secp256k1_ec_pubkey_combine SPV_secp256k1_ec_pubkey_combine +#define secp256k1_ecdh SPV_secp256k1_ecdh +#define secp256k1_ecdsa_recoverable_signature_parse_compact SPV_secp256k1_ecdsa_recoverable_signature_parse_compact +#define secp256k1_ecdsa_recoverable_signature_convert SPV_secp256k1_ecdsa_recoverable_signature_convert +#define secp256k1_ecdsa_recoverable_signature_serialize_compact SPV_secp256k1_ecdsa_recoverable_signature_serialize_compact +#define secp256k1_ecdsa_sign_recoverable SPV_secp256k1_ecdsa_sign_recoverable +#define secp256k1_ecdsa_recover SPV_secp256k1_ecdsa_recover + +#endif diff --git a/SDK/Ethereum/EthereumClient.cpp b/SDK/Ethereum/EthereumClient.cpp index 2e7995810..d51a6dc51 100644 --- a/SDK/Ethereum/EthereumClient.cpp +++ b/SDK/Ethereum/EthereumClient.cpp @@ -40,7 +40,7 @@ namespace Elastos { "pubkey should be 65 bytes and begin with 0x04"); _ewm = EthereumEWMPtr( - new EthereumEWM(this, EthereumEWM::Mode::P2P_ONLY, _network, _storagePath, pubkey)); + new EthereumEWM(this, CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC, _network, _storagePath, pubkey, 0, 6)); } void EthereumClient::getGasPrice(BREthereumWallet wid, int rid) { @@ -48,13 +48,14 @@ namespace Elastos { } void EthereumClient::getGasEstimate(BREthereumWallet wid, - BREthereumTransfer tid, + BREthereumCookie cookie, const std::string &from, const std::string &to, const std::string &amount, + const std::string &gasPrice, const std::string &data, int rid) { - _client->getGasEstimate(wid, tid, from, to, amount, data, rid); + _client->getGasEstimate(wid, cookie, from, to, amount, gasPrice, data, rid); } void EthereumClient::getBalance(BREthereumWallet wid, const std::string &address, int rid) { @@ -104,37 +105,27 @@ namespace Elastos { _client->getNonce(address, rid); } - void EthereumClient::handleEWMEvent(EthereumEWM::EWMEvent event, EthereumEWM::Status status, - const std::string &errorDescription) { - _client->handleEWMEvent(event, status, errorDescription); + void EthereumClient::handleEWMEvent(const BREthereumEWMEvent &event) { + _client->handleEWMEvent(event); } - void EthereumClient::handlePeerEvent(EthereumEWM::PeerEvent event, EthereumEWM::Status status, - const std::string &errorDescription) { - _client->handlePeerEvent(event, status, errorDescription); + void EthereumClient::handlePeerEvent(const BREthereumPeerEvent &event) { + _client->handlePeerEvent(event); } void EthereumClient::handleWalletEvent(const EthereumWalletPtr &wallet, - EthereumEWM::WalletEvent event, EthereumEWM::Status status, - const std::string &errorDescription) { - _client->handleWalletEvent(wallet, event, status, errorDescription); + const BREthereumWalletEvent &event) { + _client->handleWalletEvent(wallet, event); } - void EthereumClient::handleTokenEvent(const EthereumTokenPtr &token, EthereumEWM::TokenEvent event) { + void EthereumClient::handleTokenEvent(const EthereumTokenPtr &token, const BREthereumTokenEvent &event) { _client->handleTokenEvent(token, event); } - void EthereumClient::handleBlockEvent(const EthereumBlockPtr &block, - EthereumEWM::BlockEvent event, EthereumEWM::Status status, - const std::string &errorDescription) { - _client->handleBlockEvent(block, event, status, errorDescription); - } - void EthereumClient::handleTransferEvent(const EthereumWalletPtr &wallet, const EthereumTransferPtr &transaction, - EthereumEWM::TransactionEvent event, EthereumEWM::Status status, - const std::string &errorDescription) { - _client->handleTransferEvent(wallet, transaction, event, status, errorDescription); + const BREthereumTransferEvent &event) { + _client->handleTransferEvent(wallet, transaction, event); } } diff --git a/SDK/Ethereum/EthereumClient.h b/SDK/Ethereum/EthereumClient.h index e1f86880c..714c8d794 100644 --- a/SDK/Ethereum/EthereumClient.h +++ b/SDK/Ethereum/EthereumClient.h @@ -35,61 +35,27 @@ namespace Elastos { const std::string &storagePath, const bytes_t &pubkey); public: - // implement EthereumEWM::Client - // typedef void (*BREthereumClientHandlerGetGasPrice) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // int rid); virtual void getGasPrice(BREthereumWallet wid, int rid); - // typedef void (*BREthereumClientHandlerEstimateGas) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // BREthereumTransactionId tid, - // const char *from, - // const char *to, - // const char *amount, - // const char *data, - // int rid); virtual void getGasEstimate(BREthereumWallet wid, - BREthereumTransfer tid, + BREthereumCookie cookie, const std::string &from, const std::string &to, const std::string &amount, + const std::string &gasPrice, const std::string &data, int rid); - // typedef void (*BREthereumClientHandlerGetBalance) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // const char *address, - // int rid); virtual void getBalance(BREthereumWallet wid, const std::string &address, int rid); - // typedef void (*BREthereumClientHandlerSubmitTransaction) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // BREthereumTransactionId tid, - // const char *transaction, - // int rid); virtual void submitTransaction(BREthereumWallet wid, BREthereumTransfer tid, const std::string &rawTransaction, int rid); - // &:wtypedef void (*BREthereumClientHandlerGetTransactions) (BREthereumClientContext context, - // BREthereumEWM ewm, - // const char *address, - // int rid); virtual void getTransactions(const std::string &address, uint64_t begBlockNumber, uint64_t endBlockNumber, int rid); - // typedef void (*BREthereumClientHandlerGetLogs) (BREthereumClientContext context, - // BREthereumEWM ewm, - // const char *contract, - // const char *address, - // const char *event, - // int rid); virtual void getLogs(const std::string &contract, const std::string &address, const std::string &event, @@ -97,15 +63,6 @@ namespace Elastos { uint64_t endBlockNumber, int rid); - - //typedef void - //(*BREthereumClientHandlerGetBlocks) (BREthereumClientContext context, - // BREthereumEWM ewm, - // const char *address, - // BREthereumSyncInterestSet interests, - // uint64_t blockNumberStart, - // uint64_t blockNumberStop, - // int rid); virtual void getBlocks(const std::string &address, int interests, uint64_t blockNumberStart, @@ -125,26 +82,18 @@ namespace Elastos { // int rid); virtual void getNonce(const std::string &address, int rid); - virtual void handleEWMEvent(EthereumEWM::EWMEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + virtual void handleEWMEvent(const BREthereumEWMEvent &event); - virtual void handlePeerEvent(EthereumEWM::PeerEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + virtual void handlePeerEvent(const BREthereumPeerEvent &event); virtual void handleWalletEvent(const EthereumWalletPtr &wallet, - EthereumEWM::WalletEvent event, EthereumEWM::Status status, - const std::string &errorDescription); - - virtual void handleTokenEvent(const EthereumTokenPtr &token, EthereumEWM::TokenEvent event); + const BREthereumWalletEvent &event); - virtual void handleBlockEvent(const EthereumBlockPtr &block, - EthereumEWM::BlockEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + virtual void handleTokenEvent(const EthereumTokenPtr &token, const BREthereumTokenEvent &event); virtual void handleTransferEvent(const EthereumWalletPtr &wallet, const EthereumTransferPtr &transaction, - EthereumEWM::TransactionEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + const BREthereumTransferEvent &event); protected: friend class EthSidechainSubWallet; diff --git a/SDK/Ethereum/EthereumEWM.cpp b/SDK/Ethereum/EthereumEWM.cpp index abf067627..9386d04bd 100644 --- a/SDK/Ethereum/EthereumEWM.cpp +++ b/SDK/Ethereum/EthereumEWM.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace Elastos { namespace ElaWallet { @@ -41,17 +42,24 @@ namespace Elastos { c->trampolineGetGasPrice(node, wid, id); } - static void clientEstimateGas(BREthereumClientContext context, BREthereumEWM node, - BREthereumWallet wid, BREthereumTransfer tid, - const char *from, const char *to, const char *amount, - const char *data, int id) { + static void clientEstimateGas(BREthereumClientContext context, + BREthereumEWM ewm, + BREthereumWallet wid, + BREthereumCookie cookie, + const char *from, + const char *to, + const char *amount, + const char *gasPrice, + const char *data, + int rid) { if (NULL == context) return; EthereumEWM *c = (EthereumEWM *) context; - c->trampolineGetGasEstimate(node, wid, tid, + c->trampolineGetGasEstimate(ewm, wid, cookie, from ? from : "", to ? to : "", amount ? amount : "", - data ? data : "", id); + gasPrice ? gasPrice : "", + data ? data : "", rid); } static void clientGetBalance(BREthereumClientContext context, BREthereumEWM node, @@ -117,27 +125,28 @@ namespace Elastos { } - static void clientEWMEventHandler(BREthereumClientContext context, BREthereumEWM ewm, BREthereumEWMEvent event, - BREthereumStatus status, const char *errorDescription) { + static void + clientEWMEventHandler(BREthereumClientContext context, BREthereumEWM ewm, BREthereumEWMEvent event) { if (NULL == context) return; EthereumEWM *c = (EthereumEWM *) context; - c->trampolineEWMEvent(ewm, event, status, errorDescription ? errorDescription : ""); + c->trampolineEWMEvent(ewm, event); } - static void clientPeerEventHandler(BREthereumClientContext context, BREthereumEWM ewm, - BREthereumPeerEvent event, BREthereumStatus status, - const char *errorDescription) { + static void clientPeerEventHandler(BREthereumClientContext context, + BREthereumEWM ewm, + BREthereumPeerEvent event) { if (NULL == context) return; EthereumEWM *c = (EthereumEWM *) context; - c->trampolinePeerEvent(ewm, event, status, errorDescription ? errorDescription : ""); + c->trampolinePeerEvent(ewm, event); } - static void clientWalletEventHandler(BREthereumClientContext context, BREthereumEWM node, - BREthereumWallet wid, BREthereumWalletEvent event, - BREthereumStatus status, const char *errorDescription) { + static void clientWalletEventHandler(BREthereumClientContext context, + BREthereumEWM ewm, + BREthereumWallet wid, + BREthereumWalletEvent event) { if (NULL == context) return; EthereumEWM *c = (EthereumEWM *) context; - c->trampolineWalletEvent(node, wid, event, status, errorDescription ? errorDescription : ""); + c->trampolineWalletEvent(ewm, wid, event); } static void clientTokenEventHandler(BREthereumClientContext context, BREthereumEWM ewm, @@ -147,127 +156,261 @@ namespace Elastos { c->trampolineTokenEvent(ewm, token, event); } - static void clientTransferEventHandler(BREthereumClientContext context, BREthereumEWM node, - BREthereumWallet wid, BREthereumTransfer tid, - BREthereumTransferEvent event, BREthereumStatus status, - const char *errorDescription) { + static void clientTransferEventHandler(BREthereumClientContext context, + BREthereumEWM ewm, + BREthereumWallet wid, + BREthereumTransfer tid, + BREthereumTransferEvent event) { if (NULL == context) return; EthereumEWM *c = (EthereumEWM *) context; - c->trampolineTransferEvent(node, wid, tid, event, status, errorDescription ? errorDescription : ""); + c->trampolineTransferEvent(ewm, wid, tid, event); } - std::string EthereumEWM::Status2String(Status s) { + std::string EthereumEWM::Status2String(const BREthereumStatus &s) { std::string status; switch (s) { - case SUCCESS: status = "SUCCESS"; break; - // Reference access, - case ERROR_UNKNOWN_NODE: status = "ERROR_UNKNOWN_NODE"; break; - case ERROR_UNKNOWN_TRANSACTION: status = "ERROR_UNKNOWN_TRANSACTION"; break; - case ERROR_UNKNOWN_ACCOUNT: status = "ERROR_UNKNOWN_ACCOUNT"; break; - case ERROR_UNKNOWN_WALLET: status = "ERROR_UNKNOWN_WALLET"; break; - case ERROR_UNKNOWN_BLOCK: status = "ERROR_UNKNOWN_BLOCK"; break; - case ERROR_UNKNOWN_LISTENER: status = "ERROR_UNKNOWN_LISTENER"; break; - - // Node - case ERROR_NODE_NOT_CONNECTED: status = "ERROR_NODE_NOT_CONNECTED"; break; - - // Transaction - case ERROR_TRANSACTION_X: status = "ERROR_TRANSACTION_X"; break; - - // Acount - // Wallet - // Block - // Listener - - // Numeric - case ERROR_NUMERIC_PARSE: status = "ERROR_NUMERIC_PARSE"; break; - case NUMBER_OF_STATUS_EVENTS: status = "NUMBER_OF_STATUS_EVENTS"; break; - default: status = "UNKNOWN"; break; + case SUCCESS: + status = "SUCCESS"; + break; + case ERROR_FAILED: + status = "ERROR_FAILED"; + break; + // Reference access, + case ERROR_UNKNOWN_NODE: + status = "ERROR_UNKNOWN_NODE"; + break; + case ERROR_UNKNOWN_TRANSACTION: + status = "ERROR_UNKNOWN_TRANSACTION"; + break; + case ERROR_UNKNOWN_ACCOUNT: + status = "ERROR_UNKNOWN_ACCOUNT"; + break; + case ERROR_UNKNOWN_WALLET: + status = "ERROR_UNKNOWN_WALLET"; + break; + case ERROR_UNKNOWN_BLOCK: + status = "ERROR_UNKNOWN_BLOCK"; + break; + case ERROR_UNKNOWN_LISTENER: + status = "ERROR_UNKNOWN_LISTENER"; + break; + + // Node + case ERROR_NODE_NOT_CONNECTED: + status = "ERROR_NODE_NOT_CONNECTED"; + break; + + // Transaction + case ERROR_TRANSACTION_HASH_MISMATCH: + status = "ERROR_TRANSACTION_HASH_MISMATCH"; + break; + case ERROR_TRANSACTION_SUBMISSION: + status = "ERROR_TRANSACTION_SUBMISSION"; + break; + + // Acount + // Wallet + // Block + // Listener + + // Numeric + case ERROR_NUMERIC_PARSE: + status = "ERROR_NUMERIC_PARSE"; + break; + default: + status = "UNKNOWN"; + break; } return status; } - std::string EthereumEWM::WalletEvent2String(WalletEvent e) { + nlohmann::json EthereumEWM::WalletEvent2Json(const BREthereumWalletEvent &e) { std::string event; - switch (e) { - case WalletEvent_CREATED: event = "CREATED"; break; - case WalletEvent_BALANCE_UPDATED: event = "BALANCE_UPDATED"; break; - case WalletEvent_DEFAULT_GAS_LIMIT_UPDATED: event = "DEFAULT_GAS_LIMIT_UPDATED"; break; - case WalletEvent_DEFAULT_GAS_PRICE_UPDATED: event = "DEFAULT_GAS_PRICE_UPDATED"; break; - case WalletEvent_DELETED: event = "DELETED"; break; - case WalletEvent_NUMBER_OF_WALLET_EVENTS: event = "NUMBER_OF_WALLET_EVENTS"; break; - default: event = "UNDEFINE"; break; + nlohmann::json j; + + switch (e.type) { + case WALLET_EVENT_CREATED: + event = "CREATED"; + break; + case WALLET_EVENT_BALANCE_UPDATED: + event = "BALANCE_UPDATED"; + break; + case WALLET_EVENT_DEFAULT_GAS_LIMIT_UPDATED: + event = "DEFAULT_GAS_LIMIT_UPDATED"; + break; + case WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED: + event = "DEFAULT_GAS_PRICE_UPDATED"; + break; + case WALLET_EVENT_FEE_ESTIMATED: + event = "FEE_ESTIMATED"; +// j["Cookie"] = event.u.feeEstimate.cookie; + j["GasEstimate"] = e.u.feeEstimate.gasEstimate.amountOfGas; + j["GasPrice"] = e.u.feeEstimate.gasPrice.etherPerGas.valueInWEI.u64[0]; + break; + case WALLET_EVENT_DELETED: + event = "DELETED"; + break; + default: + event = "UNDEFINE"; + break; } - return event; + + j["Type"] = "WalletEvent"; + j["Event"] = event; + j["Status"] = EthereumEWM::Status2String(e.status); + j["ErrorDescription"] = e.errorDescription; + + return j; } - std::string EthereumEWM::TokenEvent2String(TokenEvent e) { + nlohmann::json EthereumEWM::TokenEvent2Json(const BREthereumTokenEvent &e) { std::string event; - switch (e) { - case TokenEvent_CREATED: event = "CREATED"; break; - case TokenEvent_DELETED: event = "DELETED"; break; - case TokenEvent_NUMBER_OF_TOKEN_EVENTS: event = "NUMBER_OF_TOKEN_EVENTS"; break; - default: event = "UNDEFINE"; break; + nlohmann::json j; + + switch (e.type) { + case TOKEN_EVENT_CREATED: + event = "CREATED"; + break; + case TOKEN_EVENT_DELETED: + event = "DELETED"; + break; + default: + event = "UNDEFINE"; + break; } - return event; + j["Type"] = "TokenEvent"; + j["Event"] = event; + j["Status"] = EthereumEWM::Status2String(e.status); + j["ErrorDescription"] = e.errorDescription; + + return j; } - std::string EthereumEWM::BlockEvent2String(BlockEvent e) { + nlohmann::json EthereumEWM::TransferEvent2Json(const BREthereumTransferEvent &e) { std::string event; - switch (e) { - case BlockEvent_CREATED: event = "CREATED"; break; - case BlockEvent_CHAINED: event = "CHAINED"; break; - case BlockEvent_ORPHANED: event = "ORPHANED"; break; - case BlockEvent_DELETED: event = "DELETED"; break; - case BlockEvent_NUMBER_OF_BLOCK_EVENT: event = "NUMBER_OF_BLOCK_EVENT"; break; - default: event = "UNDEFINE"; break; + nlohmann::json j; + + switch (e.type) { + case TRANSFER_EVENT_CREATED: + event = "CREATED"; + break; + case TRANSFER_EVENT_SIGNED: + event = "SIGNED"; + break; + case TRANSFER_EVENT_SUBMITTED: + event = "SUBMITTED"; + break; + case TRANSFER_EVENT_INCLUDED: + event = "INCLUDED"; + break; // aka confirmed + case TRANSFER_EVENT_ERRORED: + event = "ERRORED"; + break; + case TRANSFER_EVENT_GAS_ESTIMATE_UPDATED: + event = "GAS_ESTIMATE_UPDATED"; + break; + case TRANSFER_EVENT_DELETED: + event = "DELETED"; + break; + default: + event = "UNDEFINE"; + break; } - return event; + + j["Type"] = "TransferEvent"; + j["Event"] = event; + j["Status"] = EthereumEWM::Status2String(e.status); + j["ErrorDescription"] = e.errorDescription; + + return j; } - std::string EthereumEWM::TransactionEvent2String(TransactionEvent e) { + nlohmann::json EthereumEWM::EWMEvent2Json(const BREthereumEWMEvent &e) { std::string event; - switch (e) { - case TransactionEvent_CREATED: event = "CREATED"; break; - case TransactionEvent_SIGNED: event = "SIGNED"; break; - case TransactionEvent_SUBMITTED: event = "SUBMITTED"; break; - case TransactionEvent_INCLUDED: event = "INCLUDED"; break; // aka confirmed - case TransactionEvent_ERRORED: event = "ERRORED"; break; - case TransactionEvent_GAS_ESTIMATE_UPDATED: event = "GAS_ESTIMATE_UPDATED"; break; - case TransactionEvent_BLOCK_CONFIRMATIONS_UPDATED: event = "BLOCK_CONFIRMATIONS_UPDATED"; break; - case TransactionEvent_DELETED: event = "DELETED"; break; - case TransactionEvent_NUMBER_OF_TRANSACTION_EVENTS: event = "NUMBER_OF_TRANSACTION_EVENTS"; break; - default: event = "UNDEFINE"; break; + nlohmann::json j; + + switch (e.type) { + case EWM_EVENT_CREATED: + event = "CREATED"; + break; + case EWM_EVENT_CHANGED: + event = "CHANGED"; + j["OldState"] = EthereumEWM::EWMState2String(e.u.changed.oldState); + j["NewState"] = EthereumEWM::EWMState2String(e.u.changed.newState); + break; + case EWM_EVENT_SYNC_PROGRESS: + event = "PROGRESS"; + j["Timestamp"] = e.u.syncProgress.timestamp; + j["PercentComplete"] = e.u.syncProgress.percentComplete; + break; + case EWM_EVENT_BLOCK_HEIGHT_UPDATED: + event = "HEIGHT_UPDATED"; + j["BlockHeight"] = e.u.blockHeight.value; + break; + case EWM_EVENT_NETWORK_UNAVAILABLE: + event = "NETWORK_UNAVAILABLE"; + break; + case EWM_EVENT_DELETED: + event = "DELETED"; + break; + default: + event = "UNDEFINE"; + break; } - return event; + + j["Type"] = "EWMEvent"; + j["Event"] = event; + j["Status"] = Status2String(e.status); + j["ErrorDescription"] = e.errorDescription; + return j; } - std::string EthereumEWM::EWMEvent2String(EWMEvent e) { + nlohmann::json EthereumEWM::PeerEvent2Json(const BREthereumPeerEvent &e) { std::string event; - switch (e) { - case EWMEvent_CREATED: event = "CREATED"; break; - case EWMEvent_SYNC_STARTED: event = "SYNC_STARTED"; break; - case EWMEvent_SYNC_CONTINUES: event = "SYNC_CONTINUES"; break; - case EWMEvent_SYNC_STOPPED: event = "SYNC_STOPPED"; break; - case EWMEvent_NETWORK_UNAVAILABLE: event = "NETWORK_UNAVAILABLE"; break; - case EWMEvent_DELETED: event = "DELETED"; break; - case EWMEvent_NUMBER_OF_EWM_EVENTS: event = "NUMBER_OF_EWM_EVENTS"; break; - default: event = "UNDEFINE"; break; + switch (e.type) { + case PEER_EVENT_CREATED: + event = "CREATED"; + break; + case PEER_EVENT_DELETED: + event = "DELETED"; + break; + default: + event = "UNDEFINE"; + break; } - return event; + nlohmann::json j; + j["Type"] = "PeerEvent"; + j["Event"] = event; + j["Status"] = EthereumEWM::Status2String(e.status); + j["ErrorDescription"] = e.errorDescription; + return j; } - std::string EthereumEWM::PeerEvent2String(PeerEvent e) { - std::string event; - switch (e) { - case PeerEvent_CREATED: event = "CREATED"; break; - case PeerEvent_DELETED: event = "DELETED"; break; - case PeerEvent_NUMBER_OF_PEER_EVENTS: event = "NUMBER_OF_PEER_EVENTS"; break; - default: event = "UNDEFINE"; break; + std::string EthereumEWM::EWMState2String(const BREthereumEWMState &s) { + std::string state; + switch (s) { + case EWM_STATE_CREATED: + state = "CREATED"; + break; + case EWM_STATE_CONNECTED: + state = "CONNECTED"; + break; + case EWM_STATE_SYNCING: + state = "SYNCING"; + break; + case EWM_STATE_DISCONNECTED: + state = "DISCONNECTED"; + break; + case EWM_STATE_DELETED: + state = "DELETED"; + break; + default: + state = "UNDEFINE"; + break; } - return event; + return state; } // Client Announcers @@ -279,17 +422,26 @@ namespace Elastos { ewmAnnounceGasPrice(_ewm, wid, gasPrice.c_str(), rid); } - void - EthereumEWM::announceGasEstimate(BREthereumWallet wid, BREthereumTransfer tid, const std::string &gasEstimate, - int rid) { - ewmAnnounceGasEstimate(_ewm, wid, tid, gasEstimate.c_str(), rid); + void EthereumEWM::announceGasEstimateSuccess(BREthereumWallet wid, BREthereumCookie cookie, + const std::string &gasEstimate, + const std::string &gasPrice, + int rid) { + ewmAnnounceGasEstimateSuccess(_ewm, wid, cookie, gasEstimate.data(), gasPrice.data(), rid); + } + + void EthereumEWM::announceGasEstimateFailure(BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status, + int rid) { + ewmAnnounceGasEstimateFailure(_ewm, wallet, cookie, status, rid); } void EthereumEWM::announceSubmitTransaction(BREthereumWallet wid, BREthereumTransfer tid, const std::string &hash, int errorCode, const std::string &errorMessage, int rid) { - ewmAnnounceSubmitTransfer(_ewm, wid, tid, hash.c_str(), errorCode, errorMessage.c_str(), rid); + ewmAnnounceSubmitTransfer(_ewm, wid, tid, hash.empty() ? nullptr : hash.c_str(), errorCode, + errorMessage.empty() ? nullptr : errorMessage.c_str(), rid); } void EthereumEWM::announceTransaction(int id, @@ -312,7 +464,7 @@ namespace Elastos { // confirmations // txreceipt_status const std::string &isError) { - ewmAnnounceTransaction(_ewm, id, hash.data(), from.data(), to.data(), contract.data(), amount.data(), + ewmAnnounceTransaction(_ewm, id, hash.data(), from.data(), to.empty() ? NULL : to.data(), contract.data(), amount.data(), gasLimit.data(), gasPrice.data(), data.data(), nonce.data(), gasUsed.data(), blockNumber.data(), blockHash.data(), blockConfirmations.data(), blockTransactionIndex.data(), blockTimestamp.data(), isError.data()); @@ -355,20 +507,29 @@ namespace Elastos { ewmAnnounceNonce(_ewm, address.data(), nonce.data(), rid); } + void EthereumEWM::announceBlocks(int id, std::vector blockNumbers) { + ewmAnnounceBlocks(_ewm, id, blockNumbers.size(), blockNumbers.data()); + } + void EthereumEWM::announceToken(const std::string &address, + int rid, const std::string &symbol, const std::string &name, const std::string &description, int decimals, const std::string &defaultGasLimit, - const std::string &defaultGasPrice, - int rid) { - ewmAnnounceToken(_ewm, address.data(), symbol.data(), name.data(), description.data(), decimals, - defaultGasLimit.data(), defaultGasPrice.data(), rid); + const std::string &defaultGasPrice) { + const char *defaultGasLimitPtr, *defaultGasPricePtr; + + defaultGasLimitPtr = defaultGasLimit.empty() ? NULL : defaultGasLimit.c_str(); + defaultGasPricePtr = defaultGasPrice.empty() ? NULL : defaultGasPrice.c_str(); + + ewmAnnounceToken(_ewm, rid, address.data(), symbol.data(), name.data(), description.data(), decimals, + defaultGasLimitPtr, defaultGasPricePtr); } void EthereumEWM::announceTokenComplete(int rid, bool success) { - ewmAnnounceTokenComplete(_ewm, AS_ETHEREUM_BOOLEAN(success), rid); + ewmAnnounceTokenComplete(_ewm, rid, AS_ETHEREUM_BOOLEAN(success)); } EthereumNetworkPtr EthereumEWM::getNetwork() const { @@ -413,6 +574,28 @@ namespace Elastos { return wallet; } + std::vector EthereumEWM::getWallets() { + std::vector wallets; + + for (WalletMap::const_iterator it = _wallets.cbegin(); it != _wallets.cend(); ++it) { + EthereumTokenPtr t; + BREthereumToken tokenRef = ewmWalletGetToken(_ewm, it->first); + if (NULL != tokenRef) + t = lookupTokenByReference(tokenRef); + + EthereumWalletPtr wallet; + if (nullptr != t) { + wallet = EthereumWalletPtr(new EthereumWallet(this, it->first, _account, _network, t)); + } else { + wallet = EthereumWalletPtr(new EthereumWallet(this, it->first, _account, _network)); + } + + wallets.push_back(wallet); + } + + return wallets; + } + EthereumWalletPtr EthereumEWM::getWallet() { BREthereumWallet wid = ewmGetWallet(_ewm); return walletLookupOrCreate(wid, nullptr); @@ -442,6 +625,10 @@ namespace Elastos { return transfer; } + void EthereumEWM::transferDelete(const EthereumTransferPtr &transfer) { + ewmTransferDelete (_ewm, transfer->getRaw()); + } + EthereumBlockPtr EthereumEWM::blockLookupOrCreate(BREthereumBlock bid) { BlockMap::iterator it = _blocks.find(bid); if (it != _blocks.end()) @@ -496,26 +683,36 @@ namespace Elastos { ewmUpdateTokens(_ewm); } - EthereumEWM::EthereumEWM(Client *client, EthereumEWM::Mode mode, const EthereumNetworkPtr &network, + EthereumEWM::EthereumEWM(Client *client, BRCryptoSyncMode mode, const EthereumNetworkPtr &network, const std::string &storagePath, const std::string &paperKey, - const std::vector &wordList) : - EthereumEWM(createRawEWM(mode, network->getRaw(), storagePath, paperKey, wordList), client, network) { + const std::vector &wordList, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) : + EthereumEWM(createRawEWM(mode, network->getRaw(), storagePath, paperKey, wordList, blockHeight, + confirmationsUntilFinal), client, network) { } - EthereumEWM::EthereumEWM(Client *client, EthereumEWM::Mode mode, const EthereumNetworkPtr &network, - const std::string &storagePath, const bytes_t &publicKey) : - EthereumEWM(createRawEWMPublicKey(mode, network->getRaw(), storagePath, publicKey), client, network) { + EthereumEWM::EthereumEWM(Client *client, BRCryptoSyncMode mode, const EthereumNetworkPtr &network, + const std::string &storagePath, const bytes_t &publicKey, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) : + EthereumEWM(createRawEWMPublicKey(mode, network->getRaw(), storagePath, publicKey, blockHeight, + confirmationsUntilFinal), client, network) { } EthereumEWM::EthereumEWM(BREthereumEWM ewm, EthereumEWM::Client *client, const EthereumNetworkPtr &network) : _ewm(ewm), _client(client), _network(network), + _executor(1), _account(EthereumAccountPtr(new EthereumAccount(this, ewmGetAccount(ewm)))) { } - BREthereumEWM EthereumEWM::createRawEWM(Mode mode, BREthereumNetwork network, const std::string &storagePath, - BREthereumAccount account) { + BREthereumEWM + EthereumEWM::createRawEWM(BRCryptoSyncMode mode, BREthereumNetwork network, const std::string &storagePath, + BREthereumAccount account, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) { BREthereumClient brClient = { this, clientGetBalance, @@ -536,12 +733,15 @@ namespace Elastos { clientTransferEventHandler }; - return ewmCreate(network, account, ETHEREUM_TIMESTAMP_UNKNOWN, (BREthereumMode) mode, brClient, storagePath.data()); + return ewmCreate(network, account, ETHEREUM_TIMESTAMP_UNKNOWN, mode, brClient, storagePath.data(), + blockHeight, confirmationsUntilFinal); } - BREthereumEWM EthereumEWM::createRawEWM(Mode mode, BREthereumNetwork network, + BREthereumEWM EthereumEWM::createRawEWM(BRCryptoSyncMode mode, BREthereumNetwork network, const std::string &storagePath, const std::string &paperKey, - const std::vector &wordList) { + const std::vector &wordList, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) { int wordsCount = wordList.size(); assert (BIP39_WORDLIST_COUNT == wordsCount); static const char *wordListPtr[BIP39_WORDLIST_COUNT]; @@ -571,11 +771,13 @@ namespace Elastos { }; return ewmCreateWithPaperKey((BREthereumNetwork) network, paperKey.data(), ETHEREUM_TIMESTAMP_UNKNOWN, - (BREthereumMode) mode, brClient, storagePath.data()); + mode, brClient, storagePath.data(), blockHeight, confirmationsUntilFinal); } - BREthereumEWM EthereumEWM::createRawEWMPublicKey(Mode mode, BREthereumNetwork network, - const std::string &storagePath, const bytes_t &pubkey) { + BREthereumEWM EthereumEWM::createRawEWMPublicKey(BRCryptoSyncMode mode, BREthereumNetwork network, + const std::string &storagePath, const bytes_t &pubkey, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) { assert (65 == pubkey.size()); BRKey key = { @@ -608,11 +810,12 @@ namespace Elastos { }; // TODO: set to correct time to replace ETHEREUM_TIMESTAMP_UNKNOWN - return ewmCreateWithPublicKey((BREthereumNetwork) network, key, ETHEREUM_TIMESTAMP_UNKNOWN, - (BREthereumMode) mode, brClient, storagePath.data()); + return ewmCreateWithPublicKey((BREthereumNetwork) network, key, time(NULL), + mode, brClient, storagePath.data(), blockHeight, confirmationsUntilFinal); } bool EthereumEWM::connect() { + ewmStart(_ewm); return ETHEREUM_BOOLEAN_IS_TRUE(ewmConnect(_ewm)); } @@ -621,7 +824,7 @@ namespace Elastos { } bool EthereumEWM::addressIsValid(const std::string &address) { - return ETHEREUM_BOOLEAN_IS_TRUE(addressValidateString(address.data())); + return address.empty() || ETHEREUM_BOOLEAN_IS_TRUE(addressValidateString(address.data())); } void EthereumEWM::ensureValidAddress(const std::string &address) { @@ -635,11 +838,12 @@ namespace Elastos { })); } - void EthereumEWM::trampolineGetGasEstimate(BREthereumEWM eid, BREthereumWallet wid, BREthereumTransfer tid, + void EthereumEWM::trampolineGetGasEstimate(BREthereumEWM ewm, BREthereumWallet wid, BREthereumCookie cookie, const std::string &from, const std::string &to, - const std::string &amount, const std::string &data, int rid) { - _executor.Execute(Runnable([this, wid, tid, from, to, amount, data, rid]() -> void { - _client->getGasEstimate(wid, tid, from, to, amount, data, rid); + const std::string &amount, const std::string &gasPrice, + const std::string &data, int rid) { + _executor.Execute(Runnable([this, wid, cookie, from, to, amount, gasPrice, data, rid]() -> void { + _client->getGasEstimate(wid, cookie, from, to, amount, gasPrice, data, rid); })); } @@ -692,47 +896,46 @@ namespace Elastos { })); } - void EthereumEWM::trampolineGetNonce(BREthereumEWM eid, const std::string &address, int rid) { + void EthereumEWM::trampolineGetNonce(BREthereumEWM ewm, const std::string &address, int rid) { _executor.Execute(Runnable([this, address, rid]() -> void { _client->getNonce(address, rid); })); } - void EthereumEWM::trampolineEWMEvent(BREthereumEWM eid, int event, int status, - const std::string &errorDescription) { - _executor.Execute(Runnable([this, event, status, errorDescription]() -> void { - _client->handleEWMEvent(EWMEvent(event), Status(status), errorDescription); + void EthereumEWM::trampolineEWMEvent(BREthereumEWM ewm, const BREthereumEWMEvent &event) { + _executor.Execute(Runnable([this, event]() -> void { + _client->handleEWMEvent(event); })); } - void EthereumEWM::trampolinePeerEvent(BREthereumEWM eid, int event, int status, - const std::string &errorDescription) { - _executor.Execute(Runnable([this, event, status, errorDescription]() -> void { - _client->handlePeerEvent(PeerEvent(event), Status(status), errorDescription); + void EthereumEWM::trampolinePeerEvent(BREthereumEWM ewm, const BREthereumPeerEvent &event) { + _executor.Execute(Runnable([this, event]() -> void { + _client->handlePeerEvent(event); })); } - void EthereumEWM::trampolineWalletEvent(BREthereumEWM eid, BREthereumWallet wid, int event, int status, - const std::string &errorDescription) { + void EthereumEWM::trampolineWalletEvent(BREthereumEWM eid, BREthereumWallet wid, + const BREthereumWalletEvent &event) { EthereumWalletPtr wallet = walletLookupOrCreate(wid, nullptr); - _executor.Execute(Runnable([this, wallet, event, status, errorDescription]() -> void { - _client->handleWalletEvent(wallet, WalletEvent(event), Status(status), errorDescription); + _executor.Execute(Runnable([this, wallet, event]() -> void { + _client->handleWalletEvent(wallet, event); })); } - void EthereumEWM::trampolineTokenEvent(BREthereumEWM eid, BREthereumToken tokenId, int event) { + void EthereumEWM::trampolineTokenEvent(BREthereumEWM eid, BREthereumToken tokenId, + const BREthereumTokenEvent &event) { EthereumTokenPtr token = addTokenByReference(tokenId); _executor.Execute(Runnable([this, token, event]() -> void { - _client->handleTokenEvent(token, TokenEvent(event)); + _client->handleTokenEvent(token, event); })); } void EthereumEWM::trampolineTransferEvent(BREthereumEWM eid, BREthereumWallet wid, BREthereumTransfer tid, - int event, int status, const std::string &errorDescription) { + const BREthereumTransferEvent &event) { EthereumWalletPtr wallet = walletLookupOrCreate(wid, nullptr); EthereumTransferPtr transaction = transactionLookupOrCreate(tid); - _executor.Execute(Runnable([this, wallet, transaction, event, status, errorDescription]() -> void { - _client->handleTransferEvent(wallet, transaction, TransactionEvent(event), Status(status), errorDescription); + _executor.Execute(Runnable([this, wallet, transaction, event]() -> void { + _client->handleTransferEvent(wallet, transaction, event); })); } diff --git a/SDK/Ethereum/EthereumEWM.h b/SDK/Ethereum/EthereumEWM.h index 9850d4921..71bac9e58 100644 --- a/SDK/Ethereum/EthereumEWM.h +++ b/SDK/Ethereum/EthereumEWM.h @@ -41,176 +41,52 @@ #include #include +#include namespace Elastos { namespace ElaWallet { class EthereumEWM { public: - // The LES sync mode - enum Mode { - API_ONLY, - API_WITH_P2P_SEND, - P2P_WITH_API_SYNC, - P2P_ONLY - }; - - enum Status { - SUCCESS, - // Reference access, - ERROR_UNKNOWN_NODE, - ERROR_UNKNOWN_TRANSACTION, - ERROR_UNKNOWN_ACCOUNT, - ERROR_UNKNOWN_WALLET, - ERROR_UNKNOWN_BLOCK, - ERROR_UNKNOWN_LISTENER, - - // Node - ERROR_NODE_NOT_CONNECTED, - - // Transaction - ERROR_TRANSACTION_X, - - // Acount - // Wallet - // Block - // Listener - - // Numeric - ERROR_NUMERIC_PARSE, - NUMBER_OF_STATUS_EVENTS - }; + static std::string Status2String(const BREthereumStatus &s); - // Wallet Event - enum WalletEvent { - WalletEvent_CREATED, - WalletEvent_BALANCE_UPDATED, - WalletEvent_DEFAULT_GAS_LIMIT_UPDATED, - WalletEvent_DEFAULT_GAS_PRICE_UPDATED, - WalletEvent_DELETED, - WalletEvent_NUMBER_OF_WALLET_EVENTS - }; + static nlohmann::json WalletEvent2Json(const BREthereumWalletEvent &e); - // Token Event - enum TokenEvent { - TokenEvent_CREATED, - TokenEvent_DELETED, - TokenEvent_NUMBER_OF_TOKEN_EVENTS - }; + static nlohmann::json TokenEvent2Json(const BREthereumTokenEvent &e); - // Block Event - enum BlockEvent { - BlockEvent_CREATED, - BlockEvent_CHAINED, - BlockEvent_ORPHANED, - BlockEvent_DELETED, - BlockEvent_NUMBER_OF_BLOCK_EVENT - }; + static nlohmann::json TransferEvent2Json(const BREthereumTransferEvent &e); - // Transaction Event - enum TransactionEvent { - TransactionEvent_CREATED, - TransactionEvent_SIGNED, - TransactionEvent_SUBMITTED, - TransactionEvent_INCLUDED, // aka confirmed - TransactionEvent_ERRORED, - TransactionEvent_GAS_ESTIMATE_UPDATED, - TransactionEvent_BLOCK_CONFIRMATIONS_UPDATED, - TransactionEvent_DELETED, - TransactionEvent_NUMBER_OF_TRANSACTION_EVENTS - }; + static nlohmann::json EWMEvent2Json(const BREthereumEWMEvent &e); - // EWM Event - enum EWMEvent { - EWMEvent_CREATED, - EWMEvent_SYNC_STARTED, - EWMEvent_SYNC_CONTINUES, - EWMEvent_SYNC_STOPPED, - EWMEvent_NETWORK_UNAVAILABLE, - EWMEvent_DELETED, - EWMEvent_NUMBER_OF_EWM_EVENTS - }; + static nlohmann::json PeerEvent2Json(const BREthereumPeerEvent &e); - // Peer Event - enum PeerEvent { - PeerEvent_CREATED, - PeerEvent_DELETED, - PeerEvent_NUMBER_OF_PEER_EVENTS - }; - - public: - static std::string Status2String(Status s); - - static std::string WalletEvent2String(WalletEvent e); - - static std::string TokenEvent2String(TokenEvent e); - - static std::string BlockEvent2String(BlockEvent e); - - static std::string TransactionEvent2String(TransactionEvent e); - - static std::string EWMEvent2String(EWMEvent e); - - static std::string PeerEvent2String(PeerEvent e); + static std::string EWMState2String(const BREthereumEWMState &s); public: // Client class Client { public: - // typedef void (*BREthereumClientHandlerGetGasPrice) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // int rid); virtual void getGasPrice(BREthereumWallet wid, int rid) = 0; - // typedef void (*BREthereumClientHandlerEstimateGas) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // BREthereumTransactionId tid, - // const char *from, - // const char *to, - // const char *amount, - // const char *data, - // int rid); virtual void getGasEstimate(BREthereumWallet wid, - BREthereumTransfer tid, + BREthereumCookie cookie, const std::string &from, const std::string &to, const std::string &amount, + const std::string &gasPrice, const std::string &data, int rid) = 0; - // typedef void (*BREthereumClientHandlerGetBalance) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // const char *address, - // int rid); virtual void getBalance(BREthereumWallet wid, const std::string &address, int rid) = 0; - // typedef void (*BREthereumClientHandlerSubmitTransaction) (BREthereumClientContext context, - // BREthereumEWM ewm, - // BREthereumWalletId wid, - // BREthereumTransactionId tid, - // const char *transaction, - // int rid); virtual void submitTransaction(BREthereumWallet wid, BREthereumTransfer tid, const std::string &rawTransaction, int rid) = 0; - // &:wtypedef void (*BREthereumClientHandlerGetTransactions) (BREthereumClientContext context, - // BREthereumEWM ewm, - // const char *address, - // int rid); virtual void getTransactions(const std::string &address, uint64_t begBlockNumber, uint64_t endBlockNumber, int rid) = 0; - // typedef void (*BREthereumClientHandlerGetLogs) (BREthereumClientContext context, - // BREthereumEWM ewm, - // const char *contract, - // const char *address, - // const char *event, - // int rid); virtual void getLogs(const std::string &contract, const std::string &address, const std::string &event, @@ -219,14 +95,6 @@ namespace Elastos { int rid) = 0; - //typedef void - //(*BREthereumClientHandlerGetBlocks) (BREthereumClientContext context, - // BREthereumEWM ewm, - // const char *address, - // BREthereumSyncInterestSet interests, - // uint64_t blockNumberStart, - // uint64_t blockNumberStop, - // int rid); virtual void getBlocks(const std::string &address, int interests, uint64_t blockNumberStart, @@ -235,37 +103,22 @@ namespace Elastos { virtual void getTokens(int rid) = 0; - // typedef void (*BREthereumClientHandlerGetBlockNumber) (BREthereumClientContext context, - // BREthereumEWM ewm, - // int rid); virtual void getBlockNumber(int rid) = 0; - // typedef void (*BREthereumClientHandlerGetNonce) (BREthereumClientContext context, - // BREthereumEWM ewm, - // const char *address, - // int rid); virtual void getNonce(const std::string &address, int rid) = 0; - virtual void handleEWMEvent(EWMEvent event, Status status, - const std::string &errorDescription) = 0; + virtual void handleEWMEvent(const BREthereumEWMEvent &event) = 0; - virtual void handlePeerEvent(PeerEvent event, Status status, - const std::string &errorDescription) = 0; + virtual void handlePeerEvent(const BREthereumPeerEvent &event) = 0; virtual void handleWalletEvent(const EthereumWalletPtr &wallet, - WalletEvent event, Status status, - const std::string &errorDescription) = 0; - - virtual void handleTokenEvent(const EthereumTokenPtr &token, TokenEvent event) = 0; + const BREthereumWalletEvent &event) = 0; - virtual void handleBlockEvent(const EthereumBlockPtr &block, - BlockEvent event, Status status, - const std::string &errorDescription) = 0; + virtual void handleTokenEvent(const EthereumTokenPtr &token, const BREthereumTokenEvent &event) = 0; virtual void handleTransferEvent(const EthereumWalletPtr &wallet, const EthereumTransferPtr &transaction, - TransactionEvent event, Status status, - const std::string &errorDescription) = 0; + const BREthereumTransferEvent &event) = 0; }; public: @@ -274,10 +127,16 @@ namespace Elastos { void announceGasPrice(BREthereumWallet wid, const std::string &gasPrice, int rid); - void announceGasEstimate(BREthereumWallet wid, - BREthereumTransfer tid, - const std::string &gasEstimate, - int rid); + void announceGasEstimateSuccess(BREthereumWallet wid, + BREthereumCookie cookie, + const std::string &gasEstimate, + const std::string &gasPrice, + int rid); + + void announceGasEstimateFailure(BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status, + int rid); void announceSubmitTransaction(BREthereumWallet wid, BREthereumTransfer tid, @@ -327,14 +186,16 @@ namespace Elastos { void announceNonce(const std::string &address, const std::string &nonce, int rid); + void announceBlocks(int id, std::vector blockNumbers); + void announceToken(const std::string &address, + int rid, const std::string &symbol, const std::string &name, const std::string &description, int decimals, const std::string &defaultGasLimit, - const std::string &defaultGasPrice, - int rid); + const std::string &defaultGasPrice); void announceTokenComplete(int rid, bool success); @@ -352,6 +213,8 @@ namespace Elastos { EthereumWalletPtr walletLookupOrCreate(BREthereumWallet wid, const EthereumTokenPtr &token); public: + std::vector getWallets(); + EthereumWalletPtr getWallet(); EthereumWalletPtr getWallet(const EthereumTokenPtr &token); @@ -362,6 +225,9 @@ namespace Elastos { protected: EthereumTransferPtr transactionLookupOrCreate(BREthereumTransfer tid); + public: + void transferDelete(const EthereumTransferPtr &transfer); + // Block protected: EthereumBlockPtr blockLookupOrCreate(BREthereumBlock bid); @@ -385,25 +251,35 @@ namespace Elastos { // Constructor public: - EthereumEWM(Client *client, Mode mode, const EthereumNetworkPtr &network, + EthereumEWM(Client *client, BRCryptoSyncMode mode, const EthereumNetworkPtr &network, const std::string &storagePath, const std::string &paperKey, - const std::vector &wordList); + const std::vector &wordList, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); - EthereumEWM(Client *client, Mode mode, const EthereumNetworkPtr &network, - const std::string &storagePath, const bytes_t &publicKey); + EthereumEWM(Client *client, BRCryptoSyncMode mode, const EthereumNetworkPtr &network, + const std::string &storagePath, const bytes_t &publicKey, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); private: EthereumEWM(BREthereumEWM identifier, Client *client, const EthereumNetworkPtr &network); - BREthereumEWM createRawEWM(Mode mode, BREthereumNetwork network, - const std::string &storagePath, BREthereumAccount account); + BREthereumEWM createRawEWM(BRCryptoSyncMode mode, BREthereumNetwork network, + const std::string &storagePath, BREthereumAccount account, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); - BREthereumEWM createRawEWM(Mode mode, BREthereumNetwork network, + BREthereumEWM createRawEWM(BRCryptoSyncMode mode, BREthereumNetwork network, const std::string &storagePath, const std::string &paperKey, - const std::vector &wordList); + const std::vector &wordList, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); - BREthereumEWM createRawEWMPublicKey(Mode mode, BREthereumNetwork network, - const std::string &storagePath, const bytes_t &pubkey); + BREthereumEWM createRawEWMPublicKey(BRCryptoSyncMode mode, BREthereumNetwork network, + const std::string &storagePath, const bytes_t &pubkey, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); // Connect / Disconnect public: @@ -423,9 +299,10 @@ namespace Elastos { // void trampolineGetGasPrice(BREthereumEWM eid, BREthereumWallet wid, int rid); - void trampolineGetGasEstimate(BREthereumEWM eid, BREthereumWallet wid, BREthereumTransfer tid, + void trampolineGetGasEstimate(BREthereumEWM ewm, BREthereumWallet wid, BREthereumCookie cookie, const std::string &from, const std::string &to, - const std::string &amount, const std::string &data, int rid); + const std::string &amount, const std::string &gasPrice, + const std::string &data, int rid); void trampolineGetBalance(BREthereumEWM eid, BREthereumWallet wid, const std::string &address, int rid); @@ -445,19 +322,18 @@ namespace Elastos { void trampolineGetBlockNumber(BREthereumEWM eid, int rid); - void trampolineGetNonce(BREthereumEWM eid, const std::string &address, int rid); + void trampolineGetNonce(BREthereumEWM ewm, const std::string &address, int rid); - void trampolineEWMEvent(BREthereumEWM eid, int event, int status, const std::string &errorDescription); + void trampolineEWMEvent(BREthereumEWM ewm, const BREthereumEWMEvent &event); - void trampolinePeerEvent(BREthereumEWM eid, int event, int status, const std::string &errorDescription); + void trampolinePeerEvent(BREthereumEWM ewm, const BREthereumPeerEvent &event); - void trampolineWalletEvent(BREthereumEWM eid, BREthereumWallet wid, int event, int status, - const std::string &errorDescription); + void trampolineWalletEvent(BREthereumEWM eid, BREthereumWallet wid, const BREthereumWalletEvent &event); - void trampolineTokenEvent(BREthereumEWM eid, BREthereumToken tokenId, int event); + void trampolineTokenEvent(BREthereumEWM eid, BREthereumToken tokenId, const BREthereumTokenEvent &event); - void trampolineTransferEvent(BREthereumEWM eid, BREthereumWallet wid, BREthereumTransfer tid, int event, - int status, const std::string &errorDescription); + void trampolineTransferEvent(BREthereumEWM eid, BREthereumWallet wid, BREthereumTransfer tid, + const BREthereumTransferEvent &event); public: BREthereumEWM getRaw() const; diff --git a/SDK/Ethereum/EthereumToken.cpp b/SDK/Ethereum/EthereumToken.cpp index 0d8f54386..db85e4f31 100644 --- a/SDK/Ethereum/EthereumToken.cpp +++ b/SDK/Ethereum/EthereumToken.cpp @@ -81,21 +81,5 @@ namespace Elastos { return _token; } - std::vector EthereumToken::getTokenAll() const { - std::vector allTokens; - int count = tokenCount(); - // A uint32_t array on x86 platforms - we *require* a long array - BREthereumToken *tokens = tokenGetAll(); - - for (int i = 0; i < count; ++i) { - EthereumTokenPtr token(new EthereumToken(tokens[i])); - allTokens.push_back(token); - } - - free(tokens); - - return allTokens; - } - } } \ No newline at end of file diff --git a/SDK/Ethereum/EthereumToken.h b/SDK/Ethereum/EthereumToken.h index ffc49b098..cebe8c519 100644 --- a/SDK/Ethereum/EthereumToken.h +++ b/SDK/Ethereum/EthereumToken.h @@ -62,9 +62,6 @@ namespace Elastos { BREthereumToken getRaw() const; - protected: - std::vector getTokenAll() const; - private: BREthereumToken _token; }; diff --git a/SDK/Ethereum/EthereumTransfer.cpp b/SDK/Ethereum/EthereumTransfer.cpp index 336433b44..fb18f66a4 100644 --- a/SDK/Ethereum/EthereumTransfer.cpp +++ b/SDK/Ethereum/EthereumTransfer.cpp @@ -55,11 +55,11 @@ namespace Elastos { std::string EthereumTransfer::getSourceAddress() const { BREthereumAddress source = ewmTransferGetSource(_ewm->getRaw(), getRaw()); - return GetCString(addressGetEncodedString(source, 1)); + return GetCString(addressGetEncodedString(&source, 1)); } std::string EthereumTransfer::getTargetAddress() const { - BREthereumAddress target = ewmTransferGetTarget(_ewm->getRaw(), getRaw()); + BREthereumAddress *target = ewmTransferGetTarget(_ewm->getRaw(), getRaw()); return GetCString(addressGetEncodedString(target, 1)); } @@ -96,11 +96,10 @@ namespace Elastos { } std::string EthereumTransfer::getFee() const { - return getFee(EthereumAmount::Unit::ETHER_GWEI); + return getFee(EthereumAmount::Unit::ETHER_WEI); } std::string EthereumTransfer::getFee(EthereumAmount::Unit unit) const { - assert(!EthereumAmount::isTokenUnit(unit)); int overflow = 0; BREthereumEther fee = ewmTransferGetFee(_ewm->getRaw(), getRaw(), &overflow); std::string feeString; @@ -112,11 +111,10 @@ namespace Elastos { } std::string EthereumTransfer::getGasPrice() const { - return getGasPrice(EthereumAmount::Unit::ETHER_GWEI); + return getGasPrice(EthereumAmount::Unit::ETHER_WEI); } std::string EthereumTransfer::getGasPrice(EthereumAmount::Unit unit) const { - assert(!EthereumAmount::isTokenUnit(unit)); BREthereumGasPrice price = ewmTransferGetGasPrice(_ewm->getRaw(), getRaw(), (BREthereumEtherUnit) unit); return GetCString(ewmCoerceEtherAmountToString(_ewm->getRaw(), diff --git a/SDK/Ethereum/EthereumWallet.cpp b/SDK/Ethereum/EthereumWallet.cpp index dac8466ff..7fe9e1794 100644 --- a/SDK/Ethereum/EthereumWallet.cpp +++ b/SDK/Ethereum/EthereumWallet.cpp @@ -26,6 +26,7 @@ #include "EthereumWallet.h" #include "EthereumEWM.h" +#include namespace Elastos { namespace ElaWallet { @@ -127,7 +128,7 @@ namespace Elastos { } void EthereumWallet::estimateGas(const EthereumTransferPtr &transaction) { - ewmUpdateGasEstimate(_ewm->getRaw(), getRaw(), transaction->getRaw()); +// ewmUpdateGasEstimate(_ewm->getRaw(), getRaw(), transaction->getRaw()); } // Transactions @@ -178,7 +179,6 @@ namespace Elastos { const std::string &gasLimit, const std::string &data) const { _ewm->ensureValidAddress(targetAddress); - assert(!EthereumAmount::isTokenUnit(amountUnit) && !EthereumAmount::isTokenUnit(gasPriceUnit)); BREthereumTransfer transfer = createRawTransactionGeneric(targetAddress, amount, amountUnit, gasPrice, @@ -217,6 +217,7 @@ namespace Elastos { ? amountCreateEtherString(amount.data(), (BREthereumEtherUnit) unit, &status) : amountCreateTokenQuantityString(token, amount.data(), (BREthereumTokenQuantityUnit) unit, &status); + ErrorChecker::CheckParam(status != CORE_PARSE_OK, Error::InvalidArgument, "invalid amount"); return ewmWalletCreateTransfer(node, wallet, targetAddress.data(), a); } @@ -232,9 +233,12 @@ namespace Elastos { // Get an actual Amount BREthereumEther brAmount = etherCreateString(amount.data(), (BREthereumEtherUnit) amountUnit, &status); + ErrorChecker::CheckParam(status != CORE_PARSE_OK, Error::InvalidArgument, "invalid amount"); - BREthereumGasPrice brGasPrice = gasPriceCreate( - etherCreateString(gasPrice.data(), (BREthereumEtherUnit) gasPriceUnit, &status)); + BREthereumEther gasPriceEther = etherCreateString(gasPrice.data(), (BREthereumEtherUnit) gasPriceUnit, &status); + ErrorChecker::CheckParam(status != CORE_PARSE_OK, Error::InvalidArgument, "invalid gasPrice"); + + BREthereumGasPrice brGasPrice = gasPriceCreate(gasPriceEther); BREthereumGas brGasLimit = gasCreate(strtoull(gasLimit.data(), NULL, 0)); @@ -269,6 +273,7 @@ namespace Elastos { EthereumTransferPtr t(new EthereumTransfer(_ewm, transactionIds[i], _defaultUnit)); transactions.push_back(t); } + free(transactionIds); return transactions; } diff --git a/SDK/Ethereum/Reference.cpp b/SDK/Ethereum/Reference.cpp index d0e3fe621..46fa92db0 100644 --- a/SDK/Ethereum/Reference.cpp +++ b/SDK/Ethereum/Reference.cpp @@ -33,6 +33,9 @@ namespace Elastos { } std::string Reference::GetCString(char *data) const { + if (data == NULL) + return ""; + std::string result = data; free(data); data = NULL; diff --git a/SDK/Implement/EthSidechainSubWallet.cpp b/SDK/Implement/EthSidechainSubWallet.cpp index 6e76fb67c..cba98437a 100644 --- a/SDK/Implement/EthSidechainSubWallet.cpp +++ b/SDK/Implement/EthSidechainSubWallet.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace Elastos { namespace ElaWallet { @@ -36,6 +37,157 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; EthSidechainSubWallet::~EthSidechainSubWallet() { } + nlohmann::json EthSidechainSubWallet::CreateTransfer(const std::string &targetAddress, + const std::string &amount, + EthereumAmountUnit amountUnit) const { + ArgInfo("{} {}", _walletID, GetFunName()); + ArgInfo("target: {}", targetAddress); + ArgInfo("amount: {}", amount); + ArgInfo("amountUnit: {}", amountUnit); + + if (amountUnit != TOKEN_DECIMAL && + amountUnit != TOKEN_INTEGER && + amountUnit != ETHER_WEI && + amountUnit != ETHER_GWEI && + amountUnit != ETHER_ETHER) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid amount unit"); + } + + EthereumAmount::Unit unit = EthereumAmount::Unit(amountUnit); + nlohmann::json j; + EthereumTransferPtr tx = _client->_ewm->getWallet()->createTransfer(targetAddress, amount, unit); + + j["ID"] = GetTransferID(tx); + j["Fee"] = tx->getFee(unit); + + ArgInfo("r => {}", j.dump()); + + return j; + } + + nlohmann::json EthSidechainSubWallet::CreateTransferGeneric(const std::string &targetAddress, + const std::string &amount, + EthereumAmountUnit amountUnit, + const std::string &gasPrice, + EthereumAmountUnit gasPriceUnit, + const std::string &gasLimit, + const std::string &data) const { + ArgInfo("{} {}", _walletID, GetFunName()); + ArgInfo("target: {}", targetAddress); + ArgInfo("amount: {}", amount); + ArgInfo("amountUnit: {}", amountUnit); + ArgInfo("gasPrice: {}", gasPrice); + ArgInfo("gasPriceUnit: {}", gasPriceUnit); + ArgInfo("gasLimit: {}", gasLimit); + ArgInfo("data: {}", data); + + if (amountUnit != TOKEN_DECIMAL && + amountUnit != TOKEN_INTEGER && + amountUnit != ETHER_WEI && + amountUnit != ETHER_GWEI && + amountUnit != ETHER_ETHER) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid amount unit"); + } + + EthereumAmount::Unit unit = EthereumAmount::Unit(amountUnit); + EthereumAmount::Unit gasUnit = EthereumAmount::Unit(gasPriceUnit); + nlohmann::json j; + EthereumTransferPtr tx = _client->_ewm->getWallet()->createTransferGeneric(targetAddress, + amount, + unit, + gasPrice, + gasUnit, + gasLimit, + data); + + j["ID"] = GetTransferID(tx); + j["Fee"] = tx->getFee(unit); + + ArgInfo("r => {}", j.dump()); + + return j; + } + + void EthSidechainSubWallet::DeleteTransfer(const nlohmann::json &tx) { + ArgInfo("{} {}", _walletID, GetFunName()); + ArgInfo("tx: {}", tx.dump()); + + std::string tid; + EthereumTransferPtr transfer; + if (tx.find("ID") == tx.end()) + ErrorChecker::ThrowParamException(Error::InvalidArgument, "'ID' not found in json"); + + try { + tid = tx["ID"].get(); + transfer = LookupTransfer(tid); + } catch (const std::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "get 'ID' of json failed"); + } + + ErrorChecker::CheckParam(transfer == nullptr, Error::InvalidArgument, "transfer " + tid + " not found"); + + _client->_ewm->transferDelete(transfer); + } + + nlohmann::json EthSidechainSubWallet::GetTokenTransactions(uint32_t start, uint32_t count, + const std::string &txid, + const std::string &tokenSymbol) const { + ArgInfo("{} {}", _walletID, GetFunName()); + ArgInfo("start: {}", start); + ArgInfo("count: {}", count); + ArgInfo("txid: {}", txid); + ArgInfo("tokenSymbol: {}", tokenSymbol); + + std::vector wallets = _client->_ewm->getWallets(); + nlohmann::json j; + nlohmann::json txList = nlohmann::json::array(); + size_t maxCount = 0; + + for (const auto &w : wallets) { + EthereumTokenPtr token = w->getToken(); + if (token && (token->getSymbol() == tokenSymbol)) { + std::vector transfers = w->getTransfers(); + std::reverse(transfers.begin(), transfers.end()); + maxCount = transfers.size(); + for (size_t i = start; i < transfers.size() && i - start < count; ++i) { + const EthereumTransferPtr &transfer = transfers[i]; + std::string transferID = GetTransferID(transfer); + if (txid.empty() || txid == transferID || txid == transfer->getIdentifier()) { + nlohmann::json jtx; + jtx["ID"] = transferID; + jtx["IsConfirmed"] = transfer->isConfirmed(); + jtx["IsSubmitted"] = transfer->isSubmitted(); + jtx["IsErrored"] = transfer->isErrored(); + jtx["ErrorDesc"] = transfer->isErrored() ? transfer->getErrorDescription() : ""; + jtx["Hash"] = transfer->getIdentifier(); + jtx["OrigTxHash"] = transfer->getOriginationTransactionHash(); + jtx["Amount"] = transfer->getAmount(EthereumAmount::ETHER_WEI); + jtx["Timestamp"] = transfer->getBlockTimestamp(); + jtx["Fee"] = transfer->getFee(EthereumAmount::ETHER_WEI); + jtx["Confirmations"] = transfer->getBlockConfirmations(); + jtx["GasPrice"] = transfer->getGasPrice(); + jtx["GasLimit"] = transfer->getGasLimit(); + jtx["GasUsed"] = transfer->getGasUsed(); + jtx["BlockNumber"] = transfer->getBlockNumber(); + jtx["SourceAddress"] = transfer->getSourceAddress(); + jtx["TargetAddress"] = transfer->getTargetAddress(); + jtx["Nonce"] = transfer->getNonce(); + txList.push_back(jtx); + + if (!txid.empty()) + break; + } + } + } + } + + j["MaxCount"] = maxCount; + j["Transactions"] = txList; + + ArgInfo("r => {}", j.dump()); + return j; + } + EthSidechainSubWallet::EthSidechainSubWallet(const CoinInfoPtr &info, const ChainConfigPtr &config, MasterWallet *parent, @@ -85,41 +237,126 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; void EthSidechainSubWallet::getGasPrice(BREthereumWallet wid, int rid) { nlohmann::json j; - j["Rid"] = rid; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->GasPrice(rid); + ArgInfo("r => {}", r.dump(4)); + + if (!r.empty()) { + int id; + std::string gasPrice; + + try { + id = r["id"].get(); + gasPrice = r["result"].get(); + _client->_ewm->announceGasPrice(wid, gasPrice, id); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + } + } + } } void EthSidechainSubWallet::getGasEstimate(BREthereumWallet wid, - BREthereumTransfer tid, + BREthereumCookie cookie, const std::string &from, const std::string &to, const std::string &amount, + const std::string &gasPrice, const std::string &data, int rid) { nlohmann::json j; - j["From"] = from; - j["To"] = to; - j["Amount"] = amount; - j["Data"] = data; - j["Rid"] = rid; + + j["from"] = from; + j["to"] = to; + j["amount"] = amount; + j["data"] = data; + j["gasPrice"] = gasPrice; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->EstimateGas(from, to, amount, gasPrice, data, rid); + ArgInfo("r => {}", r.dump(4)); + + if (!r.empty()) { + int id; + std::string gasEstimate; + + try { + id = r["id"].get(); + gasEstimate = r["result"].get(); + _client->_ewm->announceGasEstimateSuccess(wid, cookie, gasEstimate, gasPrice, id); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + _client->_ewm->announceGasEstimateFailure(wid, cookie, ERROR_NODE_NOT_CONNECTED, id); + } + } else { + _client->_ewm->announceGasEstimateFailure(wid, cookie, ERROR_NODE_NOT_CONNECTED, rid); + } + } } void EthSidechainSubWallet::getBalance(BREthereumWallet wid, const std::string &address, int rid) { nlohmann::json j; - j["Address"] = address; - j["Rid"] = rid; + j["address"] = address; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->GetBalance(address, rid); + ArgInfo("r => {}", r.dump(4)); + + if (!r.empty()) { + int id; + std::string balance; + + try { + id = r["id"].get(); + balance = r["result"].get(); + _client->_ewm->announceBalance(wid, balance, id); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + } + } + } } void EthSidechainSubWallet::submitTransaction(BREthereumWallet wid, BREthereumTransfer tid, - const std::string &rawTransaction, + const std::string &tx, int rid) { nlohmann::json j; - j["RawTx"] = rawTransaction; - j["Rid"] = rid; + j["tx"] = tx; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->SubmitTransaction(tx, rid); + ArgInfo("r => {}", r.dump(4)); + + if (!r.empty()) { + int id = rid; + std::string hash; + + try { + id = r["id"].get(); + hash = r["result"].get(); + _client->_ewm->announceSubmitTransaction(wid, tid, hash, -1, "", id); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + _client->_ewm->announceSubmitTransaction(wid, tid, "", 0, "parse result failure", id); + } + } else { + _client->_ewm->announceSubmitTransaction(wid, tid, "", 0, "unknown failure", rid); + } + } } void EthSidechainSubWallet::getTransactions(const std::string &address, @@ -127,11 +364,58 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; uint64_t endBlockNumber, int rid) { nlohmann::json j; - j["Address"] = address; - j["BegBlockNumber"] = begBlockNumber; - j["EndBlockNumber"] = endBlockNumber; - j["Rid"] = rid; + j["address"] = address; + j["begBlockNumber"] = begBlockNumber; + j["endBlockNumber"] = endBlockNumber; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->GetTransactions(address, begBlockNumber, endBlockNumber, rid); + ArgInfo("r => {}", r.dump(4)); + + if (!r.empty()) { + int id = rid; + std::string hash, from, to, contract, amount, gasLimit, gasPrice, data, nonce, gasUsed, isError; + std::string blockNumber, blockHash, blockConfirmations, blockTransactionIndex, blockTimestamp; + + try { + id = r["id"].get(); + nlohmann::json txns = r["result"]; + for (nlohmann::json::iterator it = txns.begin(); it != txns.end(); ++it) { + nlohmann::json tx = *it; + hash = tx["hash"].get(); + from = tx["from"].get(); + if (tx["to"].is_null()) + to.clear(); + else + to = tx["to"].get(); + contract = tx["contract"].get(); + amount = tx["amount"].get(); + gasLimit = tx["gasLimit"].get(); + gasPrice = tx["gasPrice"].get(); + data = tx["data"].get(); + nonce = tx["nonce"].get(); + gasUsed = tx["gasUsed"].get(); + blockNumber = tx["blockNumber"].get(); + blockHash = tx["blockHash"].get(); + blockConfirmations = tx["blockConfirmations"].get(); + blockTransactionIndex = tx["blockTransactionIndex"].get(); + blockTimestamp = tx["blockTimestamp"].get(); + isError = tx["isError"].get(); + + _client->_ewm->announceTransaction(id, hash, from, to, contract, amount, gasLimit, gasPrice, data, nonce, gasUsed, blockNumber, blockHash, blockConfirmations, blockTransactionIndex, blockTimestamp, isError); + } + _client->_ewm->announceTransactionComplete(id, true); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + _client->_ewm->announceTransactionComplete(id, false); + } + } else { + _client->_ewm->announceTransactionComplete(rid, false); + } + } } void EthSidechainSubWallet::getLogs(const std::string &contract, @@ -141,13 +425,51 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; uint64_t endBlockNumber, int rid) { nlohmann::json j; - j["Contract"] = contract; - j["Address"] = address; - j["Event"] = event; - j["BegBlockNumber"] = begBlockNumber; - j["EndBlockNumber"] = endBlockNumber; - j["Rid"] = rid; + j["contract"] = contract; + j["address"] = address; + j["event"] = event; + j["begBlockNumber"] = begBlockNumber; + j["endBlockNumber"] = endBlockNumber; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->GetLogs(contract, address, event, begBlockNumber, endBlockNumber, rid); + ArgInfo("r => {}", r.dump(4)); + + if (!r.empty()) { + int id = rid; + std::string hash, c, data, gasPrice, gasUsed, logIndex, blockNumber, blockTransactionIndex, blockTimestamp; + std::vector topics; + + try { + id = r["id"].get(); + nlohmann::json logs = r["result"]; + for (nlohmann::json::iterator it = logs.begin(); it != logs.end(); ++it) { + nlohmann::json log = *it; + hash = log["hash"].get(); + c = log["contract"].get(); + topics = log["topics"].get>(); + data = log["data"].get(); + gasPrice = log["gasPrice"].get(); + gasUsed = log["gasUsed"].get(); + logIndex = log["logIndex"].get(); + blockNumber = log["blockNumber"].get(); + blockTransactionIndex = log["blockTransactionIndex"].get(); + blockTimestamp = log["blockTimestamp"].get(); + + _client->_ewm->announceLog(id, hash, c, topics, data, gasPrice, gasUsed, logIndex, blockNumber, blockTransactionIndex, blockTimestamp); + } + _client->_ewm->announceLogComplete(id, true); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + _client->_ewm->announceLogComplete(id, false); + } + } else { + _client->_ewm->announceLogComplete(rid, false); + } + } } void EthSidechainSubWallet::getBlocks(const std::string &address, @@ -156,57 +478,213 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; uint64_t blockNumberStop, int rid) { nlohmann::json j; - j["Address"] = address; - j["Interests"] = interests; - j["BlockNumberStart"] = blockNumberStart; - j["BlockNumberStop"] = blockNumberStop; - j["Rid"] = rid; + j["address"] = address; + j["interests"] = interests; + j["blockNumberStart"] = blockNumberStart; + j["blockNumberStop"] = blockNumberStop; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + int id = rid; + std::set numberSet; + nlohmann::json r = _callback->GetTransactions(address, blockNumberStart, blockNumberStop, rid); + ArgInfo("getTransactions => {}", r.dump(4)); + + if (!r.empty()) { + std::string from, to, blockNumber; + + try { + id = r["id"].get(); + nlohmann::json txns = r["result"]; + for (nlohmann::json::iterator it = txns.begin(); it != txns.end(); ++it) { + nlohmann::json tx = *it; + from = tx["from"].get(); + to = tx["to"].get(); + blockNumber = tx["blockNumber"].get(); + + std::string addressLower = address, fromLower = from, toLower = to; + std::transform(addressLower.begin(), addressLower.end(), addressLower.begin(), tolower); + std::transform(fromLower.begin(), fromLower.end(), fromLower.begin(), tolower); + std::transform(toLower.begin(), toLower.end(), toLower.begin(), tolower); + + bool include = (0 != (interests & (1)) && addressLower == fromLower) || + (0 != (interests & (1 << 1)) && addressLower == toLower); + if (include) { + std::stringstream ss; + uint64_t blockNum; + ss << blockNumber; + ss >> blockNum; + numberSet.insert(blockNum); + } + } + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + return; + } + } else { + Log::error("empty json"); + return; + } + + char *pEncodedAddress = eventERC20TransferEncodeAddress(eventERC20Transfer, address.c_str()); + std::string encodedAddress = pEncodedAddress; + free(pEncodedAddress); + std::string selector = eventGetSelector(eventERC20Transfer); + ArgInfo("address: {}", encodedAddress); + ArgInfo("event: {}", selector); + r = _callback->GetLogs("", encodedAddress, selector, blockNumberStart, blockNumberStop, rid); + ArgInfo("getLogs => {}", r.dump(4)); + + if (!r.empty()) { + std::string blockNumber; + std::vector topics; + + try { + id = r["id"].get(); + nlohmann::json logs = r["result"]; + for (nlohmann::json::iterator it = logs.begin(); it != logs.end(); ++it) { + nlohmann::json log = *it; + topics = log["topics"].get>(); + blockNumber = log["blockNumber"].get(); + + if (topics.size() >= 3) { + std::string addressLower = address, topicsLower1 = topics[1], topicsLower2 = topics[2]; + topicsLower1.erase(2, 24); + topicsLower2.erase(2, 24); + std::transform(addressLower.begin(), addressLower.end(), addressLower.begin(), tolower); + std::transform(topicsLower1.begin(), topicsLower1.end(), topicsLower1.begin(), tolower); + std::transform(topicsLower2.begin(), topicsLower2.end(), topicsLower2.begin(), tolower); + bool include = ((0 != (interests & (1 << 2)) && addressLower == topicsLower1) || + (0 != (interests & (1 << 3)) && addressLower == topicsLower2)); + + if (include) { + std::stringstream ss; + uint64_t blockNum = strtoull(blockNumber.c_str(), NULL, 0); + numberSet.insert(blockNum); + } + } + } + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + return; + } + } else { + Log::error("empty json"); + return; + } + + std::vector numbers(numberSet.begin(), numberSet.end()); + _client->_ewm->announceBlocks(id, numbers); + } } void EthSidechainSubWallet::getTokens(int rid) { nlohmann::json j; - j["Rid"] = rid; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->GetTokens(rid); + ArgInfo("r => {}", r.dump(4)); + + if (!r.empty()) { + int id = rid; + /* + * "address": "0x407d73d8a49eeb85d32cf465507dd71d507100c1", + * "symbol": "ELA", + * "name": "elastos", + * "description": "desc", + * "decimals": 18, + * "defaultGasLimit": "0x1388", + * "defaultGasPrice": "0x1dfd14000" // 8049999872 Wei*/ + std::string address, symbol, name, description, defaultGasLimit, defaultGasPrice; + int decimals; + + try { + id = r["id"].get(); + nlohmann::json tokens = r["result"]; + for (nlohmann::json::iterator it = tokens.begin(); it != tokens.end(); ++it) { + nlohmann::json token = *it; + address = token["address"].get(); + symbol = token["symbol"].get(); + name = token["name"].get(); + decimals = token["decimals"].get(); + if (token.contains("description")) + description = token["description"].get(); + if (token.contains("defaultGasLimit")) + defaultGasLimit = token["defaultGasLimit"].get(); + if (token.contains("defaultGasPrice")) + defaultGasPrice = token["defaultGasPrice"].get(); + + _client->_ewm->announceToken(address, id, symbol, name, description, decimals, defaultGasLimit, defaultGasPrice); + } + _client->_ewm->announceTokenComplete(id, true); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + _client->_ewm->announceTokenComplete(id, false); + } + } else { + _client->_ewm->announceTokenComplete(rid, false); + } + } } void EthSidechainSubWallet::getBlockNumber(int rid) { nlohmann::json j; - j["Rid"] = rid; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); + + boost::mutex::scoped_lock scoped_lock(lock); + if (_callback) { + nlohmann::json r = _callback->GetBlockNumber(rid); + ArgInfo("r => {}", r.dump(4)); + int id = rid; + + if (!r.empty()) { + std::string blockNumber; + + try { + id = r["id"].get(); + blockNumber = r["result"].get(); + _client->_ewm->announceBlockNumber(blockNumber, id); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + } + } + } } void EthSidechainSubWallet::getNonce(const std::string &address, int rid) { nlohmann::json j; - j["Address"] = address; - j["Rid"] = rid; + j["address"] = address; + j["rid"] = rid; ArgInfo("{} {}", GetFunName(), j.dump(4)); - } - - void EthSidechainSubWallet::handleEWMEvent(EthereumEWM::EWMEvent event, EthereumEWM::Status status, - const std::string &errorDescription) { - nlohmann::json eJson; - eJson["Type"] = "EWMEvent"; - eJson["Event"] = EthereumEWM::EWMEvent2String(event); - eJson["Status"] = EthereumEWM::Status2String(status); - eJson["ErrorDescription"] = errorDescription; - ArgInfo("{} {}", GetFunName(), eJson.dump(4)); boost::mutex::scoped_lock scoped_lock(lock); - if (_callback != nullptr) { - _callback->OnETHSCEventHandled(eJson); - } else { - Log::info(CALLBACK_IS_NULL_PROMPT); + if (_callback) { + nlohmann::json r = _callback->GetNonce(address, rid); + ArgInfo("r => {}", r.dump(4)); + int id = rid; + + if (!r.empty()) { + std::string nonce; + + try { + id = r["id"].get(); + nonce = r["result"].get(); + _client->_ewm->announceNonce(address, nonce, id); + } catch (const std::exception &e) { + Log::error("invalid json format: {}", e.what()); + } + } } } - void EthSidechainSubWallet::handlePeerEvent(EthereumEWM::PeerEvent event, EthereumEWM::Status status, - const std::string &errorDescription) { - nlohmann::json eJson; - eJson["Type"] = "PeerEvent"; - eJson["Event"] = EthereumEWM::PeerEvent2String(event); - eJson["Status"] = EthereumEWM::Status2String(status); - eJson["ErrorDescription"] = errorDescription; + void EthSidechainSubWallet::handleEWMEvent(const BREthereumEWMEvent &event) { + nlohmann::json eJson = EthereumEWM::EWMEvent2Json(event); ArgInfo("{} {}", GetFunName(), eJson.dump(4)); boost::mutex::scoped_lock scoped_lock(lock); @@ -217,16 +695,8 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; } } - void EthSidechainSubWallet::handleWalletEvent(const EthereumWalletPtr &wallet, - EthereumEWM::WalletEvent event, - EthereumEWM::Status status, - const std::string &errorDescription) { - nlohmann::json eJson; - eJson["Type"] = "WalletEvent"; - eJson["Event"] = EthereumEWM::WalletEvent2String(event); - eJson["Status"] = EthereumEWM::Status2String(status); - eJson["ErrorDescription"] = errorDescription; - eJson["WalletSymbol"] = wallet->getSymbol(); + void EthSidechainSubWallet::handlePeerEvent(const BREthereumPeerEvent &event) { + nlohmann::json eJson = EthereumEWM::PeerEvent2Json(event);; ArgInfo("{} {}", GetFunName(), eJson.dump(4)); boost::mutex::scoped_lock scoped_lock(lock); @@ -237,11 +707,10 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; } } - void EthSidechainSubWallet::handleTokenEvent(const EthereumTokenPtr &token, EthereumEWM::TokenEvent event) { - nlohmann::json eJson; - eJson["Type"] = "TokenEvent"; - eJson["Event"] = EthereumEWM::TokenEvent2String(event); - eJson["WalletSymbol"] = token->getSymbol(); + void EthSidechainSubWallet::handleWalletEvent(const EthereumWalletPtr &wallet, + const BREthereumWalletEvent &event) { + nlohmann::json eJson = EthereumEWM::WalletEvent2Json(event); + eJson["WalletSymbol"] = wallet->getSymbol(); ArgInfo("{} {}", GetFunName(), eJson.dump(4)); boost::mutex::scoped_lock scoped_lock(lock); @@ -252,16 +721,9 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; } } - void EthSidechainSubWallet::handleBlockEvent(const EthereumBlockPtr &block, - EthereumEWM::BlockEvent event, - EthereumEWM::Status status, - const std::string &errorDescription) { - nlohmann::json eJson; - eJson["Type"] = "BlockEvent"; - eJson["Event"] = EthereumEWM::BlockEvent2String(event); - eJson["Status"] = EthereumEWM::Status2String(status); - eJson["ErrorDescription"] = errorDescription; - eJson["BlockNumber"] = block->getBlockNumber(); + void EthSidechainSubWallet::handleTokenEvent(const EthereumTokenPtr &token, const BREthereumTokenEvent &event) { + nlohmann::json eJson = EthereumEWM::TokenEvent2Json(event); + eJson["WalletSymbol"] = token->getSymbol(); ArgInfo("{} {}", GetFunName(), eJson.dump(4)); boost::mutex::scoped_lock scoped_lock(lock); @@ -274,18 +736,57 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; void EthSidechainSubWallet::handleTransferEvent(const EthereumWalletPtr &wallet, const EthereumTransferPtr &transaction, - EthereumEWM::TransactionEvent event, - EthereumEWM::Status status, - const std::string &errorDescription) { - nlohmann::json eJson; - eJson["Type"] = "TransferEvent"; - eJson["Event"] = EthereumEWM::TransactionEvent2String(event); - eJson["Status"] = EthereumEWM::Status2String(status); - eJson["ErrorDescription"] = errorDescription; + const BREthereumTransferEvent &event) { + nlohmann::json eJson = EthereumEWM::TransferEvent2Json(event); eJson["WalletSymbol"] = wallet->getSymbol(); eJson["TxHash"] = transaction->getIdentifier(); - ArgInfo("{} {}", GetFunName(), eJson.dump(4)); + BREthereumTransfer rawTransfer = transaction->getRaw(); + BREthereumTransaction rawTx = transferGetBasisTransaction(rawTransfer); + + if (rawTx != nullptr) { + BREthereumContractFunction function = contractLookupFunctionForEncoding(contractERC20, transactionGetData(rawTx)); + if (NULL != function && functionERC20Transfer == function) { + BRCoreParseStatus status; + UInt256 funcAmount = functionERC20TransferDecodeAmount(function, transactionGetData(rawTx), &status); + char *funcAddr = functionERC20TransferDecodeAddress(function, transactionGetData(rawTx)); + char *funcAmt = coerceString(funcAmount, 10); + eJson["Token"] = transaction->getTargetAddress(); + eJson["TokenFunction"] = "ERC20Transfer"; + eJson["TokenAmount"] = funcAmt; + eJson["TokenAddress"] = funcAddr; + free(funcAmt); + free(funcAddr); + } + } else { + BREthereumLog rawLog = transferGetBasisLog(rawTransfer); + if (rawLog == nullptr) { + Log::warn("Transaction & Log is null"); + } else { + BREthereumHash logHash = logGetHash (rawLog); + char *logHashString = hashAsString(logHash); + + BREthereumAddress logAddress = logGetAddress(rawLog); + char *logAddressString = addressGetEncodedString(&logAddress, 1); + + nlohmann::json topicArray = nlohmann::json::array(); + size_t topicCount = logGetTopicsCount(rawLog); + for (size_t i = 0; i < topicCount; ++i) { + BREthereumLogTopic topic = logGetTopic(rawLog, i); + BREthereumLogTopicString topicString = logTopicAsString (topic); + topicArray.push_back(topicString.chars); + } + + eJson["LogHash"] = logHashString; + eJson["LogAddress"] = logAddressString; + eJson["LogTopics"] = topicArray; + + free(logHashString); + free(logAddressString); + } + } + + ArgInfo("{} {}", GetFunName(), eJson.dump(4)); boost::mutex::scoped_lock scoped_lock(lock); if (_callback != nullptr) { _callback->OnETHSCEventHandled(eJson); @@ -400,6 +901,9 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; void EthSidechainSubWallet::RemoveCallback() { ArgInfo("{} {}", _walletID, GetFunName()); + + boost::mutex::scoped_lock scoped_lock(lock); + _callback = nullptr; } nlohmann::json EthSidechainSubWallet::CreateTransaction(const std::string &fromAddress, @@ -412,6 +916,8 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; ArgInfo("amount: {}", amount); ArgInfo("memo: {}", memo); + ErrorChecker::ThrowParamException(Error::UnsupportOperation, "use IEthSidechainSubWallet::CreateTransfer() instead"); + nlohmann::json j; EthereumTransferPtr tx = _client->_ewm->getWallet()->createTransfer(targetAddress, amount, EthereumAmount::Unit::ETHER_ETHER); @@ -509,7 +1015,7 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; _client->_ewm->getWallet()->submit(transfer); nlohmann::json j = tx; - j["Hash"] = transfer->getOriginationTransactionHash(); + j["TxHash"] = transfer->getOriginationTransactionHash(); ArgInfo("r => {}", j.dump()); return j; @@ -538,6 +1044,8 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; count = transfers.size(); } + std::reverse(transfers.begin(), transfers.end()); + for (size_t i = start; i < transfers.size() && i - start < count; ++i) { std::string transferID = GetTransferID(transfers[i]); if (txid.empty() || txid == transferID || txid == transfers[i]->getIdentifier()) { @@ -548,9 +1056,9 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; jtx["ErrorDesc"] = transfers[i]->isErrored() ? transfers[i]->getErrorDescription() : ""; jtx["Hash"] = transfers[i]->getIdentifier(); jtx["OrigTxHash"] = transfers[i]->getOriginationTransactionHash(); - jtx["Amount"] = transfers[i]->getAmount(EthereumAmount::ETHER_ETHER); + jtx["Amount"] = transfers[i]->getAmount(EthereumAmount::ETHER_WEI); jtx["Timestamp"] = transfers[i]->getBlockTimestamp(); - jtx["Fee"] = transfers[i]->getFee(EthereumAmount::ETHER_ETHER); + jtx["Fee"] = transfers[i]->getFee(EthereumAmount::ETHER_WEI); jtx["Confirmations"] = transfers[i]->getBlockConfirmations(); jtx["GasPrice"] = transfers[i]->getGasPrice(); jtx["GasLimit"] = transfers[i]->getGasLimit(); @@ -558,6 +1066,52 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; jtx["BlockNumber"] = transfers[i]->getBlockNumber(); jtx["SourceAddress"] = transfers[i]->getSourceAddress(); jtx["TargetAddress"] = transfers[i]->getTargetAddress(); + jtx["Nonce"] = transfers[i]->getNonce(); + + BREthereumTransfer rawTransfer = transfers[i]->getRaw(); + BREthereumTransaction rawTx = transferGetBasisTransaction(rawTransfer); + + if (rawTx != NULL) { + BREthereumContractFunction function = contractLookupFunctionForEncoding(contractERC20, transactionGetData( rawTx)); + if (NULL != function && functionERC20Transfer == function) { + BRCoreParseStatus status; + UInt256 funcAmount = functionERC20TransferDecodeAmount(function, transactionGetData(rawTx), &status); + char *funcAddr = functionERC20TransferDecodeAddress(function, transactionGetData(rawTx)); + char *funcAmt = coerceString(funcAmount, 10); + jtx["Token"] = transfers[i]->getTargetAddress(); + jtx["TokenFunction"] = "ERC20Transfer"; + jtx["TokenAmount"] = funcAmt; + jtx["TokenAddress"] = funcAddr; + free(funcAmt); + free(funcAddr); + } + } else { + BREthereumLog rawLog = transferGetBasisLog(rawTransfer); + if (rawLog == nullptr) { + Log::warn("Transaction & Log is null"); + } else { + BREthereumHash logHash = logGetHash (rawLog); + char *logHashString = hashAsString(logHash); + + BREthereumAddress logAddress = logGetAddress(rawLog); + char *logAddressString = addressGetEncodedString(&logAddress, 1); + + nlohmann::json topicArray = nlohmann::json::array(); + size_t topicCount = logGetTopicsCount(rawLog); + for (size_t i = 0; i < topicCount; ++i) { + BREthereumLogTopic topic = logGetTopic(rawLog, i); + BREthereumLogTopicString topicString = logTopicAsString (topic); + topicArray.push_back(topicString.chars); + } + + jtx["LogHash"] = logHashString; + jtx["LogAddress"] = logAddressString; + jtx["LogTopics"] = topicArray; + + free(logHashString); + free(logAddressString); + } + } txList.push_back(jtx); if (!txid.empty()) @@ -591,6 +1145,16 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; return j; } + nlohmann::json EthSidechainSubWallet::GetLastBlockInfo() const { + ArgInfo("{} {}", _walletID, GetFunName()); + nlohmann::json j; + + j["BlockNumber"] = _client->_ewm->getBlockHeight(); + + ArgInfo("r => {}", j.dump()); + return j; + } + bool EthSidechainSubWallet::SetFixedPeer(const std::string &address, uint16_t port) { ArgInfo("{} {}", _walletID, GetFunName()); ArgInfo("addr: {}, port: {}", address, port); @@ -601,6 +1165,7 @@ const std::string CALLBACK_IS_NULL_PROMPT = "callback is null"; void EthSidechainSubWallet::SyncStart() { ArgInfo("{} {}", _walletID, GetFunName()); + _client->_ewm->updateTokens(); _client->_ewm->connect(); } diff --git a/SDK/Implement/EthSidechainSubWallet.h b/SDK/Implement/EthSidechainSubWallet.h index 911935603..a81b696bd 100644 --- a/SDK/Implement/EthSidechainSubWallet.h +++ b/SDK/Implement/EthSidechainSubWallet.h @@ -36,8 +36,11 @@ namespace Elastos { namespace ElaWallet { class MasterWallet; + class ChainConfig; + class CoinInfo; + class EthereumClient; typedef boost::shared_ptr ChainConfigPtr; @@ -50,15 +53,33 @@ namespace Elastos { public: // implement IEthSidechainSubWallet virtual ~EthSidechainSubWallet(); + virtual nlohmann::json CreateTransfer(const std::string &targetAddress, + const std::string &amount, + EthereumAmountUnit amountUnit) const; + + virtual nlohmann::json CreateTransferGeneric(const std::string &targetAddress, + const std::string &amount, + EthereumAmountUnit amountUnit, + const std::string &gasPrice, + EthereumAmountUnit gasPriceUnit, + const std::string &gasLimit, + const std::string &data) const; + + virtual void DeleteTransfer(const nlohmann::json &tx); + + virtual nlohmann::json GetTokenTransactions(uint32_t start, uint32_t count, const std::string &txid, + const std::string &tokenSymbol) const; + public: // implement callback of Client virtual void getGasPrice(BREthereumWallet wid, int rid); virtual void getGasEstimate(BREthereumWallet wid, - BREthereumTransfer tid, + BREthereumCookie cookie, const std::string &from, const std::string &to, const std::string &amount, + const std::string &gasPrice, const std::string &data, int rid); @@ -68,10 +89,12 @@ namespace Elastos { submitTransaction(BREthereumWallet wid, BREthereumTransfer tid, const std::string &rawTransaction, int rid); + // announce one-by-one virtual void getTransactions(const std::string &address, uint64_t begBlockNumber, uint64_t endBlockNumber, int rid); + // announce one-by-one virtual void getLogs(const std::string &contract, const std::string &address, const std::string &event, @@ -85,32 +108,25 @@ namespace Elastos { uint64_t blockNumberStop, int rid); + // announce one-by-one virtual void getTokens(int rid); virtual void getBlockNumber(int rid); virtual void getNonce(const std::string &address, int rid); - virtual void handleEWMEvent(EthereumEWM::EWMEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + virtual void handleEWMEvent(const BREthereumEWMEvent &event); - virtual void handlePeerEvent(EthereumEWM::PeerEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + virtual void handlePeerEvent(const BREthereumPeerEvent &event); virtual void handleWalletEvent(const EthereumWalletPtr &wallet, - EthereumEWM::WalletEvent event, EthereumEWM::Status status, - const std::string &errorDescription); - - virtual void handleTokenEvent(const EthereumTokenPtr &token, EthereumEWM::TokenEvent event); + const BREthereumWalletEvent &event); - virtual void handleBlockEvent(const EthereumBlockPtr &block, - EthereumEWM::BlockEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + virtual void handleTokenEvent(const EthereumTokenPtr &token, const BREthereumTokenEvent &event); virtual void handleTransferEvent(const EthereumWalletPtr &wallet, const EthereumTransferPtr &transaction, - EthereumEWM::TransactionEvent event, EthereumEWM::Status status, - const std::string &errorDescription); + const BREthereumTransferEvent &event); // implement ISubWallet public: virtual std::string GetChainID() const; @@ -179,6 +195,8 @@ namespace Elastos { virtual bool SetFixedPeer(const std::string &address, uint16_t port); + virtual nlohmann::json GetLastBlockInfo() const; + virtual void SyncStart(); virtual void SyncStop(); @@ -190,6 +208,7 @@ namespace Elastos { virtual void StopP2P(); virtual void FlushData(); + protected: friend class MasterWallet; diff --git a/SDK/Implement/IDChainSubWallet.cpp b/SDK/Implement/IDChainSubWallet.cpp index 82e363171..32f0c6d44 100644 --- a/SDK/Implement/IDChainSubWallet.cpp +++ b/SDK/Implement/IDChainSubWallet.cpp @@ -52,13 +52,18 @@ namespace Elastos { } nlohmann::json - IDChainSubWallet::CreateIDTransaction(const nlohmann::json &payloadJson, const std::string &memo) { + IDChainSubWallet::CreateIDTransaction(const nlohmann::json &payloadJson, const std::string &memo, const std::string &fee) { WalletPtr wallet = _walletManager->GetWallet(); ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); ArgInfo("payload: {}", payloadJson.dump()); ArgInfo("memo: {}", memo); + ArgInfo("fee: {}", fee); - Address receiveAddr; + BigInt userFee; + userFee.setDec(fee); + ErrorChecker::CheckParam(userFee < 0, Error::InvalidArgument, "invalid fee"); + + Address receiveAddr; PayloadPtr payload = nullptr; try { payload = PayloadPtr(new DIDInfo()); @@ -83,7 +88,7 @@ namespace Elastos { outputs.push_back(OutputPtr(new TransactionOutput(0, receiveAddr, Asset::GetELAAssetID()))); AddressPtr fromAddr(new Address()); - TransactionPtr tx = wallet->CreateTransaction(IDTransaction::didTransaction, payload, fromAddr, outputs, memo); + TransactionPtr tx = wallet->CreateTransaction(IDTransaction::didTransaction, payload, fromAddr, outputs, memo, false, userFee); nlohmann::json result; EncodeTx(result, tx); diff --git a/SDK/Implement/IDChainSubWallet.h b/SDK/Implement/IDChainSubWallet.h index 975351ed3..cc2287e08 100644 --- a/SDK/Implement/IDChainSubWallet.h +++ b/SDK/Implement/IDChainSubWallet.h @@ -37,7 +37,8 @@ namespace Elastos { virtual nlohmann::json CreateIDTransaction( const nlohmann::json &payloadJson, - const std::string &memo = ""); + const std::string &memo = "", + const std::string &fee = "10000"); virtual nlohmann::json GetAllDID(uint32_t start, uint32_t count) const; diff --git a/SDK/Implement/MainchainSubWallet.cpp b/SDK/Implement/MainchainSubWallet.cpp index c18a06367..0a4101aad 100644 --- a/SDK/Implement/MainchainSubWallet.cpp +++ b/SDK/Implement/MainchainSubWallet.cpp @@ -42,10 +42,13 @@ #include #include #include +#include #include #include #include +#include +#include namespace Elastos { namespace ElaWallet { @@ -81,6 +84,13 @@ namespace Elastos { BigInt value; value.setDec(amount); + if (sideChainID == CHAINID_IDCHAIN || sideChainID == CHAINID_TOKENCHAIN) { + Address addressValidate(sideChainAddress); + ErrorChecker::CheckParam(!addressValidate.Valid(), Error::Address, "invalid standard address"); + } else if (sideChainID == CHAINID_ESC) { + ErrorChecker::CheckParam(addressValidateString(sideChainAddress.c_str()) != ETHEREUM_BOOLEAN_TRUE, Error::Address, "invalid ethsc address"); + } + TransferInfo info(sideChainAddress, 0, value); PayloadPtr payload = PayloadPtr(new TransferCrossChainAsset({info})); @@ -965,11 +975,72 @@ namespace Elastos { return j; } + std::string MainchainSubWallet::CRCouncilMemberClaimNodeDigest(const nlohmann::json &payload) const { + WalletPtr wallet = _walletManager->GetWallet(); + ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + + + uint8_t version = CRCouncilMemberClaimNodeVersion; + CRCouncilMemberClaimNode p; + try { + p.FromJsonUnsigned(payload, version); + } catch (const std::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!p.IsValidUnsigned(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + + std::string digest = p.DigestUnsigned(version).GetHex(); + + ArgInfo("r => {}", digest); + return digest; + } + + nlohmann::json MainchainSubWallet::CreateCRCouncilMemberClaimNodeTransaction(const nlohmann::json &payload, const std::string &memo) { + WalletPtr wallet = _walletManager->GetWallet(); + ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + ArgInfo("memo: {}", memo); + + uint8_t version = CRCProposalDefaultVersion; + PayloadPtr p(new CRCouncilMemberClaimNode()); + try { + p->FromJson(payload, version); + } catch (const std::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!p->IsValid(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + OutputArray outputs; + AddressPtr receiveAddr = wallet->GetReceiveAddress(); + outputs.push_back(OutputPtr(new TransactionOutput(0, *receiveAddr))); + AddressPtr fromAddr(new Address("")); + + TransactionPtr tx = wallet->CreateTransaction(Transaction::crCouncilMemberClaimNode, p, fromAddr, outputs, memo); + + if (tx->GetOutputs().size() < 2) + ErrorChecker::ThrowLogicException(Error::BalanceNotEnough, "balance not enough"); + + tx->RemoveOutput(tx->GetOutputs().front()); + tx->FixIndex(); + + nlohmann::json result; + EncodeTx(result, tx); + ArgInfo("r => {}", result.dump()); + + return result; + } + nlohmann::json MainchainSubWallet::GetVoteInfo(const std::string &type) const { - ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); + WalletPtr wallet = _walletManager->GetWallet(); + ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); ArgInfo("type: {}", type); - WalletPtr wallet = _walletManager->GetWallet(); UTXOArray utxos = wallet->GetVoteUTXO(); nlohmann::json jinfo = nlohmann::json::array(); time_t timestamp; @@ -1032,16 +1103,22 @@ namespace Elastos { ArgInfo("payload: {}", payload.dump()); CRCProposal proposal; + uint8_t version = CRCProposalDefaultVersion; try { - proposal.FromJsonOwnerUnsigned(payload, CRCProposalDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyDraftData)) { + version = CRCProposalVersion01; + } else { + version = CRCProposalDefaultVersion; + } + proposal.FromJsonNormalOwnerUnsigned(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - ErrorChecker::CheckParam(!proposal.IsValidOwnerUnsigned(CRCProposalDefaultVersion), + ErrorChecker::CheckParam(!proposal.IsValidNormalOwnerUnsigned(version), Error::InvalidArgument, "invalid payload"); - std::string digest = proposal.DigestOwnerUnsigned(CRCProposalDefaultVersion).GetHex(); + std::string digest = proposal.DigestNormalOwnerUnsigned(version).GetHex(); ArgInfo("r => {}", digest); return digest; @@ -1052,16 +1129,22 @@ namespace Elastos { ArgInfo("payload: {}", payload.dump()); CRCProposal proposal; + uint8_t version = CRCProposalDefaultVersion; try { - proposal.FromJsonCRCouncilMemberUnsigned(payload, CRCProposalDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyDraftData)) { + version = CRCProposalVersion01; + } else { + version = CRCProposalDefaultVersion; + } + proposal.FromJsonNormalCRCouncilMemberUnsigned(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - ErrorChecker::CheckParam(!proposal.IsValidCRCouncilMemberUnsigned(CRCProposalDefaultVersion), + ErrorChecker::CheckParam(!proposal.IsValidNormalCRCouncilMemberUnsigned(version), Error::InvalidArgument, "invalid payload"); - std::string digest = proposal.DigestCRCouncilMemberUnsigned(CRCProposalDefaultVersion).GetHex(); + std::string digest = proposal.DigestNormalCRCouncilMemberUnsigned(version).GetHex(); ArgInfo("r => {}", digest); return digest; @@ -1072,16 +1155,22 @@ namespace Elastos { ArgInfo("payload: {}", payload.dump()); PayloadPtr p = PayloadPtr(new CRCProposal()); + uint8_t version = CRCProposalDefaultVersion; try { - p->FromJson(payload, 0); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyDraftData)) { + version = CRCProposalVersion01; + } else { + version = CRCProposalDefaultVersion; + } + p->FromJson(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - ErrorChecker::CheckParam(!p->IsValid(CRCProposalDefaultVersion), Error::InvalidArgument, "invalid payload"); + ErrorChecker::CheckParam(!p->IsValid(version), Error::InvalidArgument, "invalid payload"); ByteStream stream; - p->Serialize(stream, CRCProposalDefaultVersion); + p->Serialize(stream, version); uint256 hash(sha256_2(stream.GetBytes())); std::string hashString = hash.GetHex(); @@ -1098,13 +1187,19 @@ namespace Elastos { ArgInfo("memo: {}", memo); PayloadPtr p = PayloadPtr(new CRCProposal()); + uint8_t version = CRCProposalDefaultVersion; try { - p->FromJson(payload, 0); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyDraftData)) { + version = CRCProposalVersion01; + } else { + version = CRCProposalDefaultVersion; + } + p->FromJson(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - ErrorChecker::CheckParam(!p->IsValid(CRCProposalDefaultVersion), Error::InvalidArgument, "invalid payload"); + ErrorChecker::CheckParam(!p->IsValid(version), Error::InvalidArgument, "invalid payload"); AddressPtr receiveAddr = wallet->GetReceiveAddress(); OutputArray outputs; @@ -1132,17 +1227,23 @@ namespace Elastos { ArgInfo("payload: {}", payload.dump()); CRCProposalReview proposalReview; + uint8_t version = CRCProposalReviewDefaultVersion; try { - proposalReview.FromJsonUnsigned(payload, CRCProposalReviewDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyOpinionData)) { + version = CRCProposalReviewVersion01; + } else { + version = CRCProposalReviewDefaultVersion; + } + proposalReview.FromJsonUnsigned(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - if (!proposalReview.IsValidUnsigned(CRCProposalReviewDefaultVersion)) { + if (!proposalReview.IsValidUnsigned(version)) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); } - std::string digest = proposalReview.DigestUnsigned(CRCProposalReviewDefaultVersion).GetHex(); + std::string digest = proposalReview.DigestUnsigned(version).GetHex(); ArgInfo("r => {}", digest); return digest; @@ -1156,13 +1257,18 @@ namespace Elastos { ArgInfo("memo: {}", memo); PayloadPtr p = PayloadPtr(new CRCProposalReview()); + uint8_t version = CRCProposalReviewDefaultVersion; try { - p->FromJson(payload, CRCProposalReviewDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyOpinionData)) + version = CRCProposalReviewVersion01; + else + version = CRCProposalReviewDefaultVersion; + p->FromJson(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - if (!p->IsValid(CRCProposalReviewDefaultVersion)) + if (!p->IsValid(version)) ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); OutputArray outputs; @@ -1271,11 +1377,11 @@ namespace Elastos { nlohmann::json result; EncodeTx(result, tx); - std::vector dropedTypes; + std::vector droppedTypes; for(VoteContentArray::iterator it = dropedList.begin(); it != dropedList.end(); ++it) { - dropedTypes.push_back((*it).GetTypeString()); + droppedTypes.push_back((*it).GetTypeString()); } - result["DropVotes"] = dropedTypes; + result["DropVotes"] = droppedTypes; ArgInfo("r => {}", result.dump()); @@ -1286,17 +1392,22 @@ namespace Elastos { ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); ArgInfo("payload: {}", payload.dump()); + uint8_t version = CRCProposalTrackingDefaultVersion; CRCProposalTracking proposalTracking; try { - proposalTracking.FromJsonOwnerUnsigned(payload, CRCProposalTrackingDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyMessageData)) + version = CRCProposalTrackingVersion01; + else + version = CRCProposalTrackingDefaultVersion; + proposalTracking.FromJsonOwnerUnsigned(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - if (!proposalTracking.IsValidOwnerUnsigned(CRCProposalTrackingDefaultVersion)) { + if (!proposalTracking.IsValidOwnerUnsigned(version)) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); } - std::string digest = proposalTracking.DigestOwnerUnsigned(CRCProposalTrackingDefaultVersion).GetHex(); + std::string digest = proposalTracking.DigestOwnerUnsigned(version).GetHex(); ArgInfo("r => {}", digest); return digest; @@ -1306,18 +1417,23 @@ namespace Elastos { ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); ArgInfo("payload: {}", payload.dump()); + uint8_t version = CRCProposalTrackingDefaultVersion; CRCProposalTracking proposalTracking; try { - proposalTracking.FromJsonNewOwnerUnsigned(payload, CRCProposalTrackingDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyMessageData)) + version = CRCProposalTrackingVersion01; + else + version = CRCProposalTrackingDefaultVersion; + proposalTracking.FromJsonNewOwnerUnsigned(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - if (!proposalTracking.IsValidNewOwnerUnsigned(CRCProposalTrackingDefaultVersion)) { + if (!proposalTracking.IsValidNewOwnerUnsigned(version)) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); } - std::string digest = proposalTracking.DigestNewOwnerUnsigned(CRCProposalTrackingDefaultVersion).GetHex(); + std::string digest = proposalTracking.DigestNewOwnerUnsigned(version).GetHex(); ArgInfo("r => {}", digest); return digest; @@ -1327,18 +1443,23 @@ namespace Elastos { ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); ArgInfo("payload: {}", payload.dump()); + uint8_t version = CRCProposalTrackingDefaultVersion; CRCProposalTracking proposalTracking; try { - proposalTracking.FromJsonSecretaryUnsigned(payload, CRCProposalTrackingDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyMessageData) && payload.contains(JsonKeySecretaryGeneralOpinionData)) + version = CRCProposalTrackingVersion01; + else + version = CRCProposalTrackingDefaultVersion; + proposalTracking.FromJsonSecretaryUnsigned(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - if (!proposalTracking.IsValidSecretaryUnsigned(CRCProposalTrackingDefaultVersion)) { + if (!proposalTracking.IsValidSecretaryUnsigned(version)) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); } - std::string digest = proposalTracking.DigestSecretaryUnsigned(CRCProposalTrackingDefaultVersion).GetHex(); + std::string digest = proposalTracking.DigestSecretaryUnsigned(version).GetHex(); ArgInfo("r => {}", digest); return digest; @@ -1351,15 +1472,19 @@ namespace Elastos { ArgInfo("payload: {}", payload.dump()); ArgInfo("memo: {}", memo); + uint8_t version = CRCProposalTrackingDefaultVersion; PayloadPtr p(new CRCProposalTracking()); - try { - p->FromJson(payload, CRCProposalTrackingDefaultVersion); - } catch (const std::exception &e) { + if (payload.contains(JsonKeyMessageData) && payload.contains(JsonKeySecretaryGeneralOpinionData)) + version = CRCProposalTrackingVersion01; + else + version = CRCProposalTrackingDefaultVersion; + p->FromJson(payload, version); + } catch (const nlohmann::json::exception &e) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); } - if (!p->IsValid(CRCProposalTrackingDefaultVersion)) { + if (!p->IsValid(version)) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); } OutputArray outputs; @@ -1382,87 +1507,369 @@ namespace Elastos { return result; } - std::string MainchainSubWallet::ProposalWithdrawDigest(const nlohmann::json &payload) const { + std::string MainchainSubWallet::ProposalSecretaryGeneralElectionDigest( + const nlohmann::json &payload) const { ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); ArgInfo("payload: {}", payload.dump()); - CRCProposalWithdraw proposalWithdraw; + uint8_t version = CRCProposalDefaultVersion; + CRCProposal proposal; try { - proposalWithdraw.FromJsonUnsigned(payload, CRCProposalWithdrawVersion); - } catch (const std::exception &e) { - ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::secretaryGeneralElection; + proposal.FromJsonSecretaryElectionUnsigned(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); } - if (!proposalWithdraw.IsValidUnsigned(CRCProposalWithdrawVersion)) + if (!proposal.IsValidSecretaryElectionUnsigned(version)) { ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } - std::string digest = proposalWithdraw.DigestUnsigned(CRCProposalWithdrawVersion).GetHex(); + std::string digest = proposal.DigestSecretaryElectionUnsigned(version).GetHex(); ArgInfo("r => {}", digest); return digest; } - nlohmann::json MainchainSubWallet::CreateProposalWithdrawTransaction(const std::string &recipient, - const std::string &amount, - const nlohmann::json &utxo, - const nlohmann::json &payload, - const std::string &memo) { + std::string MainchainSubWallet::ProposalSecretaryGeneralElectionCRCouncilMemberDigest( + const nlohmann::json &payload) const { ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); - ArgInfo("recipient: {}", recipient); - ArgInfo("amount: {}", amount); - ArgInfo("utxo: {}", utxo.dump()); + ArgInfo("payload: {}", payload.dump()); + + uint8_t version = CRCProposalDefaultVersion; + CRCProposal proposal; + try { + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::secretaryGeneralElection; + proposal.FromJsonSecretaryElectionCRCouncilMemberUnsigned(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!proposal.IsValidSecretaryElectionCRCouncilMemberUnsigned(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + + std::string digest = proposal.DigestSecretaryElectionCRCouncilMemberUnsigned(version).GetHex(); + + ArgInfo("r => {}", digest); + return digest; + } + + nlohmann::json MainchainSubWallet::CreateSecretaryGeneralElectionTransaction( + const nlohmann::json &payload, const std::string &memo) { + WalletPtr wallet = _walletManager->GetWallet(); + ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); ArgInfo("payload: {}", payload.dump()); ArgInfo("memo: {}", memo); -#define CRCProposalWithdrawFee 10000 + uint8_t version = CRCProposalDefaultVersion; + PayloadPtr p(new CRCProposal()); + try { + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::secretaryGeneralElection; + p->FromJson(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } - TransactionPtr txn; - Address recvAddress(recipient); - ErrorChecker::CheckParam(!recvAddress.Valid(), Error::InvalidArgument, "invalid recipient"); + if (!p->IsValid(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + OutputArray outputs; + AddressPtr receiveAddr = wallet->GetReceiveAddress(); + outputs.push_back(OutputPtr(new TransactionOutput(0, *receiveAddr))); + AddressPtr fromAddr(new Address("")); + + TransactionPtr tx = wallet->CreateTransaction(Transaction::crcProposal, p, fromAddr, outputs, memo); - BigInt outputAmount, inputAmount; - outputAmount.setDec(amount); - outputAmount -= CRCProposalWithdrawFee; - ErrorChecker::CheckParam(outputAmount <= 0, Error::InvalidArgument, "invalid amount"); + if (tx->GetOutputs().size() < 2) + ErrorChecker::ThrowLogicException(Error::BalanceNotEnough, "balance not enough"); + tx->RemoveOutput(tx->GetOutputs().front()); + tx->FixIndex(); + + nlohmann::json result; + EncodeTx(result, tx); + ArgInfo("r => {}", result.dump()); + + return result; + } + + ////////////////////////////////////////////////// + /* Proposal Change Owner */ + ////////////////////////////////////////////////// + std::string MainchainSubWallet::ProposalChangeOwnerDigest(const nlohmann::json &payload) const { + ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + + uint8_t version = CRCProposalDefaultVersion; + CRCProposal proposal; try { - PayloadPtr p(new CRCProposalWithdraw()); - p->FromJson(payload, CRCProposalWithdrawVersion); - ErrorChecker::CheckParam(!p->IsValid(CRCProposalWithdrawVersion), Error::InvalidArgument, "invalid payload"); - - txn = TransactionPtr(new Transaction(Transaction::crcProposalWithdraw, p)); - std::string nonce = std::to_string((std::rand() & 0xFFFFFFFF)); - txn->AddAttribute(AttributePtr(new Attribute(Attribute::Nonce, bytes_t(nonce.c_str(), nonce.size())))); - - for (nlohmann::json::const_iterator it = utxo.cbegin(); it != utxo.cend(); ++it) { - uint256 txHash; - txHash.SetHex((*it)["Hash"].get()); - uint16_t index = (*it)["Index"].get(); - BigInt curAmount; - curAmount.setDec((*it)["Amount"].get()); - - txn->AddInput(InputPtr(new TransactionInput(txHash, index))); - inputAmount += curAmount; - } + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::changeProposalOwner; + proposal.FromJsonChangeOwnerUnsigned(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } - if (inputAmount < outputAmount + CRCProposalWithdrawFee) - ErrorChecker::ThrowParamException(Error::InvalidArgument, "input amount not enough"); + if (!proposal.IsValidChangeOwnerUnsigned(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } - txn->AddOutput(OutputPtr(new TransactionOutput(outputAmount, recvAddress))); + std::string digest = proposal.DigestChangeOwnerUnsigned(version).GetHex(); + + ArgInfo("r => {}", digest); + return digest; + } - if (inputAmount > outputAmount + CRCProposalWithdrawFee) - txn->AddOutput(OutputPtr(new TransactionOutput(inputAmount - outputAmount - CRCProposalWithdrawFee, Address("CREXPENSESXXXXXXXXXXXXXXXXXX4UdT6b")))); + std::string MainchainSubWallet::ProposalChangeOwnerCRCouncilMemberDigest(const nlohmann::json &payload) const { + ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); - txn->SetFee(CRCProposalWithdrawFee); + uint8_t version = CRCProposalDefaultVersion; + CRCProposal proposal; + try { + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::changeProposalOwner; + proposal.FromJsonChangeOwnerCRCouncilMemberUnsigned(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } - txn->SetVersion(Transaction::TxVersion::V09); - } catch (const std::exception &e) { - ErrorChecker::ThrowParamException(Error::InvalidArgument, "create tx fail"); + if (!proposal.IsValidChangeOwnerCRCouncilMemberUnsigned(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + + std::string digest = proposal.DigestChangeOwnerCRCouncilMemberUnsigned(version).GetHex(); + + ArgInfo("r => {}", digest); + return digest; + } + + nlohmann::json MainchainSubWallet::CreateProposalChangeOwnerTransaction( + const nlohmann::json &payload, const std::string &memo) { + WalletPtr wallet = _walletManager->GetWallet(); + ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + ArgInfo("memo: {}", memo); + + uint8_t version = CRCProposalDefaultVersion; + PayloadPtr p(new CRCProposal()); + try { + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::changeProposalOwner; + p->FromJson(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!p->IsValid(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); } + OutputArray outputs; + AddressPtr receiveAddr = wallet->GetReceiveAddress(); + outputs.push_back(OutputPtr(new TransactionOutput(0, *receiveAddr))); + AddressPtr fromAddr(new Address("")); + + TransactionPtr tx = wallet->CreateTransaction(Transaction::crcProposal, p, fromAddr, outputs, memo); + + if (tx->GetOutputs().size() < 2) + ErrorChecker::ThrowLogicException(Error::BalanceNotEnough, "balance not enough"); + + tx->RemoveOutput(tx->GetOutputs().front()); + tx->FixIndex(); nlohmann::json result; - EncodeTx(result, txn); + EncodeTx(result, tx); + ArgInfo("r => {}", result.dump()); + + return result; + } + + ////////////////////////////////////////////////// + /* Proposal Terminate Proposal */ + ////////////////////////////////////////////////// + std::string MainchainSubWallet::TerminateProposalOwnerDigest(const nlohmann::json &payload) const { + ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + + uint8_t version = CRCProposalDefaultVersion; + CRCProposal proposal; + try { + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::terminateProposal; + proposal.FromJsonTerminateProposalOwnerUnsigned(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!proposal.IsValidTerminateProposalOwnerUnsigned(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + + std::string digest = proposal.DigestTerminateProposalOwnerUnsigned(version).GetHex(); + + ArgInfo("r => {}", digest); + return digest; + } + + std::string MainchainSubWallet::TerminateProposalCRCouncilMemberDigest(const nlohmann::json &payload) const { + ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + + uint8_t version = CRCProposalDefaultVersion; + CRCProposal proposal; + try { + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::terminateProposal; + proposal.FromJsonTerminateProposalCRCouncilMemberUnsigned(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!proposal.IsValidTerminateProposalCRCouncilMemberUnsigned(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + + std::string digest = proposal.DigestTerminateProposalCRCouncilMemberUnsigned(version).GetHex(); + + ArgInfo("r => {}", digest); + return digest; + } + + nlohmann::json MainchainSubWallet::CreateTerminateProposalTransaction( + const nlohmann::json &payload, const std::string &memo) { + WalletPtr wallet = _walletManager->GetWallet(); + ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + ArgInfo("memo: {}", memo); + uint8_t version = CRCProposalDefaultVersion; + PayloadPtr p(new CRCProposal()); + try { + if (payload.contains(JsonKeyDraftData)) + version = CRCProposalVersion01; + else + version = CRCProposalDefaultVersion; + nlohmann::json payloadFixed = payload; + payloadFixed[JsonKeyType] = CRCProposal::terminateProposal; + p->FromJson(payloadFixed, version); + } catch (const nlohmann::json::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!p->IsValid(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + OutputArray outputs; + AddressPtr receiveAddr = wallet->GetReceiveAddress(); + outputs.push_back(OutputPtr(new TransactionOutput(0, *receiveAddr))); + AddressPtr fromAddr(new Address("")); + + TransactionPtr tx = wallet->CreateTransaction(Transaction::crcProposal, p, fromAddr, outputs, memo); + + if (tx->GetOutputs().size() < 2) + ErrorChecker::ThrowLogicException(Error::BalanceNotEnough, "balance not enough"); + + tx->RemoveOutput(tx->GetOutputs().front()); + tx->FixIndex(); + + nlohmann::json result; + EncodeTx(result, tx); + ArgInfo("r => {}", result.dump()); + + return result; + } + + std::string MainchainSubWallet::ProposalWithdrawDigest(const nlohmann::json &payload) const { + ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + + uint8_t version = CRCProposalWithdrawVersion_01; + CRCProposalWithdraw proposalWithdraw; + try { + proposalWithdraw.FromJsonUnsigned(payload, version); + } catch (const std::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "convert from json"); + } + + if (!proposalWithdraw.IsValidUnsigned(version)) + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + + std::string digest = proposalWithdraw.DigestUnsigned(version).GetHex(); + + ArgInfo("r => {}", digest); + return digest; + } + + nlohmann::json MainchainSubWallet::CreateProposalWithdrawTransaction(const nlohmann::json &payload, + const std::string &memo) { + WalletPtr wallet = _walletManager->GetWallet(); + ArgInfo("{} {}", wallet->GetWalletID(), GetFunName()); + ArgInfo("payload: {}", payload.dump()); + ArgInfo("memo: {}", memo); + + uint8_t version = CRCProposalWithdrawVersion_01; + PayloadPtr p(new CRCProposalWithdraw()); + try { + p->FromJson(payload, version); + } catch (const std::exception &e) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "from json"); + } + + if (!p->IsValid(version)) { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid payload"); + } + OutputArray outputs; + AddressPtr receiveAddr = wallet->GetReceiveAddress(); + outputs.push_back(OutputPtr(new TransactionOutput(0, *receiveAddr))); + AddressPtr fromAddr(new Address("")); + + TransactionPtr tx = wallet->CreateTransaction(Transaction::crcProposalWithdraw, p, fromAddr, outputs, memo); + tx->SetPayloadVersion(version); + + if (tx->GetOutputs().size() < 2) + ErrorChecker::ThrowLogicException(Error::BalanceNotEnough, "balance not enough"); + + tx->RemoveOutput(tx->GetOutputs().front()); + tx->FixIndex(); + + nlohmann::json result; + EncodeTx(result, tx); ArgInfo("r => {}", result.dump()); return result; diff --git a/SDK/Implement/MainchainSubWallet.h b/SDK/Implement/MainchainSubWallet.h index 483416df5..d127e0f90 100644 --- a/SDK/Implement/MainchainSubWallet.h +++ b/SDK/Implement/MainchainSubWallet.h @@ -145,6 +145,10 @@ namespace Elastos { nlohmann::json GetRegisteredCRInfo() const override; + std::string CRCouncilMemberClaimNodeDigest(const nlohmann::json &payload) const override; + + nlohmann::json CreateCRCouncilMemberClaimNodeTransaction(const nlohmann::json &payload, const std::string &memo = "") override; + public: ////////////////////////////////////////////////// /* Proposal */ @@ -178,15 +182,44 @@ namespace Elastos { nlohmann::json CreateProposalTrackingTransaction(const nlohmann::json &payload, const std::string &memo) override; + ////////////////////////////////////////////////// + /* Proposal Secretary General Election */ + ////////////////////////////////////////////////// + std::string ProposalSecretaryGeneralElectionDigest( + const nlohmann::json &payload) const override; + + std::string ProposalSecretaryGeneralElectionCRCouncilMemberDigest( + const nlohmann::json &payload) const override; + + nlohmann::json CreateSecretaryGeneralElectionTransaction( + const nlohmann::json &payload, const std::string &memo = "") override; + + ////////////////////////////////////////////////// + /* Proposal Change Owner */ + ////////////////////////////////////////////////// + std::string ProposalChangeOwnerDigest(const nlohmann::json &payload) const override; + + std::string ProposalChangeOwnerCRCouncilMemberDigest(const nlohmann::json &payload) const override ; + + nlohmann::json CreateProposalChangeOwnerTransaction( + const nlohmann::json &payload, const std::string &memo = "") override ; + + ////////////////////////////////////////////////// + /* Proposal Terminate Proposal */ + ////////////////////////////////////////////////// + std::string TerminateProposalOwnerDigest(const nlohmann::json &payload) const override ; + + std::string TerminateProposalCRCouncilMemberDigest(const nlohmann::json &payload) const override; + + nlohmann::json CreateTerminateProposalTransaction( + const nlohmann::json &payload, const std::string &memo = "") override; + ////////////////////////////////////////////////// /* Proposal Withdraw */ ////////////////////////////////////////////////// std::string ProposalWithdrawDigest(const nlohmann::json &payload) const override; - nlohmann::json CreateProposalWithdrawTransaction(const std::string &recipient, - const std::string &amount, - const nlohmann::json &utxo, - const nlohmann::json &payload, + nlohmann::json CreateProposalWithdrawTransaction(const nlohmann::json &payload, const std::string &memo) override; private: diff --git a/SDK/Implement/MasterWallet.cpp b/SDK/Implement/MasterWallet.cpp index 04c431249..a4791418d 100644 --- a/SDK/Implement/MasterWallet.cpp +++ b/SDK/Implement/MasterWallet.cpp @@ -290,11 +290,11 @@ namespace Elastos { void MasterWallet::CloseAllSubWallets() { for (WalletMap::iterator it = _createdWallets.begin(); it != _createdWallets.end(); ) { - SubWallet *subWallet = dynamic_cast(it->second); + ISubWallet *subWallet = it->second; std::string id = _id + ":" + subWallet->GetChainID(); Log::info("{} closing...", id); - stopPeerManager(subWallet); + subWallet->SyncStop(); it = _createdWallets.erase(it); delete subWallet; @@ -310,11 +310,11 @@ namespace Elastos { if (_createdWallets.find(chainID) == _createdWallets.end()) ErrorChecker::ThrowParamException(Error::InvalidArgument, "chainID not found"); - SubWallet *subWallet = dynamic_cast(_createdWallets[chainID]); - _account->RemoveSubWalletInfo(subWallet->_info); + ISubWallet *subWallet = _createdWallets[chainID]; + subWallet->SyncStop(); + _account->RemoveSubWalletInfo(subWallet->GetChainID()); _account->Save(); - stopPeerManager(subWallet); _createdWallets.erase(chainID); delete subWallet; @@ -513,6 +513,22 @@ namespace Elastos { return valid; } + bool MasterWallet::IsSubWalletAddressValid(const std::string &chainID, const std::string &address) const { + ArgInfo("{} {}", _id, GetFunName()); + ArgInfo("chainID: {}", chainID); + ArgInfo("address: {}", address); + + bool valid = false; + if (chainID == CHAINID_MAINCHAIN || chainID == CHAINID_IDCHAIN || chainID == CHAINID_TOKENCHAIN) { + valid = Address(address).Valid(); + } else if (chainID == CHAINID_ESC) { + valid = addressValidateString(address.c_str()) == ETHEREUM_BOOLEAN_TRUE; + } + + ArgInfo("r => {}", valid); + return valid; + } + std::vector MasterWallet::GetSupportedChains() const { ArgInfo("{} {}", _id, GetFunName()); @@ -535,6 +551,18 @@ namespace Elastos { _account->ChangePassword(oldPassword, newPassword); } + void MasterWallet::ResetPassword(const std::string &mnemonic, const std::string &passphrase, + const std::string &newPassword) { + ArgInfo("{} {}", _id, GetFunName()); + ArgInfo("m: *"); + ArgInfo("passphrase: *"); + ArgInfo("passwd: *"); + + _account->ResetPassword(mnemonic, passphrase, newPassword); + + ArgInfo("r => "); + } + nlohmann::json MasterWallet::GetBasicInfo() const { ArgInfo("{} {}", _id, GetFunName()); @@ -551,7 +579,8 @@ namespace Elastos { void MasterWallet::FlushData() { for (WalletMap::const_iterator it = _createdWallets.cbegin(); it != _createdWallets.cend(); ++it) { SubWallet *subWallet = dynamic_cast(it->second); - subWallet->FlushData(); + if (subWallet) + subWallet->FlushData(); } } diff --git a/SDK/Implement/MasterWallet.h b/SDK/Implement/MasterWallet.h index 5552ef365..25c7f9a61 100644 --- a/SDK/Implement/MasterWallet.h +++ b/SDK/Implement/MasterWallet.h @@ -106,10 +106,15 @@ namespace Elastos { virtual bool IsAddressValid(const std::string &address) const; + virtual bool IsSubWalletAddressValid(const std::string &chainID, const std::string &address) const; + virtual std::vector GetSupportedChains() const; virtual void ChangePassword(const std::string &oldPassword, const std::string &newPassword); + virtual void ResetPassword(const std::string &mnemonic, const std::string &passphrase, + const std::string &newPassword); + void InitSubWallets(); std::string GetWalletID() const; diff --git a/SDK/Implement/MasterWalletManager.cpp b/SDK/Implement/MasterWalletManager.cpp index aac7d73c7..c6cc308cd 100644 --- a/SDK/Implement/MasterWalletManager.cpp +++ b/SDK/Implement/MasterWalletManager.cpp @@ -567,6 +567,23 @@ namespace Elastos { }); } + void MasterWalletManager::SetLogLevel(const std::string &level) { + ArgInfo("{}", GetFunName()); + ArgInfo("level: {}", level); + + if (level != "trace" && + level != "debug" && + level != "info" && + level != "warning" && + level != "error" && + level != "critical" && + level != "off") { + ErrorChecker::ThrowParamException(Error::InvalidArgument, "invalid level"); + } + + Log::setLevel(spdlog::level::from_str(level)); + } + std::vector MasterWalletManager::GetAllMasterWalletID() const { ArgInfo("{}", GetFunName()); diff --git a/SDK/Implement/SubWallet.cpp b/SDK/Implement/SubWallet.cpp index 13be07451..425117e08 100644 --- a/SDK/Implement/SubWallet.cpp +++ b/SDK/Implement/SubWallet.cpp @@ -81,7 +81,9 @@ namespace Elastos { } SubWallet::~SubWallet() { - + if (_walletManager) { + _walletManager->ExecutorStop(); + } } std::string SubWallet::GetChainID() const { @@ -422,6 +424,11 @@ namespace Elastos { std::vector jsonList; const WalletPtr &wallet = _walletManager->GetWallet(); TransactionPtr txFound; + std::map genesisAddresses; + + genesisAddresses[CHAINID_IDCHAIN] = _parent->GetChainConfig(CHAINID_IDCHAIN)->GenesisAddress(); + genesisAddresses[CHAINID_ESC] = _parent->GetChainConfig(CHAINID_ESC)->GenesisAddress(); + genesisAddresses[CHAINID_TOKENCHAIN] = _parent->GetChainConfig(CHAINID_TOKENCHAIN)->GenesisAddress(); std::vector txnPending = wallet->LoadTxn(TXN_PENDING); for (std::vector::iterator it = txnPending.begin(); it != txnPending.end();) { @@ -446,14 +453,14 @@ namespace Elastos { } if (txFound) { confirms = txFound->GetConfirms(wallet->LastBlockHeight()); - jsonList.push_back(txFound->GetSummary(wallet, confirms, true)); + jsonList.push_back(txFound->GetSummary(wallet, genesisAddresses, confirms, true)); j["Transactions"] = jsonList; } } else { size_t realCnt, cur; for (realCnt = 0, cur = start; cur < txnPending.size() && realCnt < count; ++realCnt, ++cur) { confirms = txnPending[realCnt]->GetConfirms(wallet->LastBlockHeight()); - jsonList.push_back(txnPending[realCnt]->GetSummary(wallet, confirms, false)); + jsonList.push_back(txnPending[realCnt]->GetSummary(wallet, genesisAddresses, confirms, false)); } if (realCnt < count) { size_t offset = cur - txnPending.size(); @@ -461,7 +468,7 @@ namespace Elastos { offset, count - realCnt); for (size_t i = 0; i < txns.size(); ++i) { confirms = txns[i]->GetConfirms(wallet->LastBlockHeight()); - jsonList.push_back(txns[i]->GetSummary(wallet, confirms, false)); + jsonList.push_back(txns[i]->GetSummary(wallet, genesisAddresses, confirms, false)); } } j["Transactions"] = jsonList; @@ -707,5 +714,18 @@ namespace Elastos { return info; } + nlohmann::json SubWallet::GetLastBlockInfo() const { + ArgInfo("{} {}", _walletManager->GetWallet()->GetWalletID(), GetFunName()); + + nlohmann::json j; + j["Height"] = _walletManager->GetPeerManager()->GetLastBlockHeight(); + j["Timestamp"] = _walletManager->GetPeerManager()->GetLastBlockTimestamp(); + j["Hash"] = _walletManager->GetPeerManager()->GetLastBlockHash().GetHex(); + + ArgInfo("r => {}", j.dump()); + + return j; + } + } } diff --git a/SDK/Implement/SubWallet.h b/SDK/Implement/SubWallet.h index 156a0e55a..10d3eaaf8 100644 --- a/SDK/Implement/SubWallet.h +++ b/SDK/Implement/SubWallet.h @@ -129,6 +129,8 @@ namespace Elastos { virtual nlohmann::json GetAssetInfo( const std::string &assetID) const; + virtual nlohmann::json GetLastBlockInfo() const; + virtual bool SetFixedPeer(const std::string &address, uint16_t port); virtual void SyncStart(); diff --git a/SDK/P2P/Message/VersionMessage.cpp b/SDK/P2P/Message/VersionMessage.cpp index eedd7f2ff..4379993b8 100644 --- a/SDK/P2P/Message/VersionMessage.cpp +++ b/SDK/P2P/Message/VersionMessage.cpp @@ -9,6 +9,7 @@ #define ENABLED_SERVICES 0ULL // we don't provide full blocks to remote nodes #define PROTOCOL_VERSION 70013 +#define CRPROTOCOL_VERSION 80000 namespace Elastos { namespace ElaWallet { @@ -63,7 +64,17 @@ namespace Elastos { } _peer->SetLastBlock(uint32_t(height)); - _peer->info("got version {}, services {}", _peer->GetVersion(), _peer->GetServices()); + uint8_t relay = 0; + if (!stream.ReadUint8(relay)) { + _peer->warn("version msg without relay"); + } + + std::string versionString; + if (!stream.ReadVarString(versionString)) { + _peer->warn("version msg without version-string"); + } + + _peer->info("got version: {}, services: {}, relay: {}, v: {}", _peer->GetVersion(), _peer->GetServices(), relay, versionString); SendMessageParameter param; _peer->SendMessage(MSG_VERACK, param); return true; @@ -72,7 +83,7 @@ namespace Elastos { void VersionMessage::Send(const SendMessageParameter ¶m) { ByteStream stream; - stream.WriteUint32(PROTOCOL_VERSION); + stream.WriteUint32(CRPROTOCOL_VERSION); stream.WriteUint64(ENABLED_SERVICES); stream.WriteUint32(uint32_t(time(nullptr))); stream.WriteUint16(_peer->GetPort()); @@ -80,6 +91,7 @@ namespace Elastos { stream.WriteUint64(_peer->GetNonce()); stream.WriteUint64(0); stream.WriteUint8(0); + stream.WriteVarString("spv-" SPVSDK_VERSION_MESSAGE); SendMessage(stream.GetBytes(), Type()); } diff --git a/SDK/P2P/PeerManager.cpp b/SDK/P2P/PeerManager.cpp index c2ebbc0fa..0704a54f9 100644 --- a/SDK/P2P/PeerManager.cpp +++ b/SDK/P2P/PeerManager.cpp @@ -26,7 +26,7 @@ #include #include -#define PROTOCOL_TIMEOUT 40.0 +#define PROTOCOL_TIMEOUT 120.0 #define MAX_CONNECT_FAILURES 1000 // notify user of network problems after this many connect failures in a row #define PEER_FLAG_SYNCED 0x01 #define PEER_FLAG_NEEDSUPDATE 0x02 @@ -485,13 +485,13 @@ namespace Elastos { } uint32_t PeerManager::GetLastBlockTimestamp() const { - uint32_t timestamp; + boost::mutex::scoped_lock scoped_lock(lock); + return _lastBlock->GetTimestamp(); + } - { - boost::mutex::scoped_lock scoped_lock(lock); - timestamp = _lastBlock->GetTimestamp(); - } - return timestamp; + uint256 PeerManager::GetLastBlockHash() const { + boost::mutex::scoped_lock scopedLock(lock); + return _lastBlock->GetHash(); } time_t PeerManager::GetKeepAliveTimestamp() const { diff --git a/SDK/P2P/PeerManager.h b/SDK/P2P/PeerManager.h index d271d88f0..d6cff79a5 100644 --- a/SDK/P2P/PeerManager.h +++ b/SDK/P2P/PeerManager.h @@ -112,6 +112,8 @@ namespace Elastos { uint32_t GetLastBlockTimestamp() const; + uint256 GetLastBlockHash() const; + time_t GetKeepAliveTimestamp() const; void SetKeepAliveTimestamp(time_t t); diff --git a/SDK/Plugin/Transaction/Payload/CRCAssetsRectify.cpp b/SDK/Plugin/Transaction/Payload/CRCAssetsRectify.cpp new file mode 100644 index 000000000..ab4d3222b --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/CRCAssetsRectify.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "CRCAssetsRectify.h" +#include + +namespace Elastos { + namespace ElaWallet { + + CRCAssetsRectify::CRCAssetsRectify() { + + } + + CRCAssetsRectify::CRCAssetsRectify(const CRCAssetsRectify &payload) { + + } + + CRCAssetsRectify::~CRCAssetsRectify() { + + } + + size_t CRCAssetsRectify::EstimateSize(uint8_t version) const { + + return 0; + } + + void CRCAssetsRectify::Serialize(ByteStream &ostream, uint8_t version) const { + + } + + bool CRCAssetsRectify::Deserialize(const ByteStream &istream, uint8_t version) { + + return true; + } + + nlohmann::json CRCAssetsRectify::ToJson(uint8_t version) const { + + return nlohmann::json(); + } + + void CRCAssetsRectify::FromJson(const nlohmann::json &j, uint8_t version) { + + } + + IPayload &CRCAssetsRectify::operator=(const IPayload &payload) { + try { + const CRCAssetsRectify &p= dynamic_cast(payload); + operator=(p); + } catch (const std::bad_cast &e) { + Log::error("payload is not instance of CRCAssetsRectify"); + } + + return *this; + } + + CRCAssetsRectify &CRCAssetsRectify::operator=(const CRCAssetsRectify &payload) { + + return *this; + } + + } +} \ No newline at end of file diff --git a/SDK/Plugin/Transaction/Payload/CRCAssetsRectify.h b/SDK/Plugin/Transaction/Payload/CRCAssetsRectify.h new file mode 100644 index 000000000..a559559ad --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/CRCAssetsRectify.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ELASTOS_SPVSDK_CRCASSETSRECTIFY_H__ +#define __ELASTOS_SPVSDK_CRCASSETSRECTIFY_H__ + +#include "IPayload.h" + +namespace Elastos { + namespace ElaWallet { + + class CRCAssetsRectify : public IPayload { + public: + CRCAssetsRectify(); + + CRCAssetsRectify(const CRCAssetsRectify &payload); + + ~CRCAssetsRectify(); + + virtual size_t EstimateSize(uint8_t version) const; + + virtual void Serialize(ByteStream &ostream, uint8_t version) const; + + virtual bool Deserialize(const ByteStream &istream, uint8_t version); + + virtual nlohmann::json ToJson(uint8_t version) const; + + virtual void FromJson(const nlohmann::json &j, uint8_t version); + + virtual IPayload &operator=(const IPayload &payload); + + CRCAssetsRectify &operator=(const CRCAssetsRectify &payload); + }; + + } +} + +#endif diff --git a/SDK/Plugin/Transaction/Payload/CRCProposal.cpp b/SDK/Plugin/Transaction/Payload/CRCProposal.cpp index af59dc5d0..4e580a04f 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposal.cpp +++ b/SDK/Plugin/Transaction/Payload/CRCProposal.cpp @@ -25,23 +25,13 @@ #include #include #include +#include +#include namespace Elastos { namespace ElaWallet { -#define JsonKeyType "Type" -#define JsonKeyStage "Stage" -#define JsonKeyAmount "Amount" - -#define JsonKeyType "Type" -#define JsonKeyCategoryData "CategoryData" -#define JsonKeyOwnerPublicKey "OwnerPublicKey" -#define JsonKeyDraftHash "DraftHash" -#define JsonKeyBudgets "Budgets" -#define JsonKeyRecipient "Recipient" -#define JsonKeySignature "Signature" -#define JsonKeyCRCouncilMemberDID "CRCouncilMemberDID" -#define JsonKeyCRCouncilMemberSignature "CRCouncilMemberSignature" +#define DRAFT_DATA_MAX_SIZE (1024 * 1024) Budget::Budget() { @@ -125,6 +115,10 @@ namespace Elastos { _amount.setDec(j[JsonKeyAmount].get()); } + bool Budget::operator==(const Budget &budget) const { + return _type == budget._type && _stage == budget._stage && _amount == budget._amount; + } + CRCProposal::CRCProposal() { } @@ -189,6 +183,46 @@ namespace Elastos { return _recipient; } + void CRCProposal::SetTargetProposalHash(const uint256 &hash) { + _targetProposalHash = hash; + } + + const uint256 &CRCProposal::GetTargetProposalHash() const { + return _targetProposalHash; + } + + void CRCProposal::SetNewRecipient(const Address &recipient) { + _newRecipient = recipient; + } + + const Address &CRCProposal::GetNewRecipient() const { + return _newRecipient; + } + + void CRCProposal::SetNewOwnerPublicKey(const bytes_t &pubkey) { + _newOwnerPublicKey = pubkey; + } + + const bytes_t &CRCProposal::GetNewOwnerPublicKey() const { + return _newOwnerPublicKey; + } + + void CRCProposal::SetSecretaryPublicKey(const bytes_t &pubkey) { + _secretaryPublicKey = pubkey; + } + + const bytes_t CRCProposal::GetSecretaryPublicKey() const { + return _secretaryPublicKey; + } + + void CRCProposal::SetSecretaryDID(const Address &did) { + _secretaryDID = did; + } + + const Address &CRCProposal::GetSecretaryDID() const { + return _secretaryDID; + } + void CRCProposal::SetSignature(const bytes_t &signature) { _signature = signature; } @@ -197,6 +231,22 @@ namespace Elastos { return _signature; } + void CRCProposal::SetNewOwnerSignature(const bytes_t &sign) { + _newOwnerSignature = sign; + } + + const bytes_t &CRCProposal::GetNewOwnerSignature() const { + return _newOwnerSignature; + } + + void CRCProposal::SetSecretarySignature(const bytes_t &sign) { + _secretarySignature = sign; + } + + const bytes_t &CRCProposal::GetSecretarySignature() const { + return _secretarySignature; + } + void CRCProposal::SetCRCouncilMemberSignature(const bytes_t &signature) { _crCouncilMemberSignature = signature; } @@ -205,7 +255,7 @@ namespace Elastos { return _crCouncilMemberSignature; } - const uint256 &CRCProposal::DigestOwnerUnsigned(uint8_t version) const { + const uint256 &CRCProposal::DigestNormalOwnerUnsigned(uint8_t version) const { if (_digestOwnerUnsigned == 0) { ByteStream stream; SerializeOwnerUnsigned(stream, version); @@ -215,7 +265,7 @@ namespace Elastos { return _digestOwnerUnsigned; } - const uint256 &CRCProposal::DigestCRCouncilMemberUnsigned(uint8_t version) const { + const uint256 &CRCProposal::DigestNormalCRCouncilMemberUnsigned(uint8_t version) const { if (_digestCRCouncilMemberUnsigned == 0) { ByteStream stream; SerializeCRCouncilMemberUnsigned(stream, version); @@ -226,91 +276,121 @@ namespace Elastos { } size_t CRCProposal::EstimateSize(uint8_t version) const { - ByteStream stream; + ByteStream stream, byteStream; size_t size = 0; size += sizeof(uint16_t); - size += stream.WriteVarUint(_categoryData.size()); size += _categoryData.size(); - size += stream.WriteVarUint(_ownerPublicKey.size()); size += _ownerPublicKey.size(); - size += _draftHash.size(); - size += stream.WriteVarUint(_budgets.size()); - - ByteStream byteStream; - for (size_t i = 0; i < _budgets.size(); ++i) { - _budgets[i].Serialize(byteStream); + switch (_type) { + case elip: + case normal: + size += stream.WriteVarUint(_budgets.size()); + + for (size_t i = 0; i < _budgets.size(); ++i) { + _budgets[i].Serialize(byteStream); + } + size += byteStream.GetBytes().size(); + size += _recipient.ProgramHash().size(); + size += stream.WriteVarUint(_signature.size()); + size += _signature.size(); + break; + + case secretaryGeneralElection: + size += stream.WriteVarUint(_secretaryPublicKey.size()); + size += _secretaryPublicKey.size(); + size += _secretaryDID.ProgramHash().size(); + size += stream.WriteVarUint(_secretarySignature.size()); + size += _secretarySignature.size(); + size += stream.WriteVarUint(_signature.size()); + size += _signature.size(); + break; + + case changeProposalOwner: + size += _targetProposalHash.size(); + size += _newRecipient.ProgramHash().size(); + size += stream.WriteVarUint(_newOwnerPublicKey.size()); + size += _newOwnerPublicKey.size(); + size += stream.WriteVarUint(_signature.size()); + size += _signature.size(); + size += stream.WriteVarUint(_newOwnerSignature.size()); + size += _newOwnerSignature.size(); + break; + + case terminateProposal: + size += stream.WriteVarUint(_signature.size()); + size += _signature.size(); + size += _targetProposalHash.size(); + break; + + default: + break; } - size += byteStream.GetBytes().size(); - - size += _recipient.ProgramHash().size(); - - size += stream.WriteVarUint(_signature.size()); - size += _signature.size(); size += _crCouncilMemberDID.ProgramHash().size(); - size += stream.WriteVarUint(_crCouncilMemberSignature.size()); size += _crCouncilMemberSignature.size(); return size; } - void CRCProposal::SerializeOwnerUnsigned(ByteStream &ostream, uint8_t version) const { - ostream.WriteUint16(_type); - ostream.WriteVarString(_categoryData); - ostream.WriteVarBytes(_ownerPublicKey); - ostream.WriteBytes(_draftHash); - ostream.WriteVarUint(_budgets.size()); - for (size_t i = 0; i < _budgets.size(); ++i) { - _budgets[i].Serialize(ostream); - } - ostream.WriteBytes(_recipient.ProgramHash()); + // normal or elip + void CRCProposal::SerializeOwnerUnsigned(ByteStream &stream, uint8_t version) const { + stream.WriteUint16(_type); + stream.WriteVarString(_categoryData); + stream.WriteVarBytes(_ownerPublicKey); + stream.WriteBytes(_draftHash); + if (version >= CRCProposalVersion01) + stream.WriteVarBytes(_draftData); + stream.WriteVarUint(_budgets.size()); + for (size_t i = 0; i < _budgets.size(); ++i) + _budgets[i].Serialize(stream); + stream.WriteBytes(_recipient.ProgramHash()); } - bool CRCProposal::DeserializeOwnerUnsigned(const ByteStream &istream, uint8_t version) { - uint16_t type = 0; - if (!istream.ReadUint16(type)) { - SPVLOG_ERROR("deserialize type"); - return false; - } - _type = CRCProposal::Type(type); - - if (!istream.ReadVarString(_categoryData)) { + bool CRCProposal::DeserializeOwnerUnsigned(const ByteStream &stream, uint8_t version) { + if (!stream.ReadVarString(_categoryData)) { SPVLOG_ERROR("deserialize categoryData"); return false; } - if (!istream.ReadVarBytes(_ownerPublicKey)) { + if (!stream.ReadVarBytes(_ownerPublicKey)) { SPVLOG_ERROR("deserialize owner PublicKey"); return false; } - if (!istream.ReadBytes(_draftHash)) { + if (!stream.ReadBytes(_draftHash)) { SPVLOG_ERROR("deserialize draftHash"); return false; } + if (version >= CRCProposalVersion01) { + if (!stream.ReadVarBytes(_draftData)) { + SPVLOG_ERROR("deserialize draftdata"); + return false; + } + } + uint64_t count = 0; - if (!istream.ReadVarUint(count)) { + if (!stream.ReadVarUint(count)) { SPVLOG_ERROR("deserialize budgets size"); return false; } _budgets.resize(count); for (size_t i = 0; i < count; ++i) { - if (!_budgets[i].Deserialize(istream)) { + if (!_budgets[i].Deserialize(stream)) { SPVLOG_ERROR("deserialize bugets"); return false; } } uint168 programHash; - if (!istream.ReadBytes(programHash)) { - SPVLOG_ERROR("deserialize recipient key"); + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize recipient"); return false; } _recipient = Address(programHash); @@ -347,19 +427,19 @@ namespace Elastos { return true; } - void CRCProposal::Serialize(ByteStream &ostream, uint8_t version) const { - SerializeCRCouncilMemberUnsigned(ostream, version); + void CRCProposal::SerializeNormalOrELIP(ByteStream &stream, uint8_t version) const { + SerializeCRCouncilMemberUnsigned(stream, version); - ostream.WriteVarBytes(_crCouncilMemberSignature); + stream.WriteVarBytes(_crCouncilMemberSignature); } - bool CRCProposal::Deserialize(const ByteStream &istream, uint8_t version) { - if (!DeserializeCRCouncilMemberUnsigned(istream, version)) { + bool CRCProposal::DeserializeNormalOrELIP(const ByteStream &stream, uint8_t version) { + if (!DeserializeCRCouncilMemberUnsigned(stream, version)) { SPVLOG_ERROR("CRCProposal deserialize crc unsigned"); return false; } - if (!istream.ReadVarBytes(_crCouncilMemberSignature)) { + if (!stream.ReadVarBytes(_crCouncilMemberSignature)) { SPVLOG_ERROR("CRCProposal deserialize crc signature"); return false; } @@ -367,51 +447,851 @@ namespace Elastos { return true; } - nlohmann::json CRCProposal::ToJsonOwnerUnsigned(uint8_t version) const { + // change owner + void CRCProposal::SerializeChangeOwnerUnsigned(ByteStream &stream, uint8_t version) const { + uint16_t type = _type; + stream.WriteUint16(type); + + stream.WriteVarString(_categoryData); + stream.WriteVarBytes(_ownerPublicKey); + stream.WriteBytes(_draftHash); + if (version >= CRCProposalVersion01) + stream.WriteVarBytes(_draftData); + stream.WriteBytes(_targetProposalHash); + stream.WriteBytes(_newRecipient.ProgramHash()); + stream.WriteVarBytes(_newOwnerPublicKey); + } + + bool CRCProposal::DeserializeChangeOwnerUnsigned(const ByteStream &stream, uint8_t version) { + if (!stream.ReadVarString(_categoryData)) { + SPVLOG_ERROR("deserialize categoryData"); + return false; + } + + if (!stream.ReadVarBytes(_ownerPublicKey)) { + SPVLOG_ERROR("deserialize owner PublicKey"); + return false; + } + + if (!stream.ReadBytes(_draftHash)) { + SPVLOG_ERROR("deserialize draftHash"); + return false; + } + + if (version >= CRCProposalVersion01) { + if (!stream.ReadVarBytes(_draftData)) { + SPVLOG_ERROR("deserialize draftData"); + return false; + } + } + + if (!stream.ReadBytes(_targetProposalHash)) { + SPVLOG_ERROR("deserialize target proposal hash"); + return false; + } + + uint168 programHash; + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize new recipient"); + return false; + } + _newRecipient = Address(programHash); + + if (!stream.ReadVarBytes(_newOwnerPublicKey)) { + SPVLOG_ERROR("deserialize new owner PublicKey"); + return false; + } + + return true; + } + + void CRCProposal::SerializeChangeOwnerCRCouncilMemberUnsigned(ByteStream &stream, uint8_t version) const { + SerializeChangeOwnerUnsigned(stream, version); + + stream.WriteVarBytes(_signature); + stream.WriteVarBytes(_newOwnerSignature); + stream.WriteBytes(_crCouncilMemberDID.ProgramHash()); + } + + bool CRCProposal::DeserializeChangeOwnerCRCouncilMemberUnsigned(const ByteStream &stream, uint8_t version) { + if (!DeserializeChangeOwnerUnsigned(stream, version)) { + SPVLOG_ERROR("deserialize change owner unsigned"); + return false; + } + + if (!stream.ReadVarBytes(_signature)) { + SPVLOG_ERROR("deserialize change owner signature"); + return false; + } + + if (!stream.ReadVarBytes(_newOwnerSignature)) { + SPVLOG_ERROR("deserialize change owner new owner signature"); + return false; + } + + uint168 programHash; + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize sponsor did"); + return false; + } + _crCouncilMemberDID = Address(programHash); + + return true; + } + + void CRCProposal::SerializeChangeOwner(ByteStream &stream, uint8_t version) const { + SerializeChangeOwnerCRCouncilMemberUnsigned(stream, version); + + stream.WriteVarBytes(_crCouncilMemberSignature); + } + + bool CRCProposal::DeserializeChangeOwner(const ByteStream &stream, uint8_t version) { + if (!DeserializeChangeOwnerCRCouncilMemberUnsigned(stream, version)) { + SPVLOG_ERROR("deserialize change owner cr council member unsigned"); + return false; + } + + if (!stream.ReadVarBytes(_crCouncilMemberSignature)) { + SPVLOG_ERROR("deserialize change owner cr council member signature"); + return false; + } + + return true; + } + + nlohmann::json CRCProposal::ToJsonChangeOwnerUnsigned(uint8_t version) const { nlohmann::json j; + j[JsonKeyType] = _type; j[JsonKeyCategoryData] = _categoryData; j[JsonKeyOwnerPublicKey] = _ownerPublicKey.getHex(); j[JsonKeyDraftHash] = _draftHash.GetHex(); + if (version >= CRCProposalVersion01) + j[JsonKeyDraftData] = Base64::Encode(_draftData); + j[JsonKeyTargetProposalHash] = _targetProposalHash.GetHex(); + j[JsonKeyNewRecipient] = _newRecipient.String(); + j[JsonKeyNewOwnerPublicKey] = _newOwnerPublicKey.getHex(); + + return j; + } + + void CRCProposal::FromJsonChangeOwnerUnsigned(const nlohmann::json &j, uint8_t version) { + _type = CRCProposal::Type(j[JsonKeyType].get()); + _categoryData = j[JsonKeyCategoryData].get(); + _ownerPublicKey.setHex(j[JsonKeyOwnerPublicKey].get()); + _draftHash.SetHex(j[JsonKeyDraftHash].get()); + if (version >= CRCProposalVersion01) { + std::string draftData = j[JsonKeyDraftData].get(); + _draftData = Base64::Decode(draftData); + ErrorChecker::CheckParam(_draftData.size() > DRAFT_DATA_MAX_SIZE, Error::ProposalContentTooLarge, "proposal origin content too large"); + uint256 draftHash(sha256_2(_draftData)); + ErrorChecker::CheckParam(draftHash != _draftHash, Error::ProposalHashNotMatch, "proposal hash not match"); + } + _targetProposalHash.SetHex(j[JsonKeyTargetProposalHash].get()); + _newRecipient = Address(j[JsonKeyNewRecipient].get()); + _newOwnerPublicKey.setHex(j[JsonKeyNewOwnerPublicKey].get()); + } + + nlohmann::json CRCProposal::ToJsonChangeOwnerCRCouncilMemberUnsigned(uint8_t version) const { + nlohmann::json j = ToJsonChangeOwnerUnsigned(version); + + j[JsonKeySignature] = _signature.getHex(); + j[JsonKeyNewOwnerSignature] = _newOwnerSignature.getHex(); + j[JsonKeyCRCouncilMemberDID] = _crCouncilMemberDID.String(); + + return j; + } + + void CRCProposal::FromJsonChangeOwnerCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version) { + FromJsonChangeOwnerUnsigned(j, version); + + _signature.setHex(j[JsonKeySignature].get()); + _newOwnerSignature.setHex(j[JsonKeyNewOwnerSignature].get()); + _crCouncilMemberDID = Address(j[JsonKeyCRCouncilMemberDID].get()); + } + + bool CRCProposal::IsValidChangeOwnerUnsigned(uint8_t version) const { + if (_type != changeProposalOwner) { + SPVLOG_ERROR("invalid type: {}", _type); + return false; + } + + if (_categoryData.size() > 4096) { + SPVLOG_ERROR("category data exceed 4096 bytes"); + return false; + } + + try { + Key key(_ownerPublicKey); + Key key1(_newOwnerPublicKey); + } catch (const std::exception &e) { + SPVLOG_ERROR("invalid pubkey"); + return false; + } + + if (_draftHash == 0 || _targetProposalHash == 0) { + SPVLOG_ERROR("invalid hash"); + return false; + } + + if (!_newRecipient.Valid()) { + SPVLOG_ERROR("invalid new recipient"); + return false; + } + + return true; + } + + bool CRCProposal::IsValidChangeOwnerCRCouncilMemberUnsigned(uint8_t version) const { + if (!IsValidChangeOwnerUnsigned(version)) { + return false; + } + + try { + if (!Key(_ownerPublicKey).Verify(DigestChangeOwnerUnsigned(version), _signature)) { + SPVLOG_ERROR("verify signature fail"); + return false; + } + + if (!Key(_newOwnerPublicKey).Verify(DigestChangeOwnerUnsigned(version), _newOwnerSignature)) { + SPVLOG_ERROR("verify new owner signature fail"); + return false; + } + } catch (const std::exception &e) { + SPVLOG_ERROR("verify signature exception: {}", e.what()); + return false; + } + + if (!_crCouncilMemberDID.Valid()) { + SPVLOG_ERROR("invalid cr council member did"); + return false; + } + + return true; + } + + const uint256 &CRCProposal::DigestChangeOwnerUnsigned(uint8_t version) const { + if (_digestChangeOwnerUnsigned == 0) { + ByteStream stream; + SerializeChangeOwnerUnsigned(stream, version); + _digestChangeOwnerUnsigned = sha256(stream.GetBytes()); + } + + return _digestChangeOwnerUnsigned; + } + + const uint256 &CRCProposal::DigestChangeOwnerCRCouncilMemberUnsigned(uint8_t version) const { + if (_digestChangeOwnerCRCouncilMemberUnsigned == 0) { + ByteStream stream; + SerializeChangeOwnerCRCouncilMemberUnsigned(stream, version); + _digestChangeOwnerCRCouncilMemberUnsigned = sha256(stream.GetBytes()); + } + + return _digestChangeOwnerCRCouncilMemberUnsigned; + } + + // terminate proposal + void CRCProposal::SerializeTerminateProposalUnsigned(ByteStream &stream, uint8_t version) const { + uint16_t type = _type; + stream.WriteUint16(type); + stream.WriteVarString(_categoryData); + stream.WriteVarBytes(_ownerPublicKey); + stream.WriteBytes(_draftHash); + if (version >= CRCProposalVersion01) + stream.WriteVarBytes(_draftData); + stream.WriteBytes(_targetProposalHash); + } + + bool CRCProposal::DeserializeTerminateProposalUnsigned(const ByteStream &stream, uint8_t version) { + if (!stream.ReadVarString(_categoryData)) { + SPVLOG_ERROR("deserialize terminate proposal category data"); + return false; + } + + if (!stream.ReadVarBytes(_ownerPublicKey)) { + SPVLOG_ERROR("deserialize terminate proposal owner pubkey"); + return false; + } + + if (!stream.ReadBytes(_draftHash)) { + SPVLOG_ERROR("deserialize terminate proposal draft hash"); + return false; + } + + if (version >= CRCProposalVersion01) { + if (!stream.ReadVarBytes(_draftData)) { + SPVLOG_ERROR("deserialize terminate proposal draftData"); + return false; + } + } + + if (!stream.ReadBytes(_targetProposalHash)) { + SPVLOG_ERROR("deserialize terminate proposal target proposal hash"); + return false; + } + + return true; + } + + void CRCProposal::SerializeTerminateProposalCRCouncilMemberUnsigned(ByteStream &stream, uint8_t version) const { + SerializeTerminateProposalUnsigned(stream, version); + + stream.WriteVarBytes(_signature); + stream.WriteBytes(_crCouncilMemberDID.ProgramHash()); + } + + bool CRCProposal::DeserializeTerminateProposalCRCouncilMemberUnsigned(const ByteStream &stream, uint8_t version) { + if (!DeserializeTerminateProposalUnsigned(stream, version)) { + SPVLOG_ERROR("deserialize terminate proposal unsigned"); + return false; + } + + if (!stream.ReadVarBytes(_signature)) { + SPVLOG_ERROR("deserialize terminate proposal signature"); + return false; + } + + uint168 programHash; + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize sponsor did"); + return false; + } + _crCouncilMemberDID = Address(programHash); + + return true; + } + + void CRCProposal::SerializeTerminateProposal(ByteStream &stream, uint8_t version) const { + SerializeTerminateProposalCRCouncilMemberUnsigned(stream, version); + + stream.WriteVarBytes(_crCouncilMemberSignature); + } + + bool CRCProposal::DeserializeTerminateProposal(const ByteStream &stream, uint8_t version) { + if (!DeserializeTerminateProposalCRCouncilMemberUnsigned(stream, version)) { + SPVLOG_ERROR("deserialize terminate proposal cr council member unsigned"); + return false; + } + + if (!stream.ReadVarBytes(_crCouncilMemberSignature)) { + SPVLOG_ERROR("deserialize change owner cr council member signature"); + return false; + } + + return true; + } + + nlohmann::json CRCProposal::ToJsonTerminateProposalOwnerUnsigned(uint8_t version) const { + nlohmann::json j; + + j[JsonKeyType] = _type; + j[JsonKeyCategoryData] = _categoryData; + j[JsonKeyOwnerPublicKey] = _ownerPublicKey.getHex(); + j[JsonKeyDraftHash] = _draftHash.GetHex(); + if (version >= CRCProposalVersion01) + j[JsonKeyDraftData] = Base64::Encode(_draftData); + j[JsonKeyTargetProposalHash] = _targetProposalHash.GetHex(); + + return j; + } + + void CRCProposal::FromJsonTerminateProposalOwnerUnsigned(const nlohmann::json &j, uint8_t version) { + _type = CRCProposal::Type(j[JsonKeyType].get()); + _categoryData = j[JsonKeyCategoryData].get(); + _ownerPublicKey.setHex(j[JsonKeyOwnerPublicKey].get()); + _draftHash.SetHex(j[JsonKeyDraftHash].get()); + if (version >= CRCProposalVersion01) { + std::string draftData = j[JsonKeyDraftData].get(); + _draftData = Base64::Decode(draftData); + ErrorChecker::CheckParam(_draftData.size() > DRAFT_DATA_MAX_SIZE, Error::ProposalContentTooLarge, "proposal origin content too large"); + uint256 draftHash(sha256_2(_draftData)); + ErrorChecker::CheckParam(draftHash != _draftHash, Error::ProposalHashNotMatch, "proposal hash not match"); + } + _targetProposalHash.SetHex(j[JsonKeyTargetProposalHash].get()); + } + + nlohmann::json CRCProposal::ToJsonTerminateProposalCRCouncilMemberUnsigned(uint8_t version) const { + nlohmann::json j = ToJsonTerminateProposalOwnerUnsigned(version); + + j[JsonKeySignature] = _signature.getHex(); + j[JsonKeyCRCouncilMemberDID] = _crCouncilMemberDID.String(); + + return j; + } + + void CRCProposal::FromJsonTerminateProposalCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version) { + FromJsonTerminateProposalOwnerUnsigned(j, version); + + _signature.setHex(j[JsonKeySignature].get()); + _crCouncilMemberDID = Address(j[JsonKeyCRCouncilMemberDID].get()); + } + + bool CRCProposal::IsValidTerminateProposalOwnerUnsigned(uint8_t version) const { + if (_type != terminateProposal) { + SPVLOG_ERROR("invalid type: {}", _type); + return false; + } + + if (_categoryData.size() > 4096) { + SPVLOG_ERROR("category data exceed 4096 bytes"); + return false; + } + + try { + Key key(_ownerPublicKey); + } catch (const std::exception &e) { + SPVLOG_ERROR("invalid pubkey"); + return false; + } + + if (_draftHash == 0 || _targetProposalHash == 0) { + SPVLOG_ERROR("invalid hash"); + return false; + } + + return true; + } + + bool CRCProposal::IsValidTerminateProposalCRCouncilMemberUnsigned(uint8_t version) const { + if (!IsValidTerminateProposalOwnerUnsigned(version)) { + SPVLOG_ERROR("terminate proposal unsigned is not valid"); + return false; + } + + try { + if (!Key(_ownerPublicKey).Verify(DigestTerminateProposalOwnerUnsigned(version), _signature)) { + SPVLOG_ERROR("verify signature fail"); + return false; + } + } catch (const std::exception &e) { + SPVLOG_ERROR("verify signature exception: {}", e.what()); + return false; + } + + if (!_crCouncilMemberDID.Valid()) { + SPVLOG_ERROR("invalid cr council member did"); + return false; + } + + return true; + } + + const uint256 &CRCProposal::DigestTerminateProposalOwnerUnsigned(uint8_t version) const { + if (_digestTerminateProposalOwnerUnsigned == 0) { + ByteStream stream; + SerializeTerminateProposalUnsigned(stream, version); + _digestTerminateProposalOwnerUnsigned = sha256(stream.GetBytes()); + } + + return _digestTerminateProposalOwnerUnsigned; + } + + const uint256 &CRCProposal::DigestTerminateProposalCRCouncilMemberUnsigned(uint8_t version) const { + if (_digestTerminateProposalCRCouncilMemberUnsigned == 0) { + ByteStream stream; + SerializeTerminateProposalCRCouncilMemberUnsigned(stream, version); + _digestTerminateProposalCRCouncilMemberUnsigned = sha256(stream.GetBytes()); + } + + return _digestTerminateProposalCRCouncilMemberUnsigned; + } + + // change secretary general + void CRCProposal::SerializeSecretaryElectionUnsigned(ByteStream &stream, uint8_t version) const { + uint16_t type = _type; + stream.WriteUint16(type); + stream.WriteVarString(_categoryData); + stream.WriteVarBytes(_ownerPublicKey); + stream.WriteBytes(_draftHash); + if (version >= CRCProposalVersion01) + stream.WriteVarBytes(_draftData); + stream.WriteVarBytes(_secretaryPublicKey); + stream.WriteBytes(_secretaryDID.ProgramHash()); + } + + bool CRCProposal::DeserializeSecretaryElectionUnsigned(const ByteStream &stream, uint8_t version) { + if (!stream.ReadVarString(_categoryData)) { + SPVLOG_ERROR("deserialize category data"); + return false; + } + + if (!stream.ReadVarBytes(_ownerPublicKey)) { + SPVLOG_ERROR("deserialize owner pubkey"); + return false; + } + + if (!stream.ReadBytes(_draftHash)) { + SPVLOG_ERROR("deserialize draft hash"); + return false; + } + + if (version >= CRCProposalVersion01) { + if (!stream.ReadVarBytes(_draftData)) { + SPVLOG_ERROR("deserialize draft data"); + return false; + } + } + + if (!stream.ReadVarBytes(_secretaryPublicKey)) { + SPVLOG_ERROR("deserialize secretary pubkey"); + return false; + } + + uint168 programHash; + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize sponsor did"); + return false; + } + _secretaryDID = Address(programHash); + + return true; + } + + void CRCProposal::SerializeSecretaryElectionCRCouncilMemberUnsigned(ByteStream &stream, uint8_t version) const { + SerializeSecretaryElectionUnsigned(stream, version); + + stream.WriteVarBytes(_signature); + stream.WriteVarBytes(_secretarySignature); + stream.WriteBytes(_crCouncilMemberDID.ProgramHash()); + } + + bool CRCProposal::DeserializeSecretaryElectionCRCouncilMemberUnsigned(const ByteStream &stream, uint8_t version) { + if (!DeserializeSecretaryElectionUnsigned(stream, version)) { + SPVLOG_ERROR("deserialize change secretary secretary unsigned"); + return false; + } + + if (!stream.ReadVarBytes(_signature)) { + SPVLOG_ERROR("deserialize signature"); + return false; + } + + if (!stream.ReadVarBytes(_secretarySignature)) { + SPVLOG_ERROR("deserialize secretary signature"); + return false; + } + + uint168 programHash; + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize cr council mem did"); + return false; + } + _crCouncilMemberDID = Address(programHash); + + return true; + } + + void CRCProposal::SerializeSecretaryElection(ByteStream &stream, uint8_t version) const { + SerializeSecretaryElectionCRCouncilMemberUnsigned(stream, version); + + stream.WriteVarBytes(_crCouncilMemberSignature); + } + + bool CRCProposal::DeserializeSecretaryElection(const ByteStream &stream, uint8_t version) { + if (!DeserializeSecretaryElectionCRCouncilMemberUnsigned(stream, version)) { + return false; + } + + if (!stream.ReadVarBytes(_crCouncilMemberSignature)) { + SPVLOG_ERROR("deserialize change secretary cr council member signature"); + return false; + } + + return true; + } + + nlohmann::json CRCProposal::ToJsonSecretaryElectionUnsigned(uint8_t version) const { + nlohmann::json j; + + j[JsonKeyType] = _type; + j[JsonKeyCategoryData] = _categoryData; + j[JsonKeyOwnerPublicKey] = _ownerPublicKey.getHex(); + j[JsonKeyDraftHash] = _draftHash.GetHex(); + if (version >= CRCProposalVersion01) + j[JsonKeyDraftData] = Base64::Encode(_draftData); + j[JsonKeySecretaryPublicKey] = _secretaryPublicKey.getHex(); + j[JsonKeySecretaryDID] = _secretaryDID.String(); + + return j; + } + + void CRCProposal::FromJsonSecretaryElectionUnsigned(const nlohmann::json &j, uint8_t version) { + _type = CRCProposal::Type(j[JsonKeyType].get()); + _categoryData = j[JsonKeyCategoryData].get(); + _ownerPublicKey.setHex(j[JsonKeyOwnerPublicKey].get()); + _draftHash.SetHex(j[JsonKeyDraftHash].get()); + if (version >= CRCProposalVersion01) { + std::string draftData = j[JsonKeyDraftData].get(); + _draftData = Base64::Decode(draftData); + ErrorChecker::CheckParam(_draftData.size() > DRAFT_DATA_MAX_SIZE, Error::ProposalContentTooLarge, "proposal origin content too large"); + uint256 draftHash(sha256_2(_draftData)); + ErrorChecker::CheckParam(draftHash != _draftHash, Error::ProposalHashNotMatch, "proposal hash not match"); + } + _secretaryPublicKey.setHex(j[JsonKeySecretaryPublicKey].get()); + _secretaryDID = Address(j[JsonKeySecretaryDID].get()); + } + + nlohmann::json CRCProposal::ToJsonSecretaryElectionCRCouncilMemberUnsigned(uint8_t version) const { + nlohmann::json j; + + j = ToJsonSecretaryElectionUnsigned(version); + j[JsonKeySignature] = _signature.getHex(); + j[JsonKeySecretarySignature] = _secretarySignature.getHex(); + j[JsonKeyCRCouncilMemberDID] = _crCouncilMemberDID.String(); + + return j; + } + + void CRCProposal::FromJsonSecretaryElectionCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version) { + FromJsonSecretaryElectionUnsigned(j, version); + _signature.setHex(j[JsonKeySignature].get()); + _secretarySignature.setHex(j[JsonKeySecretarySignature].get()); + _crCouncilMemberDID = Address(j[JsonKeyCRCouncilMemberDID].get()); + } + + bool CRCProposal::IsValidSecretaryElectionUnsigned(uint8_t version) const { + if (_type != secretaryGeneralElection) { + SPVLOG_ERROR("invalid type: {}", _type); + return false; + } + + if (_categoryData.size() > 4096) { + SPVLOG_ERROR("category data exceed 4096 bytes"); + return false; + } + + try { + Key key(_ownerPublicKey); + Key key1(_secretaryPublicKey); + } catch (const std::exception &e) { + SPVLOG_ERROR("invalid ..."); + return false; + } + + if (!_secretaryDID.Valid()) { + SPVLOG_ERROR("invalid secretary did"); + return false; + } + + return true; + } + + bool CRCProposal::IsValidSecretaryElectionCRCouncilMemberUnsigned(uint8_t version) const { + if (!IsValidSecretaryElectionUnsigned(version)) { + SPVLOG_ERROR("secretary election secretary unsigned not valid"); + return false; + } + + try { + if (!Key(_ownerPublicKey).Verify(DigestSecretaryElectionUnsigned(version), _signature)) { + SPVLOG_ERROR("verify owner signature fail"); + return false; + } + if (!Key(_secretaryPublicKey).Verify(DigestSecretaryElectionUnsigned(version), _secretarySignature)) { + SPVLOG_ERROR("verify secretary signature fail"); + return false; + } + } catch (const std::exception &e) { + SPVLOG_ERROR("verify signature exception: {}", e.what()); + return false; + } + + if (!_crCouncilMemberDID.Valid()) { + SPVLOG_ERROR("invalid cr committee did"); + return false; + } + + return true; + } + + const uint256 &CRCProposal::DigestSecretaryElectionUnsigned(uint8_t version) const { + if (_digestSecretaryElectionUnsigned == 0) { + ByteStream stream; + SerializeSecretaryElectionUnsigned(stream, version); + _digestSecretaryElectionUnsigned = sha256(stream.GetBytes()); + } + + return _digestSecretaryElectionUnsigned; + } + + const uint256 &CRCProposal::DigestSecretaryElectionCRCouncilMemberUnsigned(uint8_t version) const { + if (_digestSecretaryElectionCRCouncilMemberUnsigned == 0) { + ByteStream stream; + SerializeSecretaryElectionCRCouncilMemberUnsigned(stream, version); + _digestSecretaryElectionCRCouncilMemberUnsigned = sha256(stream.GetBytes()); + } + + return _digestSecretaryElectionCRCouncilMemberUnsigned; + } + + // top serialize or deserialize + void CRCProposal::Serialize(ByteStream &stream, uint8_t version) const { + switch (_type) { + case changeProposalOwner: + SerializeChangeOwner(stream, version); + break; + + case terminateProposal: + SerializeTerminateProposal(stream, version); + break; + + case secretaryGeneralElection: + SerializeSecretaryElection(stream, version); + break; + + case elip: + case normal: + SerializeNormalOrELIP(stream, version); + break; + + default: + SPVLOG_ERROR("serialize cr proposal unknown type"); + break; + } + } + + bool CRCProposal::Deserialize(const ByteStream &stream, uint8_t version) { + uint16_t type = 0; + if (!stream.ReadUint16(type)) { + SPVLOG_ERROR("deserialize type"); + return false; + } + _type = CRCProposal::Type(type); + + bool r = false; + switch (_type) { + case changeProposalOwner: + r = DeserializeChangeOwner(stream, version); + break; + + case terminateProposal: + r = DeserializeTerminateProposal(stream, version); + break; + + case secretaryGeneralElection: + r = DeserializeSecretaryElection(stream, version); + break; + + case elip: + case normal: + r = DeserializeNormalOrELIP(stream, version); + break; + + default: + SPVLOG_ERROR("unknow type: {}", _type); + r = false; + break; + } + + return r; + } + + nlohmann::json CRCProposal::ToJsonNormalOwnerUnsigned(uint8_t version) const { + nlohmann::json j; + j[JsonKeyType] = _type; + j[JsonKeyCategoryData] = _categoryData; + j[JsonKeyOwnerPublicKey] = _ownerPublicKey.getHex(); + j[JsonKeyDraftHash] = _draftHash.GetHex(); + if (version >= CRCProposalVersion01) + j[JsonKeyDraftData] = Base64::Encode(_draftData); j[JsonKeyBudgets] = _budgets; j[JsonKeyRecipient] = _recipient.String(); return j; } - void CRCProposal::FromJsonOwnerUnsigned(const nlohmann::json &j, uint8_t version) { - _type = CRCProposal::Type(j[JsonKeyType].get()); + void CRCProposal::FromJsonNormalOwnerUnsigned(const nlohmann::json &j, uint8_t version) { + _type = CRCProposal::Type(j[JsonKeyType].get()); _categoryData = j[JsonKeyCategoryData].get(); _ownerPublicKey.setHex(j[JsonKeyOwnerPublicKey].get()); _draftHash.SetHex(j[JsonKeyDraftHash].get()); + if (version >= CRCProposalVersion01) { + std::string draftData = j[JsonKeyDraftData].get(); + _draftData = Base64::Decode(draftData); + ErrorChecker::CheckParam(_draftData.size() > DRAFT_DATA_MAX_SIZE, Error::ProposalContentTooLarge, "proposal origin content too large"); + uint256 draftHash(sha256_2(_draftData)); + ErrorChecker::CheckParam(draftHash != _draftHash, Error::ProposalHashNotMatch, "proposal hash not match"); + } _budgets = j[JsonKeyBudgets].get>(); _recipient = Address(j[JsonKeyRecipient].get()); } - nlohmann::json CRCProposal::ToJsonCRCouncilMemberUnsigned(uint8_t version) const { - nlohmann::json j = ToJsonOwnerUnsigned(version); + nlohmann::json CRCProposal::ToJsonNormalCRCouncilMemberUnsigned(uint8_t version) const { + nlohmann::json j = ToJsonNormalOwnerUnsigned(version); j[JsonKeySignature] = _signature.getHex(); j[JsonKeyCRCouncilMemberDID] = _crCouncilMemberDID.String(); return j; } - void CRCProposal::FromJsonCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version) { - FromJsonOwnerUnsigned(j, version); + void CRCProposal::FromJsonNormalCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version) { + FromJsonNormalOwnerUnsigned(j, version); _signature.setHex(j[JsonKeySignature].get()); _crCouncilMemberDID = Address(j[JsonKeyCRCouncilMemberDID].get()); } nlohmann::json CRCProposal::ToJson(uint8_t version) const { - nlohmann::json j = ToJsonCRCouncilMemberUnsigned(version); - j[JsonKeyCRCouncilMemberSignature] = _crCouncilMemberSignature.getHex(); + nlohmann::json j; + + switch (_type) { + case normal: + case elip: + j = ToJsonNormalCRCouncilMemberUnsigned(version); + j[JsonKeyCRCouncilMemberSignature] = _crCouncilMemberSignature.getHex(); + break; + + case secretaryGeneralElection: + j = ToJsonSecretaryElectionCRCouncilMemberUnsigned(version); + j[JsonKeyCRCouncilMemberSignature] = _crCouncilMemberSignature.getHex(); + break; + + case changeProposalOwner: + j = ToJsonChangeOwnerCRCouncilMemberUnsigned(version); + j[JsonKeyCRCouncilMemberSignature] = _crCouncilMemberSignature.getHex(); + break; + + case terminateProposal: + j = ToJsonTerminateProposalCRCouncilMemberUnsigned(version); + j[JsonKeyCRCouncilMemberSignature] = _crCouncilMemberSignature.getHex(); + break; + + default: + SPVLOG_ERROR("unknow type: {}", _type); + break; + } return j; } void CRCProposal::FromJson(const nlohmann::json &j, uint8_t version) { - FromJsonCRCouncilMemberUnsigned(j, version); - _crCouncilMemberSignature.setHex(j[JsonKeyCRCouncilMemberSignature].get()); + _type = CRCProposal::Type(j[JsonKeyType].get()); + + switch (_type) { + case normal: + case elip: + FromJsonNormalCRCouncilMemberUnsigned(j, version); + _crCouncilMemberSignature.setHex(j[JsonKeyCRCouncilMemberSignature].get()); + break; + + case secretaryGeneralElection: + FromJsonSecretaryElectionCRCouncilMemberUnsigned(j, version); + _crCouncilMemberSignature.setHex(j[JsonKeyCRCouncilMemberSignature].get()); + break; + + case changeProposalOwner: + FromJsonChangeOwnerCRCouncilMemberUnsigned(j, version); + _crCouncilMemberSignature.setHex(j[JsonKeyCRCouncilMemberSignature].get()); + break; + + case terminateProposal: + FromJsonTerminateProposalCRCouncilMemberUnsigned(j, version); + _crCouncilMemberSignature.setHex(j[JsonKeyCRCouncilMemberSignature].get()); + break; + + default: + SPVLOG_ERROR("unknow type: {}", _type); + break; + } } - bool CRCProposal::IsValidOwnerUnsigned(uint8_t version) const { + bool CRCProposal::IsValidNormalOwnerUnsigned(uint8_t version) const { if (_type >= CRCProposal::maxType) { SPVLOG_ERROR("invalid proposal type: {}", _type); return false; @@ -444,12 +1324,12 @@ namespace Elastos { return true; } - bool CRCProposal::IsValidCRCouncilMemberUnsigned(uint8_t version) const { - if (!IsValidOwnerUnsigned(version)) + bool CRCProposal::IsValidNormalCRCouncilMemberUnsigned(uint8_t version) const { + if (!IsValidNormalOwnerUnsigned(version)) return false; try { - if (!Key(_ownerPublicKey).Verify(DigestOwnerUnsigned(version), _signature)) { + if (!Key(_ownerPublicKey).Verify(DigestNormalOwnerUnsigned(version), _signature)) { SPVLOG_ERROR("verify owner signature fail"); return false; } @@ -467,15 +1347,35 @@ namespace Elastos { } bool CRCProposal::IsValid(uint8_t version) const { - if (!IsValidCRCouncilMemberUnsigned(version)) - return false; + bool isValid = false; + switch (_type) { + case normal: + case elip: + isValid = IsValidNormalCRCouncilMemberUnsigned(version); + break; + + case secretaryGeneralElection: + isValid = IsValidSecretaryElectionCRCouncilMemberUnsigned(version); + break; + + case changeProposalOwner: + isValid = IsValidChangeOwnerCRCouncilMemberUnsigned(version); + break; + + case terminateProposal: + isValid = IsValidTerminateProposalCRCouncilMemberUnsigned(version); + break; + + default: + break; + } if (_crCouncilMemberSignature.empty()) { SPVLOG_ERROR("cr committee not signed"); - return false; + isValid = false; } - return true; + return isValid; } CRCProposal &CRCProposal::operator=(const CRCProposal &payload) { @@ -483,15 +1383,95 @@ namespace Elastos { _categoryData = payload._categoryData; _ownerPublicKey = payload._ownerPublicKey; _draftHash = payload._draftHash; + _draftData = payload._draftData; _budgets = payload._budgets; _recipient = payload._recipient; + _targetProposalHash = payload._targetProposalHash; + _newRecipient = payload._newRecipient; + _newOwnerPublicKey = payload._newOwnerPublicKey; + _secretaryPublicKey = payload._secretaryPublicKey; + _secretaryDID = payload._secretaryDID; _signature = payload._signature; + _newOwnerSignature = payload._newOwnerSignature; + _secretarySignature = payload._secretarySignature; _crCouncilMemberDID = payload._crCouncilMemberDID; _crCouncilMemberSignature = payload._crCouncilMemberSignature; return *this; } + bool CRCProposal::operator==(const IPayload &payload) const { + bool equal = false; + try { + const CRCProposal &realPayload = dynamic_cast(payload); + + switch (_type) { + case normal: + case elip: + equal = _type == realPayload._type && + _categoryData == realPayload._categoryData && + _ownerPublicKey == realPayload._ownerPublicKey && + _draftHash == realPayload._draftHash && + _draftData == realPayload._draftData && + _budgets == realPayload._budgets && + _recipient == realPayload._recipient && + _signature == realPayload._signature && + _crCouncilMemberDID == realPayload._crCouncilMemberDID && + _crCouncilMemberSignature == realPayload._crCouncilMemberSignature; + break; + + case secretaryGeneralElection: + equal = _type == realPayload._type && + _categoryData == realPayload._categoryData && + _ownerPublicKey == realPayload._ownerPublicKey && + _draftHash == realPayload._draftHash && + _draftData == realPayload._draftData && + _secretaryPublicKey == realPayload._secretaryPublicKey && + _secretaryDID == realPayload._secretaryDID && + _signature == realPayload._signature && + _secretarySignature == realPayload._secretarySignature && + _crCouncilMemberDID == realPayload._crCouncilMemberDID && + _crCouncilMemberSignature == realPayload._crCouncilMemberSignature; + break; + + case changeProposalOwner: + equal = _type == realPayload._type && + _categoryData == realPayload._categoryData && + _ownerPublicKey == realPayload._ownerPublicKey && + _draftHash == realPayload._draftHash && + _draftData == realPayload._draftData && + _targetProposalHash == realPayload._targetProposalHash && + _newRecipient == realPayload._newRecipient && + _newOwnerPublicKey == realPayload._newOwnerPublicKey && + _signature == realPayload._signature && + _newOwnerSignature == realPayload._newOwnerSignature && + _crCouncilMemberDID == realPayload._crCouncilMemberDID && + _crCouncilMemberSignature == realPayload._crCouncilMemberSignature; + break; + + case terminateProposal: + equal = _type == realPayload._type && + _categoryData == realPayload._categoryData && + _ownerPublicKey == realPayload._ownerPublicKey && + _draftHash == realPayload._draftHash && + _draftData == realPayload._draftData && + _targetProposalHash == realPayload._targetProposalHash && + _signature == realPayload._signature && + _crCouncilMemberDID == realPayload._crCouncilMemberDID && + _crCouncilMemberSignature == realPayload._crCouncilMemberSignature; + break; + + default: + equal = false; + break; + } + } catch (const std::bad_cast &e) { + SPVLOG_ERROR("payload is not instance of CRCProposal"); + equal = false; + } + return equal; + } + IPayload &CRCProposal::operator=(const IPayload &payload) { try { const CRCProposal &crcProposal = dynamic_cast(payload); diff --git a/SDK/Plugin/Transaction/Payload/CRCProposal.h b/SDK/Plugin/Transaction/Payload/CRCProposal.h index 7b70127d0..1ecffc0a2 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposal.h +++ b/SDK/Plugin/Transaction/Payload/CRCProposal.h @@ -31,6 +31,30 @@ namespace Elastos { namespace ElaWallet { #define CRCProposalDefaultVersion 0 +#define CRCProposalVersion01 0x01 + +#define JsonKeyType "Type" +#define JsonKeyStage "Stage" +#define JsonKeyAmount "Amount" + +#define JsonKeyType "Type" +#define JsonKeyCategoryData "CategoryData" +#define JsonKeyOwnerPublicKey "OwnerPublicKey" +#define JsonKeyDraftHash "DraftHash" +#define JsonKeyDraftData "DraftData" +#define JsonKeyBudgets "Budgets" +#define JsonKeyRecipient "Recipient" +#define JsonKeyTargetProposalHash "TargetProposalHash" +#define JsonKeyNewRecipient "NewRecipient" +#define JsonKeyNewOwnerPublicKey "NewOwnerPublicKey" +#define JsonKeySecretaryPublicKey "SecretaryGeneralPublicKey" +#define JsonKeySecretaryDID "SecretaryGeneralDID" +#define JsonKeySignature "Signature" +#define JsonKeyNewOwnerSignature "NewOwnerSignature" +#define JsonKeySecretarySignature "SecretaryGeneralSignature" +#define JsonKeyCRCouncilMemberDID "CRCouncilMemberDID" +#define JsonKeyCRCouncilMemberSignature "CRCouncilMemberSignature" + class Budget : public JsonSerializer { public: enum Type { @@ -61,6 +85,8 @@ namespace Elastos { void FromJson(const nlohmann::json &j) override; + bool operator==(const Budget &budget) const; + private: Budget::Type _type; uint8_t _stage; @@ -72,15 +98,15 @@ namespace Elastos { enum Type { normal = 0x0000, elip = 0x0100, -// flowElip = 0x0101, -// infoElip = 0x0102, -// mainChainUpgradeCode = 0x0200, -// sideChainUpgradeCode = 0x0300, -// registerSideChain = 0x0301, -// secretaryGeneral = 0x0400, -// changeSponsor = 0x0401, -// closeProposal = 0x0402, -// dappConsensus = 0x0500, + flowElip = 0x0101, + infoElip = 0x0102, + mainChainUpgradeCode = 0x0200, + sideChainUpgradeCode = 0x0300, + registerSideChain = 0x0301, + secretaryGeneralElection = 0x0400, + changeProposalOwner = 0x0401, + terminateProposal = 0x0402, + dappConsensus = 0x0500, maxType }; @@ -100,10 +126,6 @@ namespace Elastos { const bytes_t &GetOwnerPublicKey() const; - void SetCRCouncilMemberDID(const Address &crSponsorDID); - - const Address &GetCRCouncilMemberDID() const; - void SetDraftHash(const uint256 &draftHash); const uint256 &GetDraftHash() const; @@ -116,48 +138,174 @@ namespace Elastos { const Address &GetRecipient() const; + void SetTargetProposalHash(const uint256 &hash); + + const uint256 &GetTargetProposalHash() const; + + void SetNewRecipient(const Address &recipient); + + const Address &GetNewRecipient() const; + + void SetNewOwnerPublicKey(const bytes_t &pubkey); + + const bytes_t &GetNewOwnerPublicKey() const; + + void SetSecretaryPublicKey(const bytes_t &pubkey); + + const bytes_t GetSecretaryPublicKey() const; + + void SetSecretaryDID(const Address &did); + + const Address &GetSecretaryDID() const; + void SetSignature(const bytes_t &signature); const bytes_t &GetSignature() const; - void SetCRCouncilMemberSignature(const bytes_t &signature); + void SetNewOwnerSignature(const bytes_t &sign); - const bytes_t &GetCRCouncilMemberSignature() const; + const bytes_t &GetNewOwnerSignature() const; - const uint256 &DigestOwnerUnsigned(uint8_t version) const; + void SetSecretarySignature(const bytes_t &sign); - const uint256 &DigestCRCouncilMemberUnsigned(uint8_t version) const; + const bytes_t &GetSecretarySignature() const; - public: - size_t EstimateSize(uint8_t version) const override; + void SetCRCouncilMemberDID(const Address &crSponsorDID); - void SerializeOwnerUnsigned(ByteStream &ostream, uint8_t version) const; + const Address &GetCRCouncilMemberDID() const; + + void SetCRCouncilMemberSignature(const bytes_t &signature); - bool DeserializeOwnerUnsigned(const ByteStream &istream, uint8_t version); + const bytes_t &GetCRCouncilMemberSignature() const; + + public: + // normal or elip + void SerializeOwnerUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeOwnerUnsigned(const ByteStream &stream, uint8_t version); void SerializeCRCouncilMemberUnsigned(ByteStream &ostream, uint8_t version) const; bool DeserializeCRCouncilMemberUnsigned(const ByteStream &istream, uint8_t version); - void Serialize(ByteStream &ostream, uint8_t version) const override; + void SerializeNormalOrELIP(ByteStream &stream, uint8_t version) const; - bool Deserialize(const ByteStream &istream, uint8_t version) override; + bool DeserializeNormalOrELIP(const ByteStream &stream, uint8_t version); - nlohmann::json ToJsonOwnerUnsigned(uint8_t version) const; + nlohmann::json ToJsonNormalOwnerUnsigned(uint8_t version) const; - void FromJsonOwnerUnsigned(const nlohmann::json &j, uint8_t version); + void FromJsonNormalOwnerUnsigned(const nlohmann::json &j, uint8_t version); - nlohmann::json ToJsonCRCouncilMemberUnsigned(uint8_t version) const; + nlohmann::json ToJsonNormalCRCouncilMemberUnsigned(uint8_t version) const; - void FromJsonCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version); + void FromJsonNormalCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version); - nlohmann::json ToJson(uint8_t version) const override; + bool IsValidNormalOwnerUnsigned(uint8_t version) const; - void FromJson(const nlohmann::json &j, uint8_t version) override; + bool IsValidNormalCRCouncilMemberUnsigned(uint8_t version) const; + + const uint256 &DigestNormalOwnerUnsigned(uint8_t version) const; + + const uint256 &DigestNormalCRCouncilMemberUnsigned(uint8_t version) const; + + // change owner + void SerializeChangeOwnerUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeChangeOwnerUnsigned(const ByteStream &stream, uint8_t version); + + void SerializeChangeOwnerCRCouncilMemberUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeChangeOwnerCRCouncilMemberUnsigned(const ByteStream &stream, uint8_t version); + + void SerializeChangeOwner(ByteStream &stream, uint8_t version) const; + + bool DeserializeChangeOwner(const ByteStream &stream, uint8_t version); + + nlohmann::json ToJsonChangeOwnerUnsigned(uint8_t version) const; + + void FromJsonChangeOwnerUnsigned(const nlohmann::json &j, uint8_t version); + + nlohmann::json ToJsonChangeOwnerCRCouncilMemberUnsigned(uint8_t version) const; + + void FromJsonChangeOwnerCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version); - bool IsValidOwnerUnsigned(uint8_t version) const; + bool IsValidChangeOwnerUnsigned(uint8_t version) const; - bool IsValidCRCouncilMemberUnsigned(uint8_t version) const; + bool IsValidChangeOwnerCRCouncilMemberUnsigned(uint8_t version) const; + + const uint256 &DigestChangeOwnerUnsigned(uint8_t version) const; + + const uint256 &DigestChangeOwnerCRCouncilMemberUnsigned(uint8_t version) const; + + // terminate proposal + void SerializeTerminateProposalUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeTerminateProposalUnsigned(const ByteStream &stream, uint8_t version); + + void SerializeTerminateProposalCRCouncilMemberUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeTerminateProposalCRCouncilMemberUnsigned(const ByteStream &stream, uint8_t version); + + void SerializeTerminateProposal(ByteStream &stream, uint8_t version) const; + + bool DeserializeTerminateProposal(const ByteStream &stream, uint8_t version); + + nlohmann::json ToJsonTerminateProposalOwnerUnsigned(uint8_t version) const; + + void FromJsonTerminateProposalOwnerUnsigned(const nlohmann::json &j, uint8_t version); + + nlohmann::json ToJsonTerminateProposalCRCouncilMemberUnsigned(uint8_t version) const; + + void FromJsonTerminateProposalCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version); + + bool IsValidTerminateProposalOwnerUnsigned(uint8_t version) const; + + bool IsValidTerminateProposalCRCouncilMemberUnsigned(uint8_t version) const; + + const uint256 &DigestTerminateProposalOwnerUnsigned(uint8_t version) const; + + const uint256 &DigestTerminateProposalCRCouncilMemberUnsigned(uint8_t version) const; + + // secretary election + void SerializeSecretaryElectionUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeSecretaryElectionUnsigned(const ByteStream &stream, uint8_t verion); + + void SerializeSecretaryElectionCRCouncilMemberUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeSecretaryElectionCRCouncilMemberUnsigned(const ByteStream &stream, uint8_t version); + + void SerializeSecretaryElection(ByteStream &stream, uint8_t version) const; + + bool DeserializeSecretaryElection(const ByteStream &stream, uint8_t version); + + nlohmann::json ToJsonSecretaryElectionUnsigned(uint8_t version) const; + + void FromJsonSecretaryElectionUnsigned(const nlohmann::json &j, uint8_t version); + + nlohmann::json ToJsonSecretaryElectionCRCouncilMemberUnsigned(uint8_t version) const; + + void FromJsonSecretaryElectionCRCouncilMemberUnsigned(const nlohmann::json &j, uint8_t version); + + bool IsValidSecretaryElectionUnsigned(uint8_t version) const; + + bool IsValidSecretaryElectionCRCouncilMemberUnsigned(uint8_t version) const; + + const uint256 &DigestSecretaryElectionUnsigned(uint8_t version) const; + + const uint256 &DigestSecretaryElectionCRCouncilMemberUnsigned(uint8_t version) const; + + // override interface + size_t EstimateSize(uint8_t version) const override; + + // top serialize or deserialize + void Serialize(ByteStream &stream, uint8_t version) const override; + + bool Deserialize(const ByteStream &stream, uint8_t version) override; + + nlohmann::json ToJson(uint8_t version) const override; + + void FromJson(const nlohmann::json &j, uint8_t version) override; bool IsValid(uint8_t version) const override; @@ -165,18 +313,41 @@ namespace Elastos { CRCProposal &operator=(const CRCProposal &payload); + bool operator==(const IPayload &payload) const; + private: + // normal & elip mutable uint256 _digestOwnerUnsigned; mutable uint256 _digestCRCouncilMemberUnsigned; + // secretary election + mutable uint256 _digestSecretaryElectionUnsigned; + mutable uint256 _digestSecretaryElectionCRCouncilMemberUnsigned; + + // change owner + mutable uint256 _digestChangeOwnerUnsigned; + mutable uint256 _digestChangeOwnerCRCouncilMemberUnsigned; + + // terminate proposal + mutable uint256 _digestTerminateProposalOwnerUnsigned; + mutable uint256 _digestTerminateProposalCRCouncilMemberUnsigned; + private: CRCProposal::Type _type; std::string _categoryData; bytes_t _ownerPublicKey; uint256 _draftHash; + bytes_t _draftData; std::vector _budgets; Address _recipient; + uint256 _targetProposalHash; + Address _newRecipient; + bytes_t _newOwnerPublicKey; + bytes_t _secretaryPublicKey; + Address _secretaryDID; bytes_t _signature; + bytes_t _newOwnerSignature; + bytes_t _secretarySignature; // cr council member did Address _crCouncilMemberDID; diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalRealWithdraw.cpp b/SDK/Plugin/Transaction/Payload/CRCProposalRealWithdraw.cpp new file mode 100644 index 000000000..7c2983980 --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/CRCProposalRealWithdraw.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "CRCProposalRealWithdraw.h" +#include "Common/Log.h" + +namespace Elastos { + namespace ElaWallet { + + CRCProposalRealWithdraw::~CRCProposalRealWithdraw() { + + } + + CRCProposalRealWithdraw::CRCProposalRealWithdraw() { + + } + + size_t CRCProposalRealWithdraw::EstimateSize(uint8_t version) const { + ByteStream stream; + size_t size = 0; + + size += sizeof(uint16_t); + + size += stream.WriteVarUint(_withdrawTxHashes.size()); + // 32 == sizeof(uint256) + size += _withdrawTxHashes.size() * 32; + + return size; + } + + void CRCProposalRealWithdraw::Serialize(ByteStream &stream, uint8_t version) const { + stream.WriteVarUint(_withdrawTxHashes.size()); + for (size_t i = 0; i < _withdrawTxHashes.size(); ++i) + stream.WriteBytes(_withdrawTxHashes[i]); + } + + bool CRCProposalRealWithdraw::Deserialize(const ByteStream &stream, uint8_t version) { + uint64_t size = 0; + if (!stream.ReadVarUint(size)) { + SPVLOG_ERROR("deserialize proposal real withdraw size"); + return false; + } + + uint256 hash; + for (size_t i = 0; i < size; ++i) { + if (!stream.ReadBytes(hash)) { + SPVLOG_ERROR("deserialize proposal real withdraw hash"); + return false; + } + _withdrawTxHashes.push_back(hash); + } + + return true; + } + + nlohmann::json CRCProposalRealWithdraw::ToJson(uint8_t version) const { + nlohmann::json jarray = nlohmann::json::array(); + nlohmann::json j; + + for (const uint256 &u : _withdrawTxHashes) + jarray.push_back(u.GetHex()); + + j["WithdrawTxHashes"] = jarray; + return j; + } + + void CRCProposalRealWithdraw::FromJson(const nlohmann::json &j, uint8_t version) { + nlohmann::json jarray = j["WithdrawTxHashes"]; + if (!jarray.is_array()) { + SPVLOG_DEBUG("json is not array"); + return; + } + + _withdrawTxHashes.clear(); + uint256 u; + for (nlohmann::json::iterator it = jarray.begin(); it != jarray.end(); ++it) { + u.SetHex((*it).get()); + _withdrawTxHashes.push_back(u); + } + } + + bool CRCProposalRealWithdraw::IsValid(uint8_t version) const { + return !_withdrawTxHashes.empty(); + } + + IPayload &CRCProposalRealWithdraw::operator=(const IPayload &payload) { + try { + const CRCProposalRealWithdraw &crcProposal = dynamic_cast(payload); + operator=(crcProposal); + } catch (const std::bad_cast &e) { + SPVLOG_ERROR("payload is not instance of CRCProposalRealWithdraw"); + } + return *this; + } + + CRCProposalRealWithdraw &CRCProposalRealWithdraw::operator=(const CRCProposalRealWithdraw &payload) { + _withdrawTxHashes = payload._withdrawTxHashes; + return *this; + } + + } +} \ No newline at end of file diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalRealWithdraw.h b/SDK/Plugin/Transaction/Payload/CRCProposalRealWithdraw.h new file mode 100644 index 000000000..e074fba0b --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/CRCProposalRealWithdraw.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ELASTOS_SDK_CRCPROPOSALREALWITHDRAW_H__ +#define __ELASTOS_SDK_CRCPROPOSALREALWITHDRAW_H__ + +#include "IPayload.h" + +namespace Elastos { + namespace ElaWallet { + + class CRCProposalRealWithdraw : public IPayload { + public: + ~CRCProposalRealWithdraw(); + + CRCProposalRealWithdraw(); + + public: + size_t EstimateSize(uint8_t version) const override; + + void Serialize(ByteStream &stream, uint8_t version) const override; + + bool Deserialize(const ByteStream &stream, uint8_t version) override; + + nlohmann::json ToJson(uint8_t version) const override; + + void FromJson(const nlohmann::json &j, uint8_t version) override; + + bool IsValid(uint8_t version) const override; + + IPayload &operator=(const IPayload &payload) override; + + CRCProposalRealWithdraw &operator=(const CRCProposalRealWithdraw &payload); + + private: + std::vector _withdrawTxHashes; + }; + + } +} + +#endif diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalReview.cpp b/SDK/Plugin/Transaction/Payload/CRCProposalReview.cpp index a8d0ab13f..5747293b8 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposalReview.cpp +++ b/SDK/Plugin/Transaction/Payload/CRCProposalReview.cpp @@ -22,15 +22,13 @@ #include "CRCProposalReview.h" #include #include +#include +#include namespace Elastos { namespace ElaWallet { -#define JsonKeyProposalHash "ProposalHash" -#define JsonKeyVoteResult "VoteResult" -#define JsonKeyOpinionHash "OpinionHash" -#define JsonKeyDID "DID" -#define JsonKeySignature "Signature" +#define OPINION_DATA_MAX_SIZE (1024 * 1024) CRCProposalReview::CRCProposalReview() { @@ -103,33 +101,42 @@ namespace Elastos { return size; } - void CRCProposalReview::SerializeUnsigned(ByteStream &ostream, uint8_t version) const { - ostream.WriteBytes(_proposalHash); - ostream.WriteUint8(_voteResult); - ostream.WriteBytes(_opinionHash); - ostream.WriteBytes(_did.ProgramHash()); + void CRCProposalReview::SerializeUnsigned(ByteStream &stream, uint8_t version) const { + stream.WriteBytes(_proposalHash); + stream.WriteUint8(_voteResult); + stream.WriteBytes(_opinionHash); + if (version >= CRCProposalReviewVersion01) + stream.WriteVarBytes(_opinionData); + stream.WriteBytes(_did.ProgramHash()); } - bool CRCProposalReview::DeserializeUnsigned(const ByteStream &istream, uint8_t version) { - if (!istream.ReadBytes(_proposalHash)) { + bool CRCProposalReview::DeserializeUnsigned(const ByteStream &stream, uint8_t version) { + if (!stream.ReadBytes(_proposalHash)) { SPVLOG_ERROR("deserialize proposal hash"); return false; } uint8_t opinion = 0; - if (!istream.ReadUint8(opinion)) { + if (!stream.ReadUint8(opinion)) { SPVLOG_ERROR("deserialize opinion"); return false; } _voteResult = VoteResult(opinion); - if (!istream.ReadBytes(_opinionHash)) { - SPVLOG_ERROR("deesrialize opinion hash"); + if (!stream.ReadBytes(_opinionHash)) { + SPVLOG_ERROR("desrialize opinion hash"); return false; } + if (version >= CRCProposalReviewVersion01) { + if (!stream.ReadVarBytes(_opinionData)) { + SPVLOG_ERROR("deserialize opinion data"); + return false; + } + } + uint168 programHash; - if (!istream.ReadBytes(programHash)) { + if (!stream.ReadBytes(programHash)) { SPVLOG_ERROR("deserialize did"); return false; } @@ -162,6 +169,8 @@ namespace Elastos { j[JsonKeyProposalHash] = _proposalHash.GetHex(); j[JsonKeyVoteResult] = _voteResult; j[JsonKeyOpinionHash] = _opinionHash.GetHex(); + if (version >= CRCProposalReviewVersion01) + j[JsonKeyOpinionData] = Base64::Encode(_opinionData); j[JsonKeyDID] = _did.String(); return j; } @@ -170,6 +179,13 @@ namespace Elastos { _proposalHash.SetHex(j[JsonKeyProposalHash].get()); _voteResult = VoteResult(j[JsonKeyVoteResult].get()); _opinionHash.SetHex(j[JsonKeyOpinionHash].get()); + if (version >= CRCProposalReviewVersion01) { + std::string opinionData = j[JsonKeyOpinionData].get(); + _opinionData = Base64::Decode(opinionData); + ErrorChecker::CheckParam(_opinionData.size() > OPINION_DATA_MAX_SIZE, Error::ProposalContentTooLarge, "opinion hash too large"); + uint256 opinionHash(sha256_2(_opinionData)); + ErrorChecker::CheckParam(opinionHash != _opinionHash, Error::ProposalHashNotMatch, "opinion hash not match"); + } _did = Address(j[JsonKeyDID].get()); } @@ -224,6 +240,7 @@ namespace Elastos { _proposalHash = payload._proposalHash; _voteResult = payload._voteResult; _opinionHash = payload._opinionHash; + _opinionData = payload._opinionData; _did = payload._did; _signature = payload._signature; return *this; diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalReview.h b/SDK/Plugin/Transaction/Payload/CRCProposalReview.h index df9bf1cf6..4f304693e 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposalReview.h +++ b/SDK/Plugin/Transaction/Payload/CRCProposalReview.h @@ -29,6 +29,15 @@ namespace Elastos { namespace ElaWallet { #define CRCProposalReviewDefaultVersion 0 +#define CRCProposalReviewVersion01 0x01 + +#define JsonKeyProposalHash "ProposalHash" +#define JsonKeyVoteResult "VoteResult" +#define JsonKeyOpinionHash "OpinionHash" +#define JsonKeyOpinionData "OpinionData" +#define JsonKeyDID "DID" +#define JsonKeySignature "Signature" + class CRCProposalReview : public IPayload { public: enum VoteResult { @@ -67,9 +76,9 @@ namespace Elastos { public: virtual size_t EstimateSize(uint8_t version) const; - void SerializeUnsigned(ByteStream &ostream, uint8_t version) const; + void SerializeUnsigned(ByteStream &stream, uint8_t version) const; - bool DeserializeUnsigned(const ByteStream &istream, uint8_t version); + bool DeserializeUnsigned(const ByteStream &stream, uint8_t version); virtual void Serialize(ByteStream &ostream, uint8_t version) const; @@ -98,6 +107,7 @@ namespace Elastos { uint256 _proposalHash; VoteResult _voteResult; uint256 _opinionHash; + bytes_t _opinionData; Address _did; bytes_t _signature; }; diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalTracking.cpp b/SDK/Plugin/Transaction/Payload/CRCProposalTracking.cpp index 605d01784..54c3c00c7 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposalTracking.cpp +++ b/SDK/Plugin/Transaction/Payload/CRCProposalTracking.cpp @@ -24,20 +24,14 @@ #include #include #include +#include +#include namespace Elastos { namespace ElaWallet { -#define JsonKeyProposalHash "ProposalHash" -#define JsonKeyMessageHash "MessageHash" -#define JsonKeyStage "Stage" -#define JsonKeyOwnerPublicKey "OwnerPublicKey" -#define JsonKeyNewOwnerPublicKey "NewOwnerPublicKey" -#define JsonKeyOwnerSignature "OwnerSignature" -#define JsonKeyNewOwnerSignature "NewOwnerSignature" -#define JsonKeyType "Type" -#define JsonKeySecretaryGeneralOpinionHash "SecretaryGeneralOpinionHash" -#define JsonKeySecretaryGeneralSignature "SecretaryGeneralSignature" +#define MESSAGE_DATA_MAX_SIZE (800 * 1024) +#define OPINION_DATA_MAX_SIZE (200 * 1024) CRCProposalTracking::CRCProposalTracking() { @@ -188,12 +182,14 @@ namespace Elastos { return size; } - void CRCProposalTracking::SerializeOwnerUnsigned(ByteStream &ostream, uint8_t version) const { - ostream.WriteBytes(_proposalHash); - ostream.WriteBytes(_messageHash); - ostream.WriteUint8(_stage); - ostream.WriteVarBytes(_ownerPubKey); - ostream.WriteVarBytes(_newOwnerPubKey); + void CRCProposalTracking::SerializeOwnerUnsigned(ByteStream &stream, uint8_t version) const { + stream.WriteBytes(_proposalHash); + stream.WriteBytes(_messageHash); + if (version >= CRCProposalTrackingVersion01) + stream.WriteVarBytes(_messageData); + stream.WriteUint8(_stage); + stream.WriteVarBytes(_ownerPubKey); + stream.WriteVarBytes(_newOwnerPubKey); } bool CRCProposalTracking::DeserializeOwnerUnsigned(const ByteStream &stream, uint8_t version) { @@ -207,6 +203,13 @@ namespace Elastos { return false; } + if (version >= CRCProposalTrackingVersion01) { + if (!stream.ReadVarBytes(_messageData)) { + SPVLOG_ERROR("deserialize msg data"); + return false; + } + } + if (!stream.ReadUint8(_stage)) { SPVLOG_ERROR("deserialize stage"); return false; @@ -245,12 +248,11 @@ namespace Elastos { void CRCProposalTracking::SerializeSecretaryUnsigned(ByteStream &stream, uint8_t version) const { SerializeNewOwnerUnsigned(stream, version); - stream.WriteVarBytes(_newOwnerSign); - stream.WriteUint8(_type); - stream.WriteBytes(_secretaryGeneralOpinionHash); + if (version >= CRCProposalTrackingVersion01) + stream.WriteVarBytes(_secretaryGeneralOpinionData); } bool CRCProposalTracking::DeserializeSecretaryUnsigned(const ByteStream &stream, uint8_t version) { @@ -274,6 +276,13 @@ namespace Elastos { return false; } + if (version >= CRCProposalTrackingVersion01) { + if (!stream.ReadVarBytes(_secretaryGeneralOpinionData)) { + SPVLOG_ERROR("deserialize secretary opinion data"); + return false; + } + } + return true; } @@ -301,6 +310,8 @@ namespace Elastos { nlohmann::json j; j[JsonKeyProposalHash] = _proposalHash.GetHex(); j[JsonKeyMessageHash] = _messageHash.GetHex(); + if (version >= CRCProposalTrackingVersion01) + j[JsonKeyMessageData] = Base64::Encode(_messageData); j[JsonKeyStage] = _stage; j[JsonKeyOwnerPublicKey] = _ownerPubKey.getHex(); j[JsonKeyNewOwnerPublicKey] = _newOwnerPubKey.getHex(); @@ -310,6 +321,13 @@ namespace Elastos { void CRCProposalTracking::FromJsonOwnerUnsigned(const nlohmann::json &j, uint8_t version) { _proposalHash.SetHex(j[JsonKeyProposalHash].get()); _messageHash.SetHex(j[JsonKeyMessageHash].get()); + if (version >= CRCProposalTrackingVersion01) { + std::string messageData = j[JsonKeyMessageData].get(); + _messageData = Base64::Decode(messageData); + ErrorChecker::CheckParam(_messageData.size() > MESSAGE_DATA_MAX_SIZE, Error::ProposalContentTooLarge, "message data size too large"); + uint256 messageHash(sha256_2(_messageData)); + ErrorChecker::CheckParam(messageHash != _messageHash, Error::ProposalHashNotMatch, "message hash not match"); + } _stage = j[JsonKeyStage].get(); _ownerPubKey.setHex(j[JsonKeyOwnerPublicKey].get()); _newOwnerPubKey.setHex(j[JsonKeyNewOwnerPublicKey].get()); @@ -331,6 +349,8 @@ namespace Elastos { j[JsonKeyNewOwnerSignature] = _newOwnerSign.getHex(); j[JsonKeyType] = _type; j[JsonKeySecretaryGeneralOpinionHash] = _secretaryGeneralOpinionHash.GetHex(); + if (version >= CRCProposalTrackingVersion01) + j[JsonKeySecretaryGeneralOpinionData] = Base64::Encode(_secretaryGeneralOpinionData); return j; } @@ -339,6 +359,13 @@ namespace Elastos { _newOwnerSign.setHex(j[JsonKeyNewOwnerSignature].get()); _type = CRCProposalTracking::Type(j[JsonKeyType].get()); _secretaryGeneralOpinionHash.SetHex(j[JsonKeySecretaryGeneralOpinionHash].get()); + if (version >= CRCProposalTrackingVersion01) { + std::string data = j[JsonKeySecretaryGeneralOpinionData].get(); + _secretaryGeneralOpinionData = Base64::Decode(data); + ErrorChecker::CheckParam(_secretaryGeneralOpinionData.size() > OPINION_DATA_MAX_SIZE, Error::ProposalContentTooLarge, "opinion data size too large"); + uint256 opinionHash(sha256_2(_secretaryGeneralOpinionData)); + ErrorChecker::CheckParam(opinionHash != _secretaryGeneralOpinionHash, Error::ProposalHashNotMatch, "opinion hash not match"); + } } nlohmann::json CRCProposalTracking::ToJson(uint8_t version) const { @@ -447,6 +474,7 @@ namespace Elastos { CRCProposalTracking &CRCProposalTracking::operator=(const CRCProposalTracking &payload) { _proposalHash = payload._proposalHash; _messageHash = payload._messageHash; + _messageData = payload._messageData; _stage = payload._stage; _ownerPubKey = payload._ownerPubKey; _newOwnerPubKey = payload._newOwnerPubKey; @@ -454,6 +482,7 @@ namespace Elastos { _newOwnerSign = payload._newOwnerSign; _type = payload._type; _secretaryGeneralOpinionHash = payload._secretaryGeneralOpinionHash; + _secretaryGeneralOpinionData = payload._secretaryGeneralOpinionData; _secretaryGeneralSignature = payload._secretaryGeneralSignature; return *this; } diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalTracking.h b/SDK/Plugin/Transaction/Payload/CRCProposalTracking.h index 062118870..37b27af80 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposalTracking.h +++ b/SDK/Plugin/Transaction/Payload/CRCProposalTracking.h @@ -28,6 +28,20 @@ namespace Elastos { namespace ElaWallet { #define CRCProposalTrackingDefaultVersion 0 +#define CRCProposalTrackingVersion01 0x01 + +#define JsonKeyProposalHash "ProposalHash" +#define JsonKeyMessageHash "MessageHash" +#define JsonKeyMessageData "MessageData" +#define JsonKeyStage "Stage" +#define JsonKeyOwnerPublicKey "OwnerPublicKey" +#define JsonKeyNewOwnerPublicKey "NewOwnerPublicKey" +#define JsonKeyOwnerSignature "OwnerSignature" +#define JsonKeyNewOwnerSignature "NewOwnerSignature" +#define JsonKeyType "Type" +#define JsonKeySecretaryGeneralOpinionHash "SecretaryGeneralOpinionHash" +#define JsonKeySecretaryGeneralOpinionData "SecretaryGeneralOpinionData" +#define JsonKeySecretaryGeneralSignature "SecretaryGeneralSignature" class CRCProposalTracking : public IPayload { public: @@ -145,6 +159,7 @@ namespace Elastos { private: uint256 _proposalHash; uint256 _messageHash; + bytes_t _messageData; uint8_t _stage; bytes_t _ownerPubKey; bytes_t _newOwnerPubKey; @@ -152,6 +167,7 @@ namespace Elastos { bytes_t _newOwnerSign; CRCProposalTracking::Type _type; uint256 _secretaryGeneralOpinionHash; + bytes_t _secretaryGeneralOpinionData; bytes_t _secretaryGeneralSignature; }; diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.cpp b/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.cpp index 32cd93431..bd63201a8 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.cpp +++ b/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.cpp @@ -31,6 +31,8 @@ namespace Elastos { #define JsonKeyProposalHash "ProposalHash" #define JsonKeyOwnerPubkey "OwnerPublicKey" +#define JsonKeyRecipient "Recipient" +#define JsonKeyAmount "Amount" #define JsonKeySignature "Signature" CRCProposalWithdraw::CRCProposalWithdraw() { @@ -83,6 +85,10 @@ namespace Elastos { size += stream.WriteVarUint(_ownerPubkey.size()); size += _ownerPubkey.size(); size += stream.WriteVarUint(_signature.size()); + if (version == CRCProposalWithdrawVersion_01) { + size += _recipient.ProgramHash().size(); + size += sizeof(uint64_t); + } size += _signature.size(); return size; @@ -91,6 +97,11 @@ namespace Elastos { void CRCProposalWithdraw::SerializeUnsigned(ByteStream &stream, uint8_t version) const { stream.WriteBytes(_proposalHash); stream.WriteVarBytes(_ownerPubkey); + + if (version == CRCProposalWithdrawVersion_01) { + stream.WriteBytes(_recipient.ProgramHash()); + stream.WriteUint64(_amount.getUint64()); + } } void CRCProposalWithdraw::Serialize(ByteStream &stream, uint8_t version) const { @@ -109,6 +120,22 @@ namespace Elastos { return false; } + if (version == CRCProposalWithdrawVersion_01) { + uint168 programHash; + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize recipient"); + return false; + } + _recipient = Address(programHash); + + uint64_t amount; + if (!stream.ReadUint64(amount)) { + SPVLOG_ERROR("deserialize amount"); + return false; + } + _amount.setUint64(amount); + } + return true; } @@ -128,6 +155,10 @@ namespace Elastos { nlohmann::json j; j[JsonKeyProposalHash] = _proposalHash.GetHex(); j[JsonKeyOwnerPubkey] = _ownerPubkey.getHex(); + if (version == CRCProposalWithdrawVersion_01) { + j[JsonKeyRecipient] = _recipient.String(); + j[JsonKeyAmount] = _amount.getDec(); + } return j; } @@ -141,6 +172,10 @@ namespace Elastos { void CRCProposalWithdraw::FromJsonUnsigned(const nlohmann::json &j, uint8_t version) { _proposalHash.SetHex(j[JsonKeyProposalHash].get()); _ownerPubkey.setHex(j[JsonKeyOwnerPubkey].get()); + if (version == CRCProposalWithdrawVersion_01) { + _recipient = Address(j[JsonKeyRecipient].get()); + _amount.setDec(j[JsonKeyAmount].get()); + } } void CRCProposalWithdraw::FromJson(const nlohmann::json &j, uint8_t version) { @@ -148,7 +183,7 @@ namespace Elastos { _signature.setHex(j[JsonKeySignature].get()); } - bool CRCProposalWithdraw::IsValidUnsigned(uint8_t versin) const { + bool CRCProposalWithdraw::IsValidUnsigned(uint8_t version) const { try { Key key(_ownerPubkey); } catch (const std::exception &e) { @@ -156,6 +191,18 @@ namespace Elastos { return false; } + if (version == CRCProposalWithdrawVersion_01) { + if (!_recipient.Valid()) { + SPVLOG_ERROR("invalid recipient"); + return false; + } + + if (_amount <= 0) { + SPVLOG_ERROR("invalid amount"); + return false; + } + } + return true; } @@ -189,6 +236,8 @@ namespace Elastos { CRCProposalWithdraw &CRCProposalWithdraw::operator=(const CRCProposalWithdraw &payload) { _proposalHash = payload._proposalHash; _ownerPubkey = payload._ownerPubkey; + _recipient = payload._recipient; + _amount = payload._amount; _signature = payload._signature; return *this; } diff --git a/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.h b/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.h index 7fa0d0e48..263eb5e67 100644 --- a/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.h +++ b/SDK/Plugin/Transaction/Payload/CRCProposalWithdraw.h @@ -23,12 +23,15 @@ #ifndef __ELASTOS_SPVSDK_CRCPROPOSALWITHDRAW_H__ #define __ELASTOS_SPVSDK_CRCPROPOSALWITHDRAW_H__ +#include +#include #include "IPayload.h" namespace Elastos { namespace ElaWallet { #define CRCProposalWithdrawVersion 0 +#define CRCProposalWithdrawVersion_01 0x01 class CRCProposalWithdraw : public IPayload { public: @@ -69,7 +72,7 @@ namespace Elastos { virtual void FromJson(const nlohmann::json &j, uint8_t version); - bool IsValidUnsigned(uint8_t versin) const; + bool IsValidUnsigned(uint8_t version) const; virtual bool IsValid(uint8_t version) const; @@ -83,6 +86,8 @@ namespace Elastos { private: uint256 _proposalHash; bytes_t _ownerPubkey; + Address _recipient; + BigInt _amount; bytes_t _signature; }; diff --git a/SDK/Plugin/Transaction/Payload/CRCouncilMemberClaimNode.cpp b/SDK/Plugin/Transaction/Payload/CRCouncilMemberClaimNode.cpp new file mode 100644 index 000000000..29abacce7 --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/CRCouncilMemberClaimNode.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "CRCouncilMemberClaimNode.h" + +namespace Elastos { + namespace ElaWallet { + +#define JsonKeyNodePublicKey "NodePublicKey" +#define JsonKeyCRCouncilMemberDID "CRCouncilMemberDID" +#define JsonKeyCRCouncilMemberSignature "CRCouncilMemberSignature" + + CRCouncilMemberClaimNode::CRCouncilMemberClaimNode() { + + } + + CRCouncilMemberClaimNode::~CRCouncilMemberClaimNode() { + + } + + size_t CRCouncilMemberClaimNode::EstimateSize(uint8_t version) const { + ByteStream stream; + size_t size = 0; + + size += stream.WriteVarUint(_nodePublicKey.size()); + size += _nodePublicKey.size(); + size += _crCouncilMemberDID.ProgramHash().size(); + size += stream.WriteVarUint(_crCouncilMemberSignature.size()); + size += _crCouncilMemberSignature.size(); + + return size; + } + + void CRCouncilMemberClaimNode::Serialize(ByteStream &stream, uint8_t version) const { + SerializeUnsigned(stream, version); + stream.WriteVarBytes(_crCouncilMemberSignature); + } + + bool CRCouncilMemberClaimNode::Deserialize(const ByteStream &stream, uint8_t version) { + if (!DeserializeUnsigned(stream, version)) { + SPVLOG_ERROR("deserialize unsigned fail"); + return false; + } + + if (!stream.ReadVarBytes(_crCouncilMemberSignature)) { + SPVLOG_ERROR("deserialize signature fail"); + return false; + } + + return true; + } + + nlohmann::json CRCouncilMemberClaimNode::ToJson(uint8_t version) const { + nlohmann::json j = ToJsonUnsigned(version); + j[JsonKeyCRCouncilMemberSignature] = _crCouncilMemberSignature.getHex(); + return j; + } + + void CRCouncilMemberClaimNode::FromJson(const nlohmann::json &j, uint8_t version) { + FromJsonUnsigned(j, version); + _crCouncilMemberSignature.setHex(j[JsonKeyCRCouncilMemberSignature].get()); + } + + bool CRCouncilMemberClaimNode::IsValid(uint8_t version) const { + if (!IsValidUnsigned(version)) { + SPVLOG_ERROR("unsigned is not valid"); + return false; + } + + if (_crCouncilMemberSignature.empty()) { + SPVLOG_ERROR("invalid signature"); + return false; + } + + return true; + } + + IPayload &CRCouncilMemberClaimNode::operator=(const IPayload &payload) { + try { + const CRCouncilMemberClaimNode &realPayload= dynamic_cast(payload); + operator=(realPayload); + } catch (const std::bad_cast &e) { + SPVLOG_ERROR("payload is not instance of CRCouncilMemberClaimNode"); + } + return *this; + } + + CRCouncilMemberClaimNode &CRCouncilMemberClaimNode::operator=(const CRCouncilMemberClaimNode &payload) { + _digestUnsigned = payload._digestUnsigned; + _nodePublicKey = payload._nodePublicKey; + _crCouncilMemberDID = payload._crCouncilMemberDID; + _crCouncilMemberSignature = payload._crCouncilMemberSignature; + return *this; + } + + bool CRCouncilMemberClaimNode::operator==(const IPayload &payload) const { + bool equal = false; + + try { + const CRCouncilMemberClaimNode &realPayload= dynamic_cast(payload); + equal = _nodePublicKey == realPayload._nodePublicKey && + _crCouncilMemberDID == realPayload._crCouncilMemberDID && + _crCouncilMemberSignature == realPayload._crCouncilMemberSignature; + } catch (const std::bad_cast &e) { + SPVLOG_ERROR("payload is not instance of CRCouncilMemberClaimNode"); + equal = false; + } + + return equal; + } + + void CRCouncilMemberClaimNode::SerializeUnsigned(ByteStream &stream, uint8_t version) const { + stream.WriteVarBytes(_nodePublicKey); + stream.WriteBytes(_crCouncilMemberDID.ProgramHash()); + } + + bool CRCouncilMemberClaimNode::DeserializeUnsigned(const ByteStream &stream, uint8_t version) { + if (!stream.ReadVarBytes(_nodePublicKey)) { + SPVLOG_ERROR("deserialize node pubkey"); + return false; + } + + uint168 programHash; + if (!stream.ReadBytes(programHash)) { + SPVLOG_ERROR("deserialize cr council member did"); + return false; + } + _crCouncilMemberDID = Address(programHash); + + return true; + } + + nlohmann::json CRCouncilMemberClaimNode::ToJsonUnsigned(uint8_t version) const { + nlohmann::json j; + + j[JsonKeyNodePublicKey] = _nodePublicKey.getHex(); + j[JsonKeyCRCouncilMemberDID] = _crCouncilMemberDID.String(); + + return j; + } + + void CRCouncilMemberClaimNode::FromJsonUnsigned(const nlohmann::json &j, uint8_t version) { + _nodePublicKey = j[JsonKeyNodePublicKey].get(); + std::string did = j[JsonKeyCRCouncilMemberDID].get(); + _crCouncilMemberDID = Address(did); + } + + bool CRCouncilMemberClaimNode::IsValidUnsigned(uint8_t version) const { + try { + Key key(_nodePublicKey); + } catch (const std::exception &e) { + SPVLOG_ERROR("invalid node pubkey"); + return false; + } + + if (!_crCouncilMemberDID.Valid()) { + SPVLOG_ERROR("invalid cr council member did"); + return false; + } + + return true; + } + + const uint256 &CRCouncilMemberClaimNode::DigestUnsigned(uint8_t version) const { + if (_digestUnsigned == 0) { + ByteStream stream; + SerializeUnsigned(stream, version); + _digestUnsigned = sha256(stream.GetBytes()); + } + + return _digestUnsigned; + } + + void CRCouncilMemberClaimNode::SetNodePublicKey(const bytes_t &pubkey) { + _nodePublicKey = pubkey; + } + + void CRCouncilMemberClaimNode::SetCRCouncilMemberDID(const Address &did) { + _crCouncilMemberDID = did; + } + + void CRCouncilMemberClaimNode::SetCRCouncilMemberSignature(const bytes_t &signature) { + _crCouncilMemberSignature = signature; + } + + } +} \ No newline at end of file diff --git a/SDK/Plugin/Transaction/Payload/CRCouncilMemberClaimNode.h b/SDK/Plugin/Transaction/Payload/CRCouncilMemberClaimNode.h new file mode 100644 index 000000000..7956e76c8 --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/CRCouncilMemberClaimNode.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ELASTOS_SPVSDK_CRCOUNCILMEMBERCLAIMNODE_H__ +#define __ELASTOS_SPVSDK_CRCOUNCILMEMBERCLAIMNODE_H__ + +#include +#include "IPayload.h" + +namespace Elastos { + namespace ElaWallet { + +#define CRCouncilMemberClaimNodeVersion 0 + + class CRCouncilMemberClaimNode : public IPayload { + public: + CRCouncilMemberClaimNode(); + + ~CRCouncilMemberClaimNode(); + + virtual size_t EstimateSize(uint8_t version) const; + + virtual void Serialize(ByteStream &stream, uint8_t version) const; + + virtual bool Deserialize(const ByteStream &stream, uint8_t version); + + virtual nlohmann::json ToJson(uint8_t version) const; + + virtual void FromJson(const nlohmann::json &j, uint8_t version); + + virtual bool IsValid(uint8_t version) const; + + virtual IPayload &operator=(const IPayload &payload); + + CRCouncilMemberClaimNode &operator=(const CRCouncilMemberClaimNode &payload); + + bool operator==(const IPayload &payload) const; + + public: + void SerializeUnsigned(ByteStream &stream, uint8_t version) const; + + bool DeserializeUnsigned(const ByteStream &stream, uint8_t version); + + nlohmann::json ToJsonUnsigned(uint8_t version) const; + + void FromJsonUnsigned(const nlohmann::json &j, uint8_t version); + + bool IsValidUnsigned(uint8_t version) const; + + const uint256 &DigestUnsigned(uint8_t version) const; + + void SetNodePublicKey(const bytes_t &pubkey); + + void SetCRCouncilMemberDID(const Address &did); + + void SetCRCouncilMemberSignature(const bytes_t &signature); + + private: + mutable uint256 _digestUnsigned; + + private: + bytes_t _nodePublicKey; + Address _crCouncilMemberDID; + bytes_t _crCouncilMemberSignature; + }; + + } +} + +#endif diff --git a/SDK/Plugin/Transaction/Payload/NextTurnDPoSInfo.cpp b/SDK/Plugin/Transaction/Payload/NextTurnDPoSInfo.cpp new file mode 100644 index 000000000..3b6d4f931 --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/NextTurnDPoSInfo.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "NextTurnDPoSInfo.h" +#include + +namespace Elastos { + namespace ElaWallet { + + NextTurnDPoSInfo::NextTurnDPoSInfo() : + _workingHeight(0) { + + } + + NextTurnDPoSInfo::NextTurnDPoSInfo(const NextTurnDPoSInfo &payload) { + operator=(payload); + } + + NextTurnDPoSInfo::~NextTurnDPoSInfo() { + + } + + void NextTurnDPoSInfo::SetWorkingHeight(uint32_t height) { + _workingHeight = height; + } + + uint32_t NextTurnDPoSInfo::GetWorkingHeight() const { + return _workingHeight; + } + + void NextTurnDPoSInfo::SetCRPublicKeys(const std::vector pubkeys) { + _crPublicKeys = pubkeys; + } + + std::vector NextTurnDPoSInfo::GetCRPublicKeys() const { + return _crPublicKeys; + } + + void NextTurnDPoSInfo::SetDPoSPublicKeys(const std::vector pubkeys) { + _dposPublicKeys = pubkeys; + } + + std::vector NextTurnDPoSInfo::GetDPoSPublicKeys() const { + return _dposPublicKeys; + } + + size_t NextTurnDPoSInfo::EstimateSize(uint8_t version) const { + size_t size = 0; + ByteStream stream; + + size += sizeof(_workingHeight); + size += stream.WriteVarUint(_crPublicKeys.size()); + for (size_t i = 0; i < _crPublicKeys.size(); ++i) { + size += stream.WriteVarUint(_crPublicKeys[i].size()); + size += _crPublicKeys[i].size(); + } + + size += stream.WriteVarUint(_dposPublicKeys.size()); + for (size_t i = 0; i < _dposPublicKeys.size(); ++i) { + size += stream.WriteVarUint(_dposPublicKeys[i].size()); + size += _dposPublicKeys[i].size(); + } + + return size; + } + + void NextTurnDPoSInfo::Serialize(ByteStream &stream, uint8_t version) const { + stream.WriteUint32(_workingHeight); + stream.WriteVarUint(_crPublicKeys.size()); + for (size_t i = 0; i < _crPublicKeys.size(); ++i) + stream.WriteVarBytes(_crPublicKeys[i]); + stream.WriteVarUint(_dposPublicKeys.size()); + for (size_t i = 0; i < _dposPublicKeys.size(); ++i) + stream.WriteVarBytes(_dposPublicKeys[i]); + } + + bool NextTurnDPoSInfo::Deserialize(const ByteStream &stream, uint8_t version) { + if (!stream.ReadUint32(_workingHeight)) { + Log::error("deserialize working height"); + return false; + } + + uint64_t len = 0; + if (!stream.ReadVarUint(len)) { + Log::error("deserialize crPubKey length"); + return false; + } + for (size_t i = 0; i < len; ++i) { + bytes_t pubkey; + if (!stream.ReadVarBytes(pubkey)) { + Log::error("deserialize crPubKeys"); + return false; + } + _crPublicKeys.push_back(pubkey); + } + + len = 0; + if (!stream.ReadVarUint(len)) { + Log::error("deserialize dpos pubkey length"); + return false; + } + for (size_t i = 0; i < len; ++i) { + bytes_t pubkey; + if (!stream.ReadVarBytes(pubkey)) { + Log::error("deserialize dpos pubkey"); + return false; + } + _dposPublicKeys.push_back(pubkey); + } + return true; + } + + nlohmann::json NextTurnDPoSInfo::ToJson(uint8_t version) const { + nlohmann::json j, crPubKeys = nlohmann::json::array(), dposPubKeys = nlohmann::json::array(); + + for (size_t i = 0; i < _crPublicKeys.size(); ++i) + crPubKeys.push_back(_crPublicKeys[i].getHex()); + + for (size_t i = 0; i < _dposPublicKeys.size(); ++i) + dposPubKeys.push_back(_dposPublicKeys[i].getHex()); + + j["WorkingHeight"] = _workingHeight; + j["CRPublicKeys"] = crPubKeys; + j["DPoSPublicKeys"] = dposPubKeys; + + return j; + } + + void NextTurnDPoSInfo::FromJson(const nlohmann::json &j, uint8_t version) { + _workingHeight = j["WorkingHeight"].get(); + + nlohmann::json crPubKeys = j["CRPublicKeys"]; + nlohmann::json dposPubKeys = j["DPoSPublicKeys"]; + for (nlohmann::json::iterator it = crPubKeys.begin(); it != crPubKeys.end(); ++it) { + bytes_t pubkey; + pubkey.setHex((*it).get()); + _crPublicKeys.push_back(pubkey); + } + + for (nlohmann::json::iterator it = dposPubKeys.begin(); it != dposPubKeys.end(); ++it) { + bytes_t pubkey; + pubkey.setHex((*it).get()); + _dposPublicKeys.push_back(pubkey); + } + } + + IPayload &NextTurnDPoSInfo::operator=(const IPayload &payload) { + try { + const NextTurnDPoSInfo &p= dynamic_cast(payload); + operator=(p); + } catch (const std::bad_cast &e) { + Log::error("payload is not instance of NextTurnDPoSInfo"); + } + + return *this; + } + + NextTurnDPoSInfo &NextTurnDPoSInfo::operator=(const NextTurnDPoSInfo &payload) { + _workingHeight = payload._workingHeight; + _crPublicKeys = payload._crPublicKeys; + _dposPublicKeys = payload._dposPublicKeys; + return *this; + } + + } +} \ No newline at end of file diff --git a/SDK/Plugin/Transaction/Payload/NextTurnDPoSInfo.h b/SDK/Plugin/Transaction/Payload/NextTurnDPoSInfo.h new file mode 100644 index 000000000..1403306e9 --- /dev/null +++ b/SDK/Plugin/Transaction/Payload/NextTurnDPoSInfo.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ELASTOS_SPVSDK_NEXTTURNDPOSINFO_H__ +#define __ELASTOS_SPVSDK_NEXTTURNDPOSINFO_H__ + +#include + +namespace Elastos { + namespace ElaWallet { + + class NextTurnDPoSInfo : public IPayload { + public: + NextTurnDPoSInfo(); + + NextTurnDPoSInfo(const NextTurnDPoSInfo &payload); + + ~NextTurnDPoSInfo(); + + void SetWorkingHeight(uint32_t height); + + uint32_t GetWorkingHeight() const; + + void SetCRPublicKeys(const std::vector pubkeys); + + std::vector GetCRPublicKeys() const; + + void SetDPoSPublicKeys(const std::vector pubkeys); + + std::vector GetDPoSPublicKeys() const; + + virtual size_t EstimateSize(uint8_t version) const; + + virtual void Serialize(ByteStream &ostream, uint8_t version) const; + + virtual bool Deserialize(const ByteStream &istream, uint8_t version); + + virtual nlohmann::json ToJson(uint8_t version) const; + + virtual void FromJson(const nlohmann::json &j, uint8_t version); + + virtual IPayload &operator=(const IPayload &payload); + + NextTurnDPoSInfo &operator=(const NextTurnDPoSInfo &payload); + + private: + uint32_t _workingHeight; + std::vector _crPublicKeys; + std::vector _dposPublicKeys; + }; + + } +} + +#endif diff --git a/SDK/Plugin/Transaction/Transaction.cpp b/SDK/Plugin/Transaction/Transaction.cpp index c18bd8d7a..63422635e 100644 --- a/SDK/Plugin/Transaction/Transaction.cpp +++ b/SDK/Plugin/Transaction/Transaction.cpp @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -165,7 +169,8 @@ namespace Elastos { return _type == registerCR || _type == unregisterCR || _type == updateCR || - _type == returnCRDepositCoin; + _type == returnCRDepositCoin || + _type == crCouncilMemberClaimNode; } bool Transaction::IsProposalTransaction() const { @@ -326,9 +331,6 @@ namespace Elastos { if (_type == rechargeToSideChain || _type == coinBase) return true; - if (_type == crcProposalWithdraw) - return _payload->IsValid(CRCProposalWithdrawVersion); - if (_programs.size() == 0) return false; @@ -676,13 +678,14 @@ namespace Elastos { return fee; } - nlohmann::json Transaction::GetSummary(const WalletPtr &wallet, uint32_t confirms, bool detail) { + nlohmann::json Transaction::GetSummary(const WalletPtr &wallet, const std::map &genesisAddresses, uint32_t confirms, bool detail) { std::string addr; nlohmann::json summary, outputPayload; std::vector outputPayloads; std::string direction = "Received"; BigInt inputAmount(0), outputAmount(0), changeAmount(0); uint64_t fee = 0; + std::string topUpSidechain; std::map::iterator it; std::map txInput = wallet->TransactionsForInputs(_inputs); @@ -722,6 +725,12 @@ namespace Elastos { for (OutputArray::iterator o = _outputs.begin(); o != _outputs.end(); ++o) { const BigInt &oAmount = (*o)->Amount(); addr = (*o)->Addr()->String(); + for (std::map::const_iterator it = genesisAddresses.cbegin(); it != genesisAddresses.cend(); ++it) { + if (addr == it->second) { + topUpSidechain = it->first; + break; + } + } if ((*o)->GetType() == TransactionOutput::VoteOutput) { outputPayload = (*o)->GetPayload()->ToJson(); @@ -776,6 +785,7 @@ namespace Elastos { summary["Direction"] = direction; summary["Amount"] = amount.getDec(); summary["Type"] = GetTransactionType(); + summary["TopUpSidechain"] = topUpSidechain; summary["Height"] = GetBlockHeight(); if (detail) { std::string memo; @@ -852,6 +862,8 @@ namespace Elastos { payload = PayloadPtr(new CancelProducer()); } else if (type == returnDepositCoin) { payload = PayloadPtr(new ReturnDepositCoin()); + } else if (type == nextTurnDPOSInfo) { + payload = PayloadPtr(new NextTurnDPoSInfo()); } else if (type == registerCR || type == updateCR) { payload = PayloadPtr(new CRInfo()); } else if (type == unregisterCR) { @@ -866,6 +878,12 @@ namespace Elastos { payload = PayloadPtr(new CRCProposalTracking()); } else if (type == crcProposalWithdraw) { payload = PayloadPtr(new CRCProposalWithdraw()); + } else if (type == crcProposalRealWithdraw) { + payload = PayloadPtr(new CRCProposalRealWithdraw()); + } else if (type == crcAssetsRectify) { + payload = PayloadPtr(new CRCAssetsRectify()); + } else if (type == crCouncilMemberClaimNode) { + payload = PayloadPtr(new CRCouncilMemberClaimNode()); } return payload; diff --git a/SDK/Plugin/Transaction/Transaction.h b/SDK/Plugin/Transaction/Transaction.h index 32337782a..971dbd022 100644 --- a/SDK/Plugin/Transaction/Transaction.h +++ b/SDK/Plugin/Transaction/Transaction.h @@ -54,6 +54,7 @@ namespace Elastos { IllegalSidechainEvidence = 0x11, InactiveArbitrators = 0x12, UpdateVersion = 0x13, + nextTurnDPOSInfo = 0x14, registerCR = 0x21, unregisterCR = 0x22, @@ -65,6 +66,9 @@ namespace Elastos { crcProposalTracking = 0x27, crcAppropriation = 0x28, crcProposalWithdraw = 0x29, + crcProposalRealWithdraw = 0x2a, + crcAssetsRectify = 0x2b, + crCouncilMemberClaimNode = 0x31, TypeMaxCount }; @@ -189,7 +193,7 @@ namespace Elastos { const std::vector &GetPrograms() const; - nlohmann::json GetSummary(const WalletPtr &wallet, uint32_t confirms, bool detail); + nlohmann::json GetSummary(const WalletPtr &wallet, const std::map &genesisAddresses, uint32_t confirms, bool detail); uint8_t GetPayloadVersion() const; diff --git a/SDK/SpvService/LocalStore.cpp b/SDK/SpvService/LocalStore.cpp index 50e6367f5..2940aad38 100644 --- a/SDK/SpvService/LocalStore.cpp +++ b/SDK/SpvService/LocalStore.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -81,6 +80,15 @@ namespace Elastos { if (j.find("ethscPrimaryPubKey") != j.end()) { _ethscPrimaryPubKey = j["ethscPrimaryPubKey"].get(); + bool isEmpty = true; + for (size_t i = 2; i < _ethscPrimaryPubKey.length(); ++i) { + if (_ethscPrimaryPubKey[i] != '0') { + isEmpty = false; + break; + } + } + if (isEmpty || _ethscPrimaryPubKey[0] != '0' || _ethscPrimaryPubKey[1] != '4') + _ethscPrimaryPubKey.clear(); } else { _ethscPrimaryPubKey.clear(); } @@ -381,9 +389,9 @@ namespace Elastos { _subWalletsInfoList.push_back(info); } - void LocalStore::RemoveSubWalletInfo(const CoinInfoPtr &info) { + void LocalStore::RemoveSubWalletInfo(const std::string &chainID) { for (std::vector::iterator it = _subWalletsInfoList.begin(); it != _subWalletsInfoList.end(); ++it) { - if (info->GetChainID() == (*it)->GetChainID()) { + if (chainID == (*it)->GetChainID()) { _subWalletsInfoList.erase(it); break; } diff --git a/SDK/SpvService/LocalStore.h b/SDK/SpvService/LocalStore.h index a80a2837e..72f838835 100644 --- a/SDK/SpvService/LocalStore.h +++ b/SDK/SpvService/LocalStore.h @@ -109,7 +109,7 @@ namespace Elastos { void AddSubWalletInfoList(const CoinInfoPtr &info); - void RemoveSubWalletInfo(const CoinInfoPtr &info); + void RemoveSubWalletInfo(const std::string &chainID); void SetSubWalletInfoList(const std::vector &infoList); diff --git a/SDK/Wallet/GroupedAsset.cpp b/SDK/Wallet/GroupedAsset.cpp index f87a30a60..83ee75ee3 100644 --- a/SDK/Wallet/GroupedAsset.cpp +++ b/SDK/Wallet/GroupedAsset.cpp @@ -163,7 +163,7 @@ namespace Elastos { tx->AddAttribute(AttributePtr(new Attribute(Attribute::Memo, bytes_t(memo.c_str(), memo.size())))); { - boost::mutex::scoped_lock scopedLock(_parent->GetLock()); + _parent->GetLock().lock(); for (UTXOSet::iterator u = _utxosDeposit.begin(); u != _utxosDeposit.end(); ++u) { if (_parent->IsUTXOSpending(*u) || (*u)->Index() != 0) @@ -179,11 +179,15 @@ namespace Elastos { bytes_t code; std::string path; inputAddr = (*u)->Output()->Addr(); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } tx->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); } } feeAmount = CalculateFee(_parent->_feePerKb, tx->EstimateSize()); + _parent->GetLock().unlock(); } // boost::mutex::scoped_lock if (tx->GetInputs().empty()) { @@ -248,11 +252,11 @@ namespace Elastos { std::vector oldVoteAmount; { - boost::mutex::scoped_lock scopedLock(_parent->GetLock()); + _parent->GetLock().lock(); for (UTXOSet::const_iterator u = _utxosVote.cbegin(); u != _utxosVote.cend(); ++u) { if ((*u)->GetConfirms(_parent->_blockHeight) < 2 || _parent->IsUTXOSpending(*u)) { + _parent->GetLock().unlock(); ErrorChecker::ThrowLogicException(Error::LastVoteConfirming, "Last vote tx is pending"); - return nullptr; } PayloadVote *pv = dynamic_cast((*u)->Output()->GetPayload().get()); @@ -274,14 +278,14 @@ namespace Elastos { oldVoteContent.push_back(vc); if (vc.GetType() == VoteContent::CRC || vc.GetType() == VoteContent::CRCImpeachment) { - oldVoteAmount.push_back(BigInt(vc.GetTotalVoteAmount())); + oldVoteAmount.emplace_back(vc.GetTotalVoteAmount()); } else if (vc.GetType() == VoteContent::Delegate || vc.GetType() == VoteContent::CRCProposal) { - oldVoteAmount.push_back(BigInt(vc.GetMaxVoteAmount())); + oldVoteAmount.emplace_back(vc.GetMaxVoteAmount()); } else { + _parent->GetLock().unlock(); ErrorChecker::ThrowLogicException(Error::LastVoteConfirming, "Invalid vote content type"); - return nullptr; } if (oldVoteAmount.back() > totalOutputAmount) totalOutputAmount = oldVoteAmount.back(); @@ -312,7 +316,10 @@ namespace Elastos { firstInput = *u; tx->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } tx->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); } feeAmount = CalculateFee(_parent->_feePerKb, tx->EstimateSize()); @@ -336,7 +343,10 @@ namespace Elastos { if ((*u)->GetConfirms(_parent->_blockHeight) < 2) continue; tx->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } tx->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); txSize = tx->EstimateSize(); @@ -348,13 +358,14 @@ namespace Elastos { firstInput = *u; if (txSize >= TX_MAX_SIZE) { // transaction size-in-bytes too large + _parent->GetLock().unlock(); BigInt maxAmount = totalInputAmount - feeAmount; - ErrorChecker::CheckCondition(true, Error::CreateTransactionExceedSize, + ErrorChecker::ThrowParamException(Error::CreateTransactionExceedSize, "Tx size too large, max available amount: " + maxAmount.getDec() + " sela"); - return nullptr; } } + _parent->GetLock().unlock(); } // boost::mutex::scoped_lock VoteContentArray newVoteContent; @@ -434,7 +445,7 @@ namespace Elastos { tx->AddAttribute(AttributePtr(new Attribute(Attribute::Memo, bytes_t(memo.c_str(), memo.size())))); { - boost::mutex::scoped_lock scopedLock(_parent->GetLock()); + _parent->GetLock().lock(); UTXOArray utxo2Pick(_utxos.begin(), _utxos.end()); std::sort(utxo2Pick.begin(), utxo2Pick.end(), [](const UTXOPtr &a, const UTXOPtr &b) { return a->Output()->Amount() > b->Output()->Amount(); @@ -457,7 +468,10 @@ namespace Elastos { tx->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); bytes_t code; std::string path; - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } tx->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); totalInputAmount += (*u)->Output()->Amount(); @@ -466,6 +480,7 @@ namespace Elastos { if (_asset->GetName() == "ELA") feeAmount = CalculateFee(_parent->_feePerKb, txSize); } + _parent->GetLock().unlock(); } // boost::mutex::scoped_lock if (totalInputAmount <= feeAmount) { @@ -497,6 +512,7 @@ namespace Elastos { const AddressPtr &fromAddress, const std::string &memo, bool max, + const BigInt &fee, bool pickVoteFirst) { ErrorChecker::CheckLogic(outputs.empty(), Error::InvalidArgument, "outputs should not be empty"); ErrorChecker::CheckParam(max && outputs.size() > 1, Error::InvalidArgument, @@ -504,7 +520,8 @@ namespace Elastos { TransactionPtr txn = TransactionPtr(new Transaction(type, payload)); BigInt totalOutputAmount(0), totalInputAmount(0); - uint64_t txSize = 0, feeAmount = 0; + uint64_t txSize = 0; + BigInt feeAmount = 0; bytes_t code; std::string path; bool lastUTXOPending = false; @@ -519,11 +536,16 @@ namespace Elastos { txn->SetOutputs(outputs); { - boost::mutex::scoped_lock scopedLock(_parent->GetLock()); - if (_asset->GetName() == "ELA") - feeAmount = CalculateFee(_parent->_feePerKb, txn->EstimateSize()); + _parent->GetLock().lock(); + if (_asset->GetName() == "ELA") { + if (fee <= 0) { + feeAmount.setUint64(CalculateFee(_parent->_feePerKb, txn->EstimateSize())); + } else { + feeAmount = fee; + } + } - if (pickVoteFirst && totalInputAmount < totalOutputAmount + feeAmount) { + if (pickVoteFirst && (max || totalInputAmount < totalOutputAmount + feeAmount)) { // voted utxo for (UTXOSet::iterator u = _utxosVote.begin(); u != _utxosVote.end(); ++u) { if (_parent->IsUTXOSpending(*u)) { @@ -535,13 +557,19 @@ namespace Elastos { continue; txn->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } txn->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); totalInputAmount += (*u)->Output()->Amount(); txSize = txn->EstimateSize(); - if (_asset->GetName() == "ELA") - feeAmount = CalculateFee(_parent->_feePerKb, txSize); + if (_asset->GetName() == "ELA") { + if (fee <= 0) { + feeAmount.setUint64(CalculateFee(_parent->_feePerKb, txn->EstimateSize())); + } + } } } @@ -567,24 +595,32 @@ namespace Elastos { if ((*u)->GetConfirms(_parent->_blockHeight) < 2) continue; txn->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } txn->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); txSize = txn->EstimateSize(); if (txSize >= TX_MAX_SIZE) { // transaction size-in-bytes too large - if (!pickVoteFirst) + _parent->GetLock().unlock(); + if (!pickVoteFirst && !_utxosVote.empty()) { return CreateTxForOutputs(type, payload, outputs, fromAddress, memo, max, !pickVoteFirst); + } BigInt maxAmount = totalInputAmount - feeAmount; - ErrorChecker::CheckCondition(true, Error::CreateTransactionExceedSize, + ErrorChecker::ThrowParamException(Error::CreateTransactionExceedSize, "Tx size too large, max available amount: " + maxAmount.getDec() + " sela"); return nullptr; } totalInputAmount += (*u)->Output()->Amount(); - if (_asset->GetName() == "ELA") - feeAmount = CalculateFee(_parent->_feePerKb, txSize); + if (_asset->GetName() == "ELA") { + if (fee <= 0) { + feeAmount.setUint64(CalculateFee(_parent->_feePerKb, txn->EstimateSize())); + } + } } if (!pickVoteFirst && (max || totalInputAmount < totalOutputAmount + feeAmount)) { @@ -599,15 +635,22 @@ namespace Elastos { continue; txn->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } txn->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); totalInputAmount += (*u)->Output()->Amount(); txSize = txn->EstimateSize(); - if (_asset->GetName() == "ELA") - feeAmount = CalculateFee(_parent->_feePerKb, txSize); + if (_asset->GetName() == "ELA") { + if (fee <= 0) { + feeAmount.setUint64(CalculateFee(_parent->_feePerKb, txn->EstimateSize())); + } + } } } + _parent->GetLock().unlock(); } // boost::mutex::scoped_lock if (max) { @@ -637,7 +680,7 @@ namespace Elastos { BigInt changeAmount = totalInputAmount - totalOutputAmount - feeAmount; txn->AddOutput(OutputPtr(new TransactionOutput(changeAmount, *addresses[0], assetID))); } - txn->SetFee(feeAmount); + txn->SetFee(feeAmount.getUint64()); } return txn; @@ -663,7 +706,7 @@ namespace Elastos { } { - boost::mutex::scoped_lock scopedLock(_parent->GetLock()); + _parent->GetLock().lock(); txSize = tx->EstimateSize(); feeAmount = CalculateFee(_parent->_feePerKb, txSize); @@ -687,15 +730,18 @@ namespace Elastos { if ((*u)->GetConfirms(_parent->_blockHeight) < 2) continue; tx->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } tx->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); txSize = tx->EstimateSize(); if (txSize > TX_MAX_SIZE) { // transaction size-in-bytes too large - ErrorChecker::CheckCondition(true, Error::CreateTransactionExceedSize, + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::CreateTransactionExceedSize, "Tx size too large, max available amount for fee: " + totalInputAmount.getDec() + " sela"); - break; } totalInputAmount += (*u)->Output()->Amount(); @@ -714,7 +760,10 @@ namespace Elastos { continue; tx->AddInput(InputPtr(new TransactionInput((*u)->Hash(), (*u)->Index()))); - _parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path); + if (!_parent->_subAccount->GetCodeAndPath((*u)->Output()->Addr(), code, path)) { + _parent->GetLock().unlock(); + ErrorChecker::ThrowParamException(Error::Address, "Can't found code and path for input"); + } tx->AddUniqueProgram(ProgramPtr(new Program(path, code, bytes_t()))); totalInputAmount += (*u)->Output()->Amount(); @@ -723,6 +772,7 @@ namespace Elastos { feeAmount = CalculateFee(_parent->_feePerKb, txSize); } } + _parent->GetLock().unlock(); } // boost::mutex::scope_lock if (totalInputAmount < feeAmount) { diff --git a/SDK/Wallet/GroupedAsset.h b/SDK/Wallet/GroupedAsset.h index 68fb7b738..349f8ee5f 100644 --- a/SDK/Wallet/GroupedAsset.h +++ b/SDK/Wallet/GroupedAsset.h @@ -66,6 +66,7 @@ namespace Elastos { const AddressPtr &fromAddress, const std::string &memo, bool max, + const BigInt &fee, bool pickVoteFirst = false); void AddFeeForTx(TransactionPtr &tx); diff --git a/SDK/Wallet/Wallet.cpp b/SDK/Wallet/Wallet.cpp index 66f0e9a88..959f8c335 100644 --- a/SDK/Wallet/Wallet.cpp +++ b/SDK/Wallet/Wallet.cpp @@ -308,7 +308,8 @@ namespace Elastos { const AddressPtr &fromAddress, const OutputArray &outputs, const std::string &memo, - bool max) { + bool max, + const BigInt &fee) { for (const OutputPtr &output : outputs) { ErrorChecker::CheckParam(!output->Addr()->Valid(), Error::CreateTransaction, "invalid receiver address"); @@ -334,7 +335,7 @@ namespace Elastos { ErrorChecker::CheckParam(!containAsset, Error::InvalidAsset, "asset not found: " + assetID.GetHex()); - TransactionPtr tx = _groupedAssets[assetID]->CreateTxForOutputs(type, payload, outputs, fromAddress, memoFixed, max); + TransactionPtr tx = _groupedAssets[assetID]->CreateTxForOutputs(type, payload, outputs, fromAddress, memoFixed, max, fee); if (assetID != Asset::GetELAAssetID()) _groupedAssets[Asset::GetELAAssetID()]->AddFeeForTx(tx); diff --git a/SDK/Wallet/Wallet.h b/SDK/Wallet/Wallet.h index ef8990137..e15e05ecf 100644 --- a/SDK/Wallet/Wallet.h +++ b/SDK/Wallet/Wallet.h @@ -21,7 +21,7 @@ #define TX_FEE_PER_KB 1000ULL // standard tx fee per kb of tx size, rounded up to nearest kb #define TX_OUTPUT_SIZE 34 // estimated size for a typical transaction output #define TX_INPUT_SIZE 148 // estimated size for a typical compact pubkey transaction input -#define TX_MAX_SIZE 120000 // no tx can be larger than this size in bytes +#define TX_MAX_SIZE (1024 * 1024) // no tx can be larger than this size in bytes #define TX_UNCONFIRMED INT32_MAX #define DEFAULT_FEE_PER_KB (10000) // 10 satoshis-per-byte #define MIN_FEE_PER_KB TX_FEE_PER_KB // bitcoind 0.12 default min-relay fee @@ -140,7 +140,7 @@ namespace Elastos { TransactionPtr CreateTransaction(uint8_t type, const PayloadPtr &payload, const AddressPtr &fromAddress, const OutputArray &outputs, - const std::string &memo, bool max = false); + const std::string &memo, bool max = false, const BigInt &fee = 0); bool ContainsTransaction(const TransactionPtr &transaction); diff --git a/Test/AccountTest.cpp b/Test/AccountTest.cpp index 7c7cd2018..34a44ecdd 100644 --- a/Test/AccountTest.cpp +++ b/Test/AccountTest.cpp @@ -115,4 +115,40 @@ TEST_CASE("Account test", "[Account]") { } } + SECTION("Reset password") { + std::string mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + std::string passphrase = "1234Abcd"; + std::string payPasswd = "12345678"; + + AccountPtr account(new Account("Data/resetpasswd", mnemonic, passphrase, payPasswd, false)); + account->Save(); + + REQUIRE_NOTHROW(account->VerifyPayPassword(payPasswd)); + REQUIRE_NOTHROW(account->GetSeed(payPasswd)); + REQUIRE_NOTHROW(account->ExportMnemonic(payPasswd)); + REQUIRE_NOTHROW(account->GetxPrvKeyString(payPasswd)); + REQUIRE_NOTHROW(account->ExportKeystore(payPasswd)); + REQUIRE_NOTHROW(account->VerifyPassPhrase(passphrase, payPasswd)); + + payPasswd = "Abcd1234"; + REQUIRE_NOTHROW(account->ResetPassword(mnemonic, passphrase, payPasswd)); + + REQUIRE_NOTHROW(account->VerifyPayPassword(payPasswd)); + REQUIRE_NOTHROW(account->GetSeed(payPasswd)); + REQUIRE_NOTHROW(account->ExportMnemonic(payPasswd)); + REQUIRE_NOTHROW(account->GetxPrvKeyString(payPasswd)); + REQUIRE_NOTHROW(account->ExportKeystore(payPasswd)); + REQUIRE_NOTHROW(account->VerifyPassPhrase(passphrase, payPasswd)); + + payPasswd = "qqqqqqq1"; + REQUIRE_NOTHROW(account->ResetPassword(mnemonic, passphrase, payPasswd)); + + REQUIRE_NOTHROW(account->VerifyPayPassword(payPasswd)); + REQUIRE_NOTHROW(account->GetSeed(payPasswd)); + REQUIRE_NOTHROW(account->ExportMnemonic(payPasswd)); + REQUIRE_NOTHROW(account->GetxPrvKeyString(payPasswd)); + REQUIRE_NOTHROW(account->ExportKeystore(payPasswd)); + REQUIRE_NOTHROW(account->VerifyPassPhrase(passphrase, payPasswd)); + } + } diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 8d8ff3447..c866b5a2e 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -24,6 +24,15 @@ include_directories( ${PROJECT_INT_DIST_DIR}/include/catch2 ) +if(MSVC) + add_definitions(/FI"${CMAKE_CURRENT_SOURCE_DIR}/../SDK/Common/BRNameFix.h") + add_definitions(/FI"${CMAKE_CURRENT_SOURCE_DIR}/../SDK/Common/secp256k1_name_fix.h") +else() + # GCC or Clang + add_definitions(-include ${CMAKE_CURRENT_SOURCE_DIR}/../SDK/Common/BRNameFix.h) + add_definitions(-include ${CMAKE_CURRENT_SOURCE_DIR}/../SDK/Common/secp256k1_name_fix.h) +endif() + link_directories( ${PROJECT_INT_DIST_DIR}/lib ${CMAKE_CURRENT_BINARY_DIR}/../SDK @@ -34,9 +43,9 @@ foreach(src ${TEST_SOURCE_FILES}) add_executable(${TEST_TARGET_NAME} ${src}) if(SPV_ENABLE_STATIC) - target_link_libraries(${TEST_TARGET_NAME} spvsdk-static dl boost_filesystem boost_system boost_thread crypto ssl fruit sqlite3) + target_link_libraries(${TEST_TARGET_NAME} spvsdk-static dl boost_filesystem boost_system boost_thread crypto ssl fruit sqlite3 resolv) else() - target_link_libraries(${TEST_TARGET_NAME} spvsdk dl) + target_link_libraries(${TEST_TARGET_NAME} spvsdk dl resolv) endif() add_dependencies(${TEST_TARGET_NAME} libspvsdk catch2) diff --git a/Test/CRCProposalTest.cpp b/Test/CRCProposalTest.cpp index 33adece57..e7684f68d 100644 --- a/Test/CRCProposalTest.cpp +++ b/Test/CRCProposalTest.cpp @@ -7,21 +7,32 @@ #include #include "TestHelper.h" +#include +#include #include +#include +#include +#include +#include using namespace Elastos::ElaWallet; -static void initCRCProposal(CRCProposal &crcProposal) { - CRCProposal::Type type = CRCProposal::Type(getRandUInt8() % 6); - crcProposal.SetTpye(type); +static void initCRCProposal(CRCProposal &crcProposal, CRCProposal::Type type) { + std::string mnemonic = Mnemonic(boost::filesystem::path("Data")).Create("English", Mnemonic::WORDS_12); + uint512 seed = BIP39::DeriveSeed(mnemonic, ""); + HDSeed hdseed(seed.bytes()); + HDKeychain rootkey(hdseed.getExtendedKey(true)); + HDKeychain masterKey = rootkey.getChild("44'/0'/0'"); + HDKeychain ownerKey = masterKey.getChild("0/0"); + HDKeychain newOwnerKey = masterKey.getChild("0/1"); + HDKeychain secretaryKey = masterKey.getChild("0/2"); + HDKeychain councilMemberKey = masterKey.getChild("0/3"); + uint8_t version = CRCProposalDefaultVersion; + crcProposal.SetTpye(type); crcProposal.SetCategoryData(getRandString(100)); - - std::string pubKey = "031f7a5a6bf3b2450cd9da4048d00a8ef1cb4912b5057535f65f3cc0e0c36f13b4"; - crcProposal.SetOwnerPublicKey(pubKey); - + crcProposal.SetOwnerPublicKey(ownerKey.pubkey().getHex()); crcProposal.SetDraftHash(getRanduint256()); - std::vector budgets; for (int i = 0; i < 4; ++i) { Budget::Type budgetType = Budget::Type(getRandUInt8() % Budget::maxType); @@ -29,60 +40,117 @@ static void initCRCProposal(CRCProposal &crcProposal) { budgets.push_back(budget); } crcProposal.SetBudgets(budgets); - - crcProposal.SetRecipient(Address("EPbdmxUVBzfNrVdqJzZEySyWGYeuKAeKqv")); - crcProposal.SetSignature(getRandBytes(50)); - crcProposal.SetCRCouncilMemberDID(Address("icwTktC5M6fzySQ5yU7bKAZ6ipP623apFY")); - crcProposal.SetCRCouncilMemberSignature(getRandBytes(60)); -} - -static void verifyProposal(CRCProposal &p1, CRCProposal &p2) { - REQUIRE(p1.GetType() == p2.GetType()); - REQUIRE(p1.GetCategoryData() == p2.GetCategoryData()); - REQUIRE(p1.GetOwnerPublicKey() == p2.GetOwnerPublicKey()); - REQUIRE(p1.GetDraftHash() == p2.GetDraftHash()); - - const std::vector &budgets1 = p1.GetBudgets(); - const std::vector &budgets2 = p2.GetBudgets(); - REQUIRE(budgets1.size() == budgets2.size()); - for (size_t i = 0; i < budgets1.size(); ++i) { - REQUIRE(budgets1[i].GetType() == budgets2[i].GetType()); - REQUIRE(budgets1[i].GetStage() == budgets2[i].GetStage()); - REQUIRE(budgets1[i].GetAmount() == budgets2[i].GetAmount()); + crcProposal.SetRecipient(Address(Prefix::PrefixStandard, ownerKey.pubkey())); + crcProposal.SetTargetProposalHash(getRanduint256()); + crcProposal.SetNewRecipient(Address(Prefix::PrefixStandard, newOwnerKey.pubkey())); + crcProposal.SetNewOwnerPublicKey(newOwnerKey.pubkey()); + crcProposal.SetSecretaryPublicKey(secretaryKey.pubkey()); + crcProposal.SetSecretaryDID(Address(Prefix::PrefixIDChain, secretaryKey.pubkey(), true)); + crcProposal.SetCRCouncilMemberDID(Address(Prefix::PrefixIDChain, councilMemberKey.pubkey(), true)); + + Key key; + uint256 digest; + bytes_t signature; + switch (type) { + case CRCProposal::Type::elip: + case CRCProposal::Type::normal: + digest = crcProposal.DigestNormalOwnerUnsigned(version); + key = ownerKey; + signature = key.Sign(digest); + crcProposal.SetSignature(signature); + + digest = crcProposal.DigestNormalCRCouncilMemberUnsigned(version); + key = councilMemberKey; + signature = key.Sign(digest); + crcProposal.SetCRCouncilMemberSignature(signature); + break; + + case CRCProposal::Type::secretaryGeneralElection: + digest = crcProposal.DigestSecretaryElectionUnsigned(version); + key = ownerKey; + signature = key.Sign(digest); + crcProposal.SetSignature(signature); + + key = secretaryKey; + signature = key.Sign(digest); + crcProposal.SetSecretarySignature(signature); + + digest = crcProposal.DigestSecretaryElectionCRCouncilMemberUnsigned(version); + key = councilMemberKey; + signature = key.Sign(digest); + crcProposal.SetCRCouncilMemberSignature(signature); + break; + + case CRCProposal::Type::changeProposalOwner: + digest = crcProposal.DigestChangeOwnerUnsigned(version); + key = ownerKey; + signature = key.Sign(digest); + crcProposal.SetSignature(signature); + + key = newOwnerKey; + signature = key.Sign(digest); + crcProposal.SetNewOwnerSignature(signature); + + digest = crcProposal.DigestChangeOwnerCRCouncilMemberUnsigned(version); + key = councilMemberKey; + signature = key.Sign(digest); + crcProposal.SetCRCouncilMemberSignature(signature); + break; + + case CRCProposal::Type::terminateProposal: + digest = crcProposal.DigestTerminateProposalOwnerUnsigned(version); + key = ownerKey; + signature = key.Sign(digest); + crcProposal.SetSignature(signature); + + digest = crcProposal.DigestTerminateProposalCRCouncilMemberUnsigned(version); + key = councilMemberKey; + signature = key.Sign(digest); + crcProposal.SetCRCouncilMemberSignature(signature); + break; + + default: + break; } - - REQUIRE(p1.GetRecipient() == p2.GetRecipient()); - REQUIRE(p1.GetSignature() == p2.GetSignature()); - REQUIRE(p1.GetCRCouncilMemberDID() == p2.GetCRCouncilMemberDID()); - REQUIRE(p1.GetCRCouncilMemberSignature() == p2.GetCRCouncilMemberSignature()); } TEST_CASE("CRCProposal test", "[CRCProposal]") { + Log::registerMultiLogger(); + uint8_t version = CRCProposalDefaultVersion; SECTION("Serialize and Deserialize test") { - CRCProposal p1; - initCRCProposal(p1); - - ByteStream byteStream; - p1.Serialize(byteStream, 0); - - REQUIRE(byteStream.GetBytes().size() == p1.EstimateSize(0)); - - CRCProposal p2; - REQUIRE(p2.Deserialize(byteStream, 0)); - - verifyProposal(p1, p2); + CRCProposal p1, p2; + std::vector types = { + CRCProposal::normal, + CRCProposal::secretaryGeneralElection, + CRCProposal::changeProposalOwner, + CRCProposal::terminateProposal + }; + + for (size_t i = 0; i < types.size(); ++i) { + ByteStream byteStream; + initCRCProposal(p1, types[i]); + p1.Serialize(byteStream, version); + REQUIRE(byteStream.GetBytes().size() == p1.EstimateSize(version)); + REQUIRE(p2.Deserialize(byteStream, version)); + REQUIRE(p1 == p2); + } } SECTION("ToJson FromJson test") { - CRCProposal p1; - initCRCProposal(p1); - - nlohmann::json j = p1.ToJson(0); - - CRCProposal p2; - p2.FromJson(j, 0); - - verifyProposal(p1, p2); + CRCProposal p1, p2; + std::vector types = { + CRCProposal::normal, + CRCProposal::secretaryGeneralElection, + CRCProposal::changeProposalOwner, + CRCProposal::terminateProposal + }; + + for (size_t i = 0; i < types.size(); ++i) { + initCRCProposal(p1, types[i]); + nlohmann::json j = p1.ToJson(version); + p2.FromJson(j, version); + REQUIRE(p1 == p2); + } } } \ No newline at end of file diff --git a/Test/NextTurnDPoSInfoTest.cpp b/Test/NextTurnDPoSInfoTest.cpp new file mode 100644 index 000000000..95fdecbf6 --- /dev/null +++ b/Test/NextTurnDPoSInfoTest.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define CATCH_CONFIG_MAIN + +#include +#include "TestHelper.h" +#include +#include + +using namespace Elastos::ElaWallet; + +static void InitRandNextTurnDPoSInfo(NextTurnDPoSInfo &p) { + std::vector pubkeys; + p.SetWorkingHeight(getRandUInt32()); + + for (size_t i = 0; i < 20; ++i) { + pubkeys.push_back(getRandBytes(33)); + } + p.SetCRPublicKeys(pubkeys); + + pubkeys.clear(); + for (size_t i = 0; i < 20; ++i) { + pubkeys.push_back(getRandBytes(33)); + } + p.SetDPoSPublicKeys(pubkeys); +} + +TEST_CASE("NextTurnDPoSInfo Test", "[Payload]") { + Log::registerMultiLogger(); + + SECTION("Serialize and deserialize") { + NextTurnDPoSInfo p1, p2; + + InitRandNextTurnDPoSInfo(p1); + + ByteStream stream; + p1.Serialize(stream, 0); + + REQUIRE(p2.Deserialize(stream, 0)); + + REQUIRE((p1.GetWorkingHeight() == p2.GetWorkingHeight())); + REQUIRE(p1.GetCRPublicKeys() == p2.GetCRPublicKeys()); + REQUIRE(p1.GetDPoSPublicKeys() == p2.GetDPoSPublicKeys()); + } + + SECTION("to json and from json") { + NextTurnDPoSInfo p1, p2; + + InitRandNextTurnDPoSInfo(p1); + + nlohmann::json p1Json = p1.ToJson(0); + p2.FromJson(p1Json, 0); + + REQUIRE((p1.GetWorkingHeight() == p2.GetWorkingHeight())); + REQUIRE(p1.GetCRPublicKeys() == p2.GetCRPublicKeys()); + REQUIRE(p1.GetDPoSPublicKeys() == p2.GetDPoSPublicKeys()); + } +} diff --git a/Test/PayloadCRCouncilMemberClaimNodeTest.cpp b/Test/PayloadCRCouncilMemberClaimNodeTest.cpp new file mode 100644 index 000000000..78dc11ee5 --- /dev/null +++ b/Test/PayloadCRCouncilMemberClaimNodeTest.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define CATCH_CONFIG_MAIN + +#include + +#include +#include +#include +#include +#include +#include + +static uint8_t version = CRCouncilMemberClaimNodeVersion; + +using namespace Elastos::ElaWallet; +static void initPayload(CRCouncilMemberClaimNode &payload) { + std::string mnemonic = Mnemonic(boost::filesystem::path("Data")).Create("English", Mnemonic::WORDS_12); + uint512 seed = BIP39::DeriveSeed(mnemonic, ""); + HDSeed hdseed(seed.bytes()); + HDKeychain rootkey(hdseed.getExtendedKey(true)); + HDKeychain masterKey = rootkey.getChild("44'/0'/0'"); + HDKeychain nodePublicKey = masterKey.getChild("0/0"); + HDKeychain councilMemberKey = masterKey.getChild("0/1"); + + payload.SetNodePublicKey(nodePublicKey.pubkey()); + payload.SetCRCouncilMemberDID(Address(PrefixIDChain, councilMemberKey.pubkey(), true)); + + Key key; + const uint256 &digest = payload.DigestUnsigned(version); + key = councilMemberKey; + bytes_t signature = key.Sign(digest); + payload.SetCRCouncilMemberSignature(signature); +} + +TEST_CASE("RechargeToSideChain test", "[RechargeToSideChain]") { + Log::registerMultiLogger(); + CRCouncilMemberClaimNode p1, p2; + ByteStream stream; + + initPayload(p1); + + REQUIRE(p1.IsValid(version)); + p1.Serialize(stream, version); + REQUIRE(p2.Deserialize(stream, version)); + REQUIRE(p1 == p2); + + initPayload(p1); + REQUIRE(p1.IsValid(version)); + nlohmann::json j = p1.ToJson(version); + REQUIRE_NOTHROW(p2.FromJson(j, version)); + REQUIRE(p1 == p2); +} \ No newline at end of file diff --git a/Test/SignTest.cpp b/Test/SignTest.cpp index 31abbbf0c..88d8a7e7f 100644 --- a/Test/SignTest.cpp +++ b/Test/SignTest.cpp @@ -18,9 +18,12 @@ using namespace Elastos::ElaWallet; +const std::string rootpath = "Data"; + TEST_CASE("Sign transaction test", "[SignTransaction]") { Log::registerMultiLogger(); + boost::filesystem::create_directory(rootpath); nlohmann::json content = "{\"Attributes\":[{\"Data\":\"353634383333303934\",\"Usage\":0}],\"BlockHeight\":2147483647,\"Fee\":10000,\"Inputs\":[{\"Address\":\"8Gqrkk876Kc1HUjeG9evyFsc91RGYWyQj4\",\"Amount\":200000000,\"Index\":0,\"Script\":\"76a914134a742f7782c295d3ea18cb59cd0101b21b1a2f88ac\",\"Sequence\":4294967295,\"Signature\":\"\",\"TxHash\":\"e77c3bea963d124311076d4737372cbb23aef8d63d5eadaad578455d481cc025\"}],\"IsRegistered\":false,\"LockTime\":0,\"Outputs\":[{\"FixedIndex\":0,\"Address\":\"Ed8ZSxSB98roeyuRZwwekrnRqcgnfiUDeQ\",\"Amount\":\"10000000\",\"AssetId\":\"b037db964a231458d2d6ffd5ea18944c4f90e63d547c5d3b9874df66a4ead0a3\",\"OutputLock\":0,\"ProgramHash\":\"21db215de2758b7d743f66e4c66cfcc35dc54ccbcb\",\"OutputType\":0,\"Payload\":null},{\"FixedIndex\":1,\"Address\":\"8Gqrkk876Kc1HUjeG9evyFsc91RGYWyQj4\",\"Amount\":\"189990000\",\"AssetId\":\"b037db964a231458d2d6ffd5ea18944c4f90e63d547c5d3b9874df66a4ead0a3\",\"OutputLock\":0,\"ProgramHash\":\"12134a742f7782c295d3ea18cb59cd0101b21b1a2f\",\"OutputType\":0,\"Payload\":null}],\"PayLoad\":null,\"PayloadVersion\":0,\"Programs\":[],\"Remark\":\"\",\"Timestamp\":0,\"TxHash\":\"80a0eb3c6bbce2c21d542c7ce9d248fe013fc1c757addd7fcee04b14098d5fa7\",\"Type\":2,\"Version\":1}"_json; SECTION("Sign and Verify") { @@ -56,25 +59,25 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { uint32_t requiredSignCount = 3; uint32_t coinIndex = 0; - AccountPtr account1(new Account("Data/1", mnemonic1, passphrase, payPasswd, false)); + AccountPtr account1(new Account(rootpath + "/1", mnemonic1, passphrase, payPasswd, false)); SubAccountPtr subAccount1(new SubAccount(account1, coinIndex)); subAccount1->Init(); std::string multiSignPubKey1 = account1->MasterPubKeyHDPMString(); bytes_t ownerPubKey1 = account1->OwnerPubKey(); - AccountPtr account2(new Account("Data/2", mnemonic2, passphrase, payPasswd, false)); + AccountPtr account2(new Account(rootpath + "/2", mnemonic2, passphrase, payPasswd, false)); SubAccountPtr subAccount2(new SubAccount(account2, coinIndex)); subAccount2->Init(); std::string multiSignPubKey2 = account2->MasterPubKeyHDPMString(); bytes_t ownerPubKey2 = account2->OwnerPubKey(); - AccountPtr account3(new Account("Data/3", mnemonic3, passphrase, payPasswd, false)); + AccountPtr account3(new Account(rootpath + "/3", mnemonic3, passphrase, payPasswd, false)); SubAccountPtr subAccount3(new SubAccount(account3, coinIndex)); subAccount3->Init(); std::string multiSignPubKey3 = account3->MasterPubKeyHDPMString(); bytes_t ownerPubKey3 = account3->OwnerPubKey(); - AccountPtr account4(new Account("Data/4", mnemonic4, passphrase, payPasswd, false)); + AccountPtr account4(new Account(rootpath + "/4", mnemonic4, passphrase, payPasswd, false)); SubAccountPtr subAccount4(new SubAccount(account4, coinIndex)); subAccount4->Init(); std::string multiSignPubKey4 = account4->MasterPubKeyHDPMString(); @@ -161,7 +164,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { cosigners.push_back(PublicKeyRing(account3->RequestPubKey().getHex(), multiSignPubKey3)); cosigners.push_back(PublicKeyRing(account4->RequestPubKey().getHex(), multiSignPubKey4)); - AccountPtr multiSignAccount(new Account("Data/multisign", cosigners, requiredSignCount, false, false)); + AccountPtr multiSignAccount(new Account(rootpath + "/multisign", cosigners, requiredSignCount, false, false)); SubAccountPtr multiSignSubAccount(new SubAccount(multiSignAccount, coinIndex)); multiSignSubAccount->Init(); AddressArray addresses; @@ -195,7 +198,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { cosigners.push_back(PublicKeyRing(account4->RequestPubKey().getHex(), multiSignPubKey4)); AccountPtr multiSignAccount1( - new Account("Data/m1", mnemonic1, passphrase, payPasswd, cosigners, requiredSignCount, false, + new Account(rootpath + "/m1", mnemonic1, passphrase, payPasswd, cosigners, requiredSignCount, false, false)); SubAccountPtr ms1(new SubAccount(multiSignAccount1, coinIndex)); ms1->Init(); @@ -208,7 +211,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { cosigners.push_back(PublicKeyRing(account4->RequestPubKey().getHex(), multiSignPubKey4)); AccountPtr multiSignAccount2( - new Account("Data/m2", mnemonic2, passphrase, payPasswd, cosigners, requiredSignCount, false, + new Account(rootpath + "/m2", mnemonic2, passphrase, payPasswd, cosigners, requiredSignCount, false, false)); SubAccountPtr ms2(new SubAccount(multiSignAccount2, coinIndex)); ms2->Init(); @@ -221,7 +224,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { cosigners.push_back(PublicKeyRing(account4->RequestPubKey().getHex(), multiSignPubKey4)); AccountPtr multiSignAccount3( - new Account("Data/m3", mnemonic3, passphrase, payPasswd, cosigners, requiredSignCount, false, + new Account(rootpath + "/m3", mnemonic3, passphrase, payPasswd, cosigners, requiredSignCount, false, false)); SubAccountPtr ms3(new SubAccount(multiSignAccount3, coinIndex)); ms3->Init(); @@ -234,7 +237,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { cosigners.push_back(PublicKeyRing(account3->RequestPubKey().getHex(), multiSignPubKey3)); AccountPtr multiSignAccount4( - new Account("Data/m4", mnemonic4, passphrase, payPasswd, cosigners, requiredSignCount, false, + new Account(rootpath + "/m4", mnemonic4, passphrase, payPasswd, cosigners, requiredSignCount, false, false)); SubAccountPtr ms4(new SubAccount(multiSignAccount4, coinIndex)); ms4->Init(); @@ -248,7 +251,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { cosigners.push_back(PublicKeyRing(account4->RequestPubKey().getHex(), multiSignPubKey4)); AccountPtr multiSignAccount5( - new Account("Data/multisign-readonly", cosigners, requiredSignCount, false, false)); + new Account(rootpath + "/multisign-readonly", cosigners, requiredSignCount, false, false)); SubAccountPtr ms5(new SubAccount(multiSignAccount5, coinIndex)); ms5->Init(); @@ -295,7 +298,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { )"_json; KeyStore ks; REQUIRE_NOTHROW(ks.Import(keystoreJson, backupPasswd)); - AccountPtr account1(new Account("Data/AccountTestMultiSignFromWeb", ks, payPasswd)); + AccountPtr account1(new Account(rootpath + "/AccountTestMultiSignFromWeb", ks, payPasswd)); SubAccountPtr ms1(new SubAccount(account1, 0)); ms1->Init(); @@ -307,7 +310,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { std::vector cosigners = account1->MasterPubKeyRing(); - AccountPtr account2(new Account("Data/MultiSignRO", cosigners, account1->GetM(), false, true)); + AccountPtr account2(new Account(rootpath + "/MultiSignRO", cosigners, account1->GetM(), false, true)); SubAccountPtr ms2(new SubAccount(account2, 0)); ms2->Init(); AddressArray addr2; @@ -331,7 +334,7 @@ TEST_CASE("Sign transaction test", "[SignTransaction]") { AccountPtr account3; SubAccountPtr ms3; - REQUIRE_NOTHROW(account3 = AccountPtr(new Account("Data/ReadOnly", readonlyJSON))); + REQUIRE_NOTHROW(account3 = AccountPtr(new Account(rootpath + "/ReadOnly", readonlyJSON))); REQUIRE_NOTHROW(ms3 = SubAccountPtr(new SubAccount(account3, 0))); ms3->Init(); REQUIRE(account3->GetM() == account1->GetM()); diff --git a/ThirdParty/CMakeLists.txt b/ThirdParty/CMakeLists.txt index 038bd7ddc..8739599b3 100644 --- a/ThirdParty/CMakeLists.txt +++ b/ThirdParty/CMakeLists.txt @@ -8,4 +8,8 @@ add_submodule(json DEPENDS platform-specific) add_submodule(catch2 DEPENDS platform-specific) add_submodule(libsqlite DEPENDS platform-specific) add_submodule(libspdlog DEPENDS platform-specific) -add_submodule(secp256k1 DEPENDS platform-specific) \ No newline at end of file +add_submodule(secp256k1 DEPENDS platform-specific) +if(SPV_BUILD_APPS) + add_submodule(zlib DEPENDS platform-specific) + add_submodule(libcurl DEPENDS libressl zlib platform-specific) +endif() \ No newline at end of file diff --git a/ThirdParty/breadwallet-core/bitcoin/BRTransaction.h b/ThirdParty/breadwallet-core/bitcoin/BRTransaction.h index fe4ad049d..2689960f3 100644 --- a/ThirdParty/breadwallet-core/bitcoin/BRTransaction.h +++ b/ThirdParty/breadwallet-core/bitcoin/BRTransaction.h @@ -38,7 +38,7 @@ extern "C" { #define TX_OUTPUT_SIZE 34 // estimated size for a typical transaction output #define TX_INPUT_SIZE 148 // estimated size for a typical compact pubkey transaction input #define TX_MIN_OUTPUT_AMOUNT (TX_FEE_PER_KB*3*(TX_OUTPUT_SIZE + TX_INPUT_SIZE)/1000) //no txout can be below this amount -#define TX_MAX_SIZE 120000 // no tx can be larger than this size in bytes +#define TX_MAX_SIZE (1024 * 1024) // no tx can be larger than this size in bytes #define TX_UNCONFIRMED INT32_MAX // block height indicating transaction is unconfirmed #define TX_MAX_LOCK_HEIGHT 500000000 // a lockTime below this value is a block height, otherwise a timestamp diff --git a/ThirdParty/breadwallet-core/ethereum/BREthereum.h b/ThirdParty/breadwallet-core/ethereum/BREthereum.h index 08a835765..7a2f27c20 100644 --- a/ThirdParty/breadwallet-core/ethereum/BREthereum.h +++ b/ThirdParty/breadwallet-core/ethereum/BREthereum.h @@ -1,9 +1,9 @@ // // BREthereum -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.c b/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.c index 7a9161558..d16c2a7dc 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.c +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/17/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -43,9 +43,6 @@ extern BREthereumAddress addressCreateKey (const BRKey *key) { BREthereumAddress address; - //assert ( 0 == key->compressed); - //assert (65 == BRKeyPubKey(key, NULL, 0)); - // We interrupt your regularly scheduled programming... // "Use any method you like to get it in the form of an hexadecimal string @@ -77,9 +74,12 @@ addressCreateKey (const BRKey *key) { extern char * -addressGetEncodedString (BREthereumAddress address, int useChecksum) { +addressGetEncodedString (BREthereumAddress *address, int useChecksum) { + if (address == NULL) + return NULL; + char *string = calloc (1, ADDRESS_ENCODED_CHARS); - addressFillEncodedString(address, useChecksum, string); + addressFillEncodedString(*address, useChecksum, string); return string; } @@ -156,14 +156,15 @@ addressGetHash (BREthereumAddress address) { return hashCreateFromData(data); } -extern BREthereumAddress +extern BREthereumAddress* addressRlpDecode (BRRlpItem item, BRRlpCoder coder) { - BREthereumAddress address = EMPTY_ADDRESS_INIT; + BREthereumAddress *address = NULL; BRRlpData data = rlpDecodeBytes(coder, item); if (0 != data.bytesCount) { + address = calloc(1, sizeof(BREthereumAddress)); assert (20 == data.bytesCount); - memcpy (address.bytes, data.bytes, 20); + memcpy (address->bytes, data.bytes, 20); } rlpDataRelease(data); @@ -171,9 +172,12 @@ addressRlpDecode (BRRlpItem item, BRRlpCoder coder) { } extern BRRlpItem -addressRlpEncode(BREthereumAddress address, +addressRlpEncode(BREthereumAddress *address, BRRlpCoder coder) { - return rlpEncodeBytes(coder, address.bytes, 20); + if (address == NULL) + return rlpEncodeBytes(coder, NULL, 0); + + return rlpEncodeBytes(coder, address->bytes, 20); } extern BREthereumBoolean diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.h index b161182e1..c8e954704 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumAddress.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/17/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -69,7 +69,7 @@ addressCreateKey (const BRKey *keyWithPubKeyProvided); * @return newly allocated memory of char* */ extern char * -addressGetEncodedString (BREthereumAddress address, int useChecksum); +addressGetEncodedString (BREthereumAddress *address, int useChecksum); /** @@ -88,12 +88,12 @@ addressFillEncodedString (BREthereumAddress address, extern BREthereumHash addressGetHash (BREthereumAddress address); -extern BREthereumAddress +extern BREthereumAddress* addressRlpDecode (BRRlpItem item, BRRlpCoder coder); extern BRRlpItem -addressRlpEncode(BREthereumAddress address, +addressRlpEncode(BREthereumAddress *address, BRRlpCoder coder); extern BREthereumBoolean diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumBase.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumBase.h index 89e897bab..1256096c3 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumBase.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumBase.h @@ -1,9 +1,9 @@ // // BREthereumBase -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/22/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -12,6 +12,7 @@ #define BR_Ethereum_Base_H #include "support/BRArray.h" +#include "support/BRBase.h" #include "support/BRSet.h" #include "ethereum/util/BRUtil.h" #include "ethereum/rlp/BRRlp.h" @@ -20,6 +21,7 @@ #include "BREthereumLogic.h" #include "BREthereumEther.h" #include "BREthereumGas.h" +#include "BREthereumFeeBasis.h" #include "BREthereumHash.h" #include "BREthereumData.h" #include "BREthereumAddress.h" @@ -32,9 +34,6 @@ extern "C" { #define REPEAT(index, count) \ for (size_t index = 0, __indexLimit = (size_t) (count); index < __indexLimit; index++) -#define OwnershipGiven -#define OwnershipKept - typedef uint64_t BREthereumTimestamp; // A Unix time #define ETHEREUM_TIMESTAMP_UNKNOWN ((uint64_t) 0) @@ -54,43 +53,6 @@ typedef enum { RLP_TYPE_TRANSACTION_SIGNED = RLP_TYPE_NETWORK, } BREthereumRlpType; - -/** - * An Ethereum Sync mode specifies how an EWM interfaces with the Ethereum P2P network or with 'BRD - * Server Assisted' interfaces) to determine a User's transfers. - * - * There are four modes; they differ in the extent of BRD vs P2P interaction. - */ -typedef enum { - /** - * Use the BRD backend for all Core blockchain state. The BRD backend provides: account state - * (balance + nonce), transactions, logs, block chain head number, etc. (The BRD backend - * offers an etherscan.io-like HTTP interface). The BRD backend includes a 'submit transaction' - * interface. - */ - BRD_ONLY, - - /* - * Use the BRD backend for everything other than 'submit transaction' - */ - BRD_WITH_P2P_SEND, - - /** - * We'll use the BRD endpoint to identiy blocks of interest based on ETH and TOK transfer - * where our addres is the SOURCE or TARGET. We'll only process blocks from the last N (~ 2000) - * blocks in the chain. - */ - P2P_WITH_BRD_SYNC, - - /** - * We'll be willing to do a complete block chain sync, even starting at block zero. We'll - * use our 'N-ary Search on Account Changes' to perform the sync effectively. We'll use the - * BRD endpoint to augment the 'N-Ary Search' to find TOK transfers where our address is the - * SOURCE. - */ - P2P_ONLY -} BREthereumMode; - /** * An Ehtereum Sync Interest specifies what a 'BRD Service Assisted' mode sync shoul provide data * for. When query BRD services we can be interested in blocks where the User's account/address diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.c b/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.c index 8686907b9..dbdaea634 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.c +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 10/3/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.h index d6acda87b..a73283e8a 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumData.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 10/3/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.c b/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.c index 74d56695a..efdb12068 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.c +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.c @@ -1,9 +1,9 @@ // // BBREthereumEther.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.h index e493bd111..f4169b2a5 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumEther.h @@ -1,9 +1,9 @@ // // BREthereumEther -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumFeeBasis.c b/ThirdParty/breadwallet-core/ethereum/base/BREthereumFeeBasis.c new file mode 100644 index 000000000..9fca4d76d --- /dev/null +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumFeeBasis.c @@ -0,0 +1,60 @@ +// +// BREthereumFeeBasis.c +// Core +// +// Created by Ed Gamble on 8/13/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#include "BREthereumFeeBasis.h" + +extern BREthereumFeeBasis +feeBasisCreate (BREthereumGas limit, + BREthereumGasPrice price) { + return (BREthereumFeeBasis) { + FEE_BASIS_GAS, + { .gas = { limit, price }} + }; +} + +extern BREthereumGas +feeBasisGetGasLimit (BREthereumFeeBasis basis) { + return (FEE_BASIS_GAS == basis.type ? basis.u.gas.limit : gasCreate(0)); +} + +extern BREthereumGasPrice +feeBasisGetGasPrice (BREthereumFeeBasis basis) { + return (FEE_BASIS_GAS == basis.type ? basis.u.gas.price : gasPriceCreate(etherCreateZero())); +} + +extern BREthereumEther +feeBasisGetFee (BREthereumFeeBasis feeBasis, int *overflow) { // BREthereumBoolean + *overflow = 0; + + switch (feeBasis.type) { + case FEE_BASIS_NONE: + return etherCreateZero(); + + case FEE_BASIS_GAS: + return etherCreate (mulUInt256_Overflow (feeBasis.u.gas.price.etherPerGas.valueInWEI, + createUInt256 (feeBasis.u.gas.limit.amountOfGas), + overflow)); + } +} + +extern BREthereumBoolean +feeBasisEqual (const BREthereumFeeBasis *feeBasis1, + const BREthereumFeeBasis *feeBasis2) { + if (feeBasis1 == feeBasis2) return ETHEREUM_BOOLEAN_TRUE; + if (feeBasis1->type != feeBasis2->type) return ETHEREUM_BOOLEAN_FALSE; + switch (feeBasis1->type) { + case FEE_BASIS_NONE: + return ETHEREUM_BOOLEAN_TRUE; + + case FEE_BASIS_GAS: + return AS_ETHEREUM_BOOLEAN (ETHEREUM_COMPARISON_EQ == gasCompare (feeBasis1->u.gas.limit, feeBasis2->u.gas.limit) && + ETHEREUM_COMPARISON_EQ == gasPriceCompare(feeBasis1->u.gas.price, feeBasis2->u.gas.price)); + } +} diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumFeeBasis.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumFeeBasis.h new file mode 100644 index 000000000..faa7064a2 --- /dev/null +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumFeeBasis.h @@ -0,0 +1,48 @@ +// +// BREthereumFeeBasis.h +// Core +// +// Created by Ed Gamble on 8/13/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BR_Ethereum_Fee_Basis_h +#define BR_Ethereum_Fee_Basis_h + +#include "BREthereumGas.h" + +typedef enum { + FEE_BASIS_NONE, + FEE_BASIS_GAS +} BREthereumFeeBasisType; + +typedef struct { + BREthereumFeeBasisType type; + union { + struct { + BREthereumGas limit; + BREthereumGasPrice price; + } gas; + } u; +} BREthereumFeeBasis; + +extern BREthereumFeeBasis +feeBasisCreate (BREthereumGas limit, + BREthereumGasPrice price); + +extern BREthereumGas +feeBasisGetGasLimit (BREthereumFeeBasis basis); + +extern BREthereumGasPrice +feeBasisGetGasPrice (BREthereumFeeBasis basis); + +extern BREthereumEther +feeBasisGetFee (BREthereumFeeBasis feeBasis, int *overflow); + +extern BREthereumBoolean +feeBasisEqual (const BREthereumFeeBasis *feeBasis1, + const BREthereumFeeBasis *feeBasis2); + +#endif /* BR_Ethereum_Fee_Basis_h */ diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.c b/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.c index 3b7236e69..5d8039030 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.c +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.c @@ -1,9 +1,9 @@ // // BREthereumGas -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.h index 89630f901..cbf6f4ab0 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumGas.h @@ -1,9 +1,9 @@ // // BREthereumGas -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.c b/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.c index c796112cf..5a508fed7 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.c +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/9/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.h index da9536c25..7c6fc11a1 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumHash.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/17/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumLogic.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumLogic.h index 268de9bcb..b23811bcb 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumLogic.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumLogic.h @@ -1,9 +1,9 @@ // // BREthereumLogic -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/22/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.c b/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.c index 127f4aac1..a43c83244 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.c +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/17/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.h b/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.h index 39b463ca8..71d087518 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.h +++ b/ThirdParty/breadwallet-core/ethereum/base/BREthereumSignature.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/17/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/base/testBase.c b/ThirdParty/breadwallet-core/ethereum/base/testBase.c index 6943598c5..f6f4f721b 100644 --- a/ThirdParty/breadwallet-core/ethereum/base/testBase.c +++ b/ThirdParty/breadwallet-core/ethereum/base/testBase.c @@ -3,13 +3,15 @@ // CoreTests // // Created by Ed Gamble on 7/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #include #include #include "support/BRCrypto.h" -#include "BREthereumBase.h" +#include "ethereum/base/BREthereumBase.h" static void runEtherParseTests () { @@ -121,6 +123,8 @@ runEtherParseTests () { #define SIGNING_DATA_2 "f86c258502540be40083035b609482e041e84074fc5f5947d4d27e3c44f824b7a1a187b1a2bc2ec500008078a04a7db627266fa9a4116e3f6b33f5d245db40983234eb356261f36808909d2848a0166fa098a2ce3bda87af6000ed0083e3bf7cc31c6686b670bd85cbc6da2d6e85" #define SIGNING_HASH_2 "58e5a0fc7fbc849eddc100d44e86276168a8c7baaa5604e44ba6f5eb8ba1b7eb" +#define ETHEREUM_ADDRESS_PARAMS ((BRAddressParams) { BITCOIN_PUBKEY_PREFIX, BITCOIN_SCRIPT_PREFIX, BITCOIN_PRIVKEY_PREFIX, BITCOIN_BECH32_PREFIX }) + static void runSignatureTests1 (void) { printf ("\n== Signature 1\n"); UInt256 digest; @@ -139,7 +143,7 @@ static void runSignatureTests1 (void) { assert (0 == strcmp (digestString, signingHash)); BRKey privateKeyUncompressed; - BRKeySetPrivKey(&privateKeyUncompressed, SIGNATURE_PRIVATE_KEY); + BRKeySetPrivKey(&privateKeyUncompressed, ETHEREUM_ADDRESS_PARAMS, SIGNATURE_PRIVATE_KEY); size_t signatureLen = BRKeyCompactSign(&privateKeyUncompressed, NULL, 0, diff --git a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.c b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.c index 94ce759a6..916c8806b 100644 --- a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.c +++ b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 5/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -176,7 +176,7 @@ extern BREthereumBCS bcsCreate (BREthereumNetwork network, BREthereumAddress address, BREthereumBCSListener listener, - BREthereumMode mode, + BRCryptoSyncMode mode, OwnershipGiven BRSetOf(BREthereumNodeConfig) peers, OwnershipGiven BRSetOf(BREthereumBlock) blocks, OwnershipGiven BRSetOf(BREthereumTransaction) transactions, @@ -265,14 +265,15 @@ bcsCreate (BREthereumNetwork network, chainHeader = blockCheckpointCreatePartialBlockHeader(checkpoint); } - // There is no need to discover nodes if we are in BRD_ONLY mode. - BREthereumBoolean discoverNodes = AS_ETHEREUM_BOOLEAN (mode != BRD_ONLY); #if defined (LES_DISABLE_DISCOVERY) - discoverNodes = ETHEREUM_BOOLEAN_FALSE; + BREthereumBoolean discoverNodes = ETHEREUM_BOOLEAN_FALSE; +#else + // There is no need to discover nodes if we are in BRD_ONLY mode. + BREthereumBoolean discoverNodes = AS_ETHEREUM_BOOLEAN (mode != CRYPTO_SYNC_MODE_API_ONLY); #endif - BREthereumBoolean handleSync = AS_ETHEREUM_BOOLEAN (P2P_ONLY == mode || - P2P_WITH_BRD_SYNC == mode); + BREthereumBoolean handleSync = AS_ETHEREUM_BOOLEAN (CRYPTO_SYNC_MODE_P2P_ONLY == mode || + CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == mode); bcs->les = lesCreate (bcs->network, (BREthereumLESCallbackContext) bcs, @@ -379,11 +380,11 @@ bcsSyncRange (BREthereumBCS bcs, uint64_t blockNumberStartAdjusted; switch (bcs->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: assert (0); - case P2P_WITH_BRD_SYNC: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: // // For a PRIME_WITH_ENDPOINT sync we rely 100% on the BRD backend to provide any and // all blocks of interest - which is any block involving `address` in a transaction @@ -398,7 +399,7 @@ bcsSyncRange (BREthereumBCS bcs, blockNumberStartAdjusted = maximum (blockNumberStart, blockNumberStop - SYNC_LINEAR_LIMIT + 1); break; - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_ONLY: // // For a FULL_BLOCKCHAIN sync we run our 'N-Ary Search on Account Changes' algorithm // which has a (current) weakness on 'ERC20 transfers w/ address as target'. So, we @@ -422,7 +423,7 @@ bcsSyncRange (BREthereumBCS bcs, extern void bcsSync (BREthereumBCS bcs, uint64_t blockNumber) { - assert (P2P_ONLY == bcs->mode || P2P_WITH_BRD_SYNC == bcs->mode); + assert (CRYPTO_SYNC_MODE_P2P_ONLY == bcs->mode || CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == bcs->mode); // Stop a sync that is currently in progress. if (ETHEREUM_BOOLEAN_IS_TRUE(bcsSyncInProgress(bcs))) @@ -443,7 +444,7 @@ bcsSyncInProgress (BREthereumBCS bcs) { extern void bcsSendTransaction (BREthereumBCS bcs, BREthereumTransaction transaction) { - assert (BRD_ONLY != bcs->mode); + assert (CRYPTO_SYNC_MODE_API_ONLY != bcs->mode); bcsSignalSubmitTransaction (bcs, transactionCopy (transaction)); } @@ -452,7 +453,7 @@ bcsSendTransactionRequest (BREthereumBCS bcs, BREthereumHash transactionHash, uint64_t blockNumber, uint64_t blockTransactionIndex) { - assert (P2P_ONLY == bcs->mode || P2P_WITH_BRD_SYNC == bcs->mode); + assert (CRYPTO_SYNC_MODE_P2P_ONLY == bcs->mode || CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == bcs->mode); // There is a transaction in `blockNumber` - get the block header and 'flow through' the logic // to find the suspected transaction. lesProvideBlockHeaders (bcs->les, @@ -467,7 +468,7 @@ bcsSendLogRequest (BREthereumBCS bcs, BREthereumHash transactionHash, uint64_t blockNumber, uint64_t blockTransactionIndex) { - assert (P2P_ONLY == bcs->mode || P2P_WITH_BRD_SYNC == bcs->mode); + assert (CRYPTO_SYNC_MODE_P2P_ONLY == bcs->mode || CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == bcs->mode); // There is a log in `blockNumber` - get the block header and 'flow through' the logic to find // the suspected log. lesProvideBlockHeaders (bcs->les, @@ -482,7 +483,7 @@ bcsReportInterestingBlocks (BREthereumBCS bcs, // interest // request id BRArrayOf(uint64_t) blockNumbers) { - assert (P2P_ONLY == bcs->mode || P2P_WITH_BRD_SYNC == bcs->mode); + assert (CRYPTO_SYNC_MODE_P2P_ONLY == bcs->mode || CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == bcs->mode); eth_log ("BCS", "Report Interesting Blocks: %zu", array_count(blockNumbers)); for (size_t index = 0; index < array_count(blockNumbers); index++) lesProvideBlockHeaders (bcs->les, @@ -600,6 +601,9 @@ bcsHandleSubmitTransaction (BREthereumBCS bcs, int pendingIndex = bcsLookupPendingTransaction (bcs, hash); if (-1 != pendingIndex) return; // already pending, so skip out. + // We only ever submit transactions that are UNKNOWN. + assert (TRANSACTION_STATUS_UNKNOWN == transactionGetStatus(transaction).type); + // Make the transaction pending. bcsPendTransaction(bcs, transaction); @@ -621,7 +625,7 @@ bcsHandleStatus (BREthereumBCS bcs, BREthereumHash headHash, uint64_t headNumber) { // If we are not a P2P_* node, we won't handle announcements - if (BRD_ONLY == bcs->mode || BRD_WITH_P2P_SEND == bcs->mode) { + if (CRYPTO_SYNC_MODE_API_ONLY == bcs->mode || CRYPTO_SYNC_MODE_API_WITH_P2P_SEND == bcs->mode) { #if defined (BCS_REPORT_IGNORED_ANNOUNCE) eth_log ("BCS", "Status %" PRIu64 " Ignored (not P2P) <== %s", headNumber, @@ -647,7 +651,7 @@ bcsHandleAnnounce (BREthereumBCS bcs, UInt256 headTotalDifficulty, uint64_t reorgDepth) { // If we are not a P2P_* node, we won't handle announcements - if (BRD_ONLY == bcs->mode || BRD_WITH_P2P_SEND == bcs->mode) { + if (CRYPTO_SYNC_MODE_API_ONLY == bcs->mode || CRYPTO_SYNC_MODE_API_WITH_P2P_SEND == bcs->mode) { #if defined (BCS_REPORT_IGNORED_ANNOUNCE) eth_log ("BCS", "Block %" PRIu64 " Ignored (not P2P) <== %s", headNumber, @@ -1262,6 +1266,9 @@ bcsBlockHasMatchingLogs (BREthereumBCS bcs, static BREthereumBoolean bcsBlockNeedsAccountState (BREthereumBCS bcs, BREthereumBlock block) { + if (blockGetNumber(block) >= bcsSyncGetLastBlockNumber(bcs->sync)) + return ETHEREUM_BOOLEAN_TRUE; + return ETHEREUM_BOOLEAN_FALSE; } @@ -1383,18 +1390,18 @@ bcsHandleBlockHeaders (BREthereumBCS bcs, array_free(headers); + if (NULL != receiptsHashes && array_count(receiptsHashes) > 0) + lesProvideReceipts (bcs->les, node, + (BREthereumLESProvisionContext) bcs, + (BREthereumLESProvisionCallback) bcsSignalProvision, + receiptsHashes); + if (NULL != bodiesHashes && array_count(bodiesHashes) > 0) lesProvideBlockBodies (bcs->les, node, (BREthereumLESProvisionContext) bcs, (BREthereumLESProvisionCallback) bcsSignalProvision, bodiesHashes); - if (NULL != receiptsHashes && array_count(receiptsHashes) > 0) - lesProvideReceipts (bcs->les, node, - (BREthereumLESProvisionContext) bcs, - (BREthereumLESProvisionCallback) bcsSignalProvision, - receiptsHashes); - if (NULL != accountsHashes && array_count(accountsHashes) > 0) lesProvideAccountStates (bcs->les, node, (BREthereumLESProvisionContext) bcs, @@ -1530,9 +1537,26 @@ bcsHandleBlockBody (BREthereumBCS bcs, for (int i = 0; i < array_count(transactions); i++) { BREthereumTransaction tx = transactions[i]; assert (NULL != tx); - + + // get transaction logs + BREthereumLog log = NULL; + BREthereumBlockStatus blockStatus = blockGetStatus(block); + if (blockStatus.logs) { + for (size_t li = 0; li < array_count(blockStatus.logs); li++) { + BREthereumTransactionStatus status = logGetStatus(blockStatus.logs[li]); + uint64_t transactionIndex = -1; + if (transactionStatusExtractIncluded(&status, NULL, NULL, &transactionIndex, NULL, NULL) && + transactionIndex == i) { + log = blockStatus.logs[i]; + break; + } + } + } + // If it is our transaction (as source or target), handle it. - if (ETHEREUM_BOOLEAN_IS_TRUE(transactionHasAddress(tx, bcs->address))) { + if (ETHEREUM_BOOLEAN_IS_TRUE(transactionHasAddress(tx, bcs->address)) || + (log != NULL && + ETHEREUM_BOOLEAN_TRUE == logMatchesAddress(log, bcs->address, ETHEREUM_BOOLEAN_TRUE))) { eth_log("BCS", "Bodies %" PRIu64 " Found Transaction at %d", blockGetNumber(block), i); @@ -1582,7 +1606,7 @@ bcsHandleBlockBody (BREthereumBCS bcs, // 3) We want the account state too - because we've found a transaction for bcs->address // and the account changed (instead of computing the account, we'll query definitively). - if (ETHEREUM_BOOLEAN_IS_TRUE (blockHasStatusAccountStateRequest (block, BLOCK_REQUEST_NOT_NEEDED))) { + if (0 && ETHEREUM_BOOLEAN_IS_TRUE (blockHasStatusAccountStateRequest (block, BLOCK_REQUEST_NOT_NEEDED))) { blockReportStatusAccountStateRequest(block, BLOCK_REQUEST_PENDING); lesProvideAccountStatesOne (bcs->les, node, (BREthereumLESProvisionContext) bcs, @@ -1892,7 +1916,7 @@ bcsHandleTransactionReceiptsMultiple (BREthereumBCS bcs, * Note: we'll only remove from pending if two back-to-back nodes report the same status. * * b) if a node reports a transaction as errored (other than 'dropped'), we'll remove the - * transaction from pending and mark the transaction as ERRORED. If is posslble that the + * transaction from pending and mark the transaction as ERRORED. It is posslble that the * transaction does get included in a subsequently announced block. Having the tranaction * subsequently included implies a race condition between nodes, I think. Note: we'll only * remote from pending if two back-to-back statuses report the same error. @@ -1902,6 +1926,11 @@ bcsHandleTransactionReceiptsMultiple (BREthereumBCS bcs, * requests occur and we'll hopefully get better status info. Or, the transaction will be * part of an announced block. * + * There are races here. Say we've connected to two nodes that are reliably reporting status. We + * get a TxStatus response from A, B and then B, A. If 'B' is error, we'll declare the transaction + * in error and unpend. However, as we noted above, once the transaction is in a block, it will + * move to INCLUDED. + * * What does this imply from a User's perspective. They might see a transaction in error that * comes back to life as INCLUDED. Worse, they might see a transaction in error and try to * cancel or resubmit that transaction - hard to say if the cancel/resubmit would succeed as once @@ -1940,12 +1969,15 @@ bcsHandleTransactionStatus (BREthereumBCS bcs, return; case TRANSACTION_STATUS_QUEUED: - needStatus = TRANSACTION_STATUS_UNKNOWN == oldStatus.type; + // See below comment in PENDING. + needStatus = TRANSACTION_STATUS_QUEUED != oldStatus.type; break; case TRANSACTION_STATUS_PENDING: - needStatus = (TRANSACTION_STATUS_UNKNOWN == oldStatus.type || - TRANSACTION_STATUS_QUEUED == oldStatus.type); + // Be willing to go PENDING no matter the prior status. In particular, if some status + // is ERROR (like a node is syncing and reject sthe transaction submission outright) and + // some other node reports PENDING, then go to PENDING (needStatus = 1). + needStatus = TRANSACTION_STATUS_PENDING != oldStatus.type; break; case TRANSACTION_STATUS_INCLUDED: @@ -2274,19 +2306,22 @@ bcsHandleProvision (BREthereumBCS bcs, // // This can cause problems... as in, we are queryng a node based on a recent // block but upon resubmission the other node is behind. +#if 0 lesRetryProvision (bcs->les, NODE_REFERENCE_ANY, (BREthereumLESProvisionContext) bcs, (BREthereumLESProvisionCallback) bcsSignalProvision, provision); needProvisionRelease = 0; +#endif eth_log ("BCS", "Resubmitted Provision: %zu: %s", provision->identifier, provisionGetTypeName(provision->type)); break; - default: break; + case PROVISION_ERROR_NODE_DATA: + break; } break; diff --git a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.h b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.h index b13f8b1fc..83065c0b0 100644 --- a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.h +++ b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCS.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 5/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -13,6 +13,7 @@ #include "ethereum/base/BREthereumBase.h" #include "ethereum/les/BREthereumLES.h" +#include "BRCryptoSync.h" #ifdef __cplusplus extern "C" { @@ -53,7 +54,6 @@ typedef void uint64_t headBlockNumber, uint64_t headBlockTimestamp); - /** * The BCS account state has been updated. */ @@ -161,7 +161,7 @@ extern BREthereumBCS bcsCreate (BREthereumNetwork network, BREthereumAddress address, BREthereumBCSListener listener, - BREthereumMode syncMode, + BRCryptoSyncMode syncMode, BRSetOf(BREthereumNodeConfig) peers, BRSetOf(BREthereumBlock) blocks, BRSetOf(BREthereumTransaction) transactions, diff --git a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSEvent.c b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSEvent.c index f90257260..a74ba47c7 100644 --- a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSEvent.c +++ b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSEvent.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 5/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSPrivate.h b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSPrivate.h index 6dc444580..ab9adb834 100644 --- a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSPrivate.h +++ b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSPrivate.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -82,7 +82,7 @@ struct BREthereumBCSStruct { /** * The sync mode */ - BREthereumMode mode; + BRCryptoSyncMode mode; /** * A BloomFilter with address for application to transactions @@ -354,6 +354,8 @@ bcsSyncRelease (BREthereumBCSSync sync); extern BREthereumBoolean bcsSyncIsActive (BREthereumBCSSync sync); +extern uint64_t bcsSyncGetLastBlockNumber(BREthereumBCSSync sync); + extern void bcsSyncStop (BREthereumBCSSync sync); diff --git a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSSync.c b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSSync.c index a1d9064fc..c04a75f59 100644 --- a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSSync.c +++ b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBCSSync.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 7/25/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -160,9 +160,9 @@ syncRangeReport (BREthereumBCSSyncRange range, assert (range->head > range->tail); - eth_log ("BCS", "Sync: %s: (T:C:R:D) = ( %d : %4" PRIu64 ": %8llu : {%7" PRIu64 ", %7" PRIu64 "} : %2d ) *** %s%p -> %p", + eth_log ("BCS", "Sync: %s: (T:C:R:D) = ( %d : %4" PRIu64 ": {%7" PRIu64 ", %7" PRIu64 "} : %2d ) *** %s%p -> %p", action, - range->type, range->count, range->step, range->tail, range->head, depth, + range->type, range->count, range->tail, range->head, depth, spaces, range, range->parent); } @@ -563,6 +563,16 @@ bcsSyncIsActive (BREthereumBCSSync sync) { return AS_ETHEREUM_BOOLEAN(NULL != sync->root); } +extern uint64_t +bcsSyncGetLastBlockNumber(BREthereumBCSSync sync) { + if (NULL == sync->root) + return UINT64_MAX; + + BREthereumBCSSyncRange root = syncRangeGetRoot(sync->root); + + return root->head; +} + /** * The callback for sync ranges. If `header` is NULL, then we are simply announcing progress. If * `header` is not NULL then add `header` to `results` and periodically invoke the sync callback diff --git a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBlockChainSlice.h b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBlockChainSlice.h index a13ed6fc5..c9c011257 100644 --- a/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBlockChainSlice.h +++ b/ThirdParty/breadwallet-core/ethereum/bcs/BREthereumBlockChainSlice.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.c index d128be017..50cb74389 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/15/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.h index 32f5fe50f..ca9910ea7 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumAccountState.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/15/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.c index 9256814a1..13244cead 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.c @@ -1,9 +1,9 @@ // // BBREthereumBlock.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/23/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -39,6 +39,8 @@ static unsigned int blockHeaderAllocCount = 0; #define EIP155_FORK_BLOCK_NUMBER (2675000) #define EIP158_FORK_BLOCK_NUMBER (2675000) #define BYZANTIUM_FORK_BLOCK_NUMBER (4370000) +#define ISTANBUL_FORK_BLOCK_NUMBER (9069000) + /// MARK: - Block Status @@ -143,7 +145,7 @@ struct BREthereumBlockHeaderRecord { * An arbitrary byte array containing data relevant to this block. This must be 32 bytes or * fewer; formally Hx. */ - uint8_t extraData [1024]; + uint8_t extraData [20480]; uint32_t extraDataCount; /** @@ -438,7 +440,7 @@ blockHeaderValidateGasUsed (BREthereumBlockHeader this, static int blockHeaderValidateExtraData (BREthereumBlockHeader this, BREthereumBlockHeader parent) { - return this->extraDataCount <= 1024; + return this->extraDataCount <= sizeof(this->extraData); } #if defined (INCLUDE_UNUSED_FUNCTION) @@ -538,7 +540,7 @@ blockHeaderRlpEncode (BREthereumBlockHeader header, items[ 0] = hashRlpEncode(header->parentHash, coder); items[ 1] = hashRlpEncode(header->ommersHash, coder); - items[ 2] = addressRlpEncode(header->beneficiary, coder); + items[ 2] = addressRlpEncode(&header->beneficiary, coder); items[ 3] = hashRlpEncode(header->stateRoot, coder); items[ 4] = hashRlpEncode(header->transactionsRoot, coder); items[ 5] = hashRlpEncode(header->receiptsRoot, coder); @@ -572,7 +574,13 @@ blockHeaderRlpDecode (BRRlpItem item, header->parentHash = hashRlpDecode(items[0], coder); header->ommersHash = hashRlpDecode(items[1], coder); - header->beneficiary = addressRlpDecode(items[2], coder); + BREthereumAddress *tmpAddress = addressRlpDecode(items[2], coder); + if (tmpAddress != NULL) { + header->beneficiary = *tmpAddress; + free(tmpAddress); + } else { + header->beneficiary = EMPTY_ADDRESS_INIT; + } header->stateRoot = hashRlpDecode(items[3], coder); header->transactionsRoot = hashRlpDecode(items[4], coder); header->receiptsRoot = hashRlpDecode(items[5], coder); @@ -897,7 +905,9 @@ blockLinkLogsWithTransactions (BREthereumBlock block) { BREthereumLog log = block->status.logs[index]; BREthereumTransactionStatus status = logGetStatus(log); uint64_t transactionIndex; size_t logIndex; - assert (transactionStatusExtractIncluded(&status, NULL, NULL, &transactionIndex, NULL, NULL)); + + int transactionIncluded = transactionStatusExtractIncluded (&status, NULL, NULL, &transactionIndex, NULL, NULL); + assert (transactionIncluded); // Importantly, note that the log has no reference to the transaction itself. And, if only // implicitly, we assume that `block` has the correct transaction at transactionIndex. @@ -1428,8 +1438,7 @@ static struct BREthereumBlockHeaderRecord genesisMainnetBlockHeaderRecord = { 0, // uint8_t extraData [32]; - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0 }, // uint8_t extraDataCount; 0, @@ -1698,29 +1707,21 @@ initializeGenesisBlocks (void) { /// MARK: - Block Checkpoint -BREthereumBlockCheckpoint +static BREthereumBlockCheckpoint ethereumMainnetCheckpoints [] = { - { 0, "\xd4\xe5\x67\x40\xf8\x76\xae\xf8\xc0\x10\xb8\x6a\x40\xd5\xf5\x67\x45\xa1\x18\xd0\x90\x6a\x34\xe6\x9a\xec\x8c\x0d\xb1\xcb\x8f\xa3", { .std = "17179869184" }, 0 }, - { 4000000, "\xb8\xa3\xf7\xf5\xcf\xc1\x74\x8f\x91\xa6\x84\xf2\x0f\xe8\x90\x31\x20\x2c\xba\xdc\xd1\x50\x78\xc4\x9b\x85\xec\x2a\x57\xf4\x38\x53", { .std = "469024977881526938386" }, 1499633567 }, - { 4500000, "\x43\x34\x0a\x6d\x23\x25\x32\xc3\x28\x21\x1d\x8a\x8c\x0f\xa8\x4a\xf6\x58\xdb\xff\x1f\x49\x06\xab\x7a\x7d\x4e\x41\xf8\x2f\xe3\xa3", { .std = "1386905480746946772236" }, 1509953783 }, - { 5000000, "\x7d\x5a\x43\x69\x27\x3c\x72\x34\x54\xac\x13\x7f\x48\xa4\xf1\x42\xb0\x97\xaa\x27\x79\x46\x4e\x65\x05\xf1\xb1\xc5\xe3\x7b\x53\x82", { .std = "2285199027754071740498" }, 1517319693 }, - { 5500000, "\x2d\x3a\x15\x4e\xee\x9f\x90\x66\x6c\x6e\x82\x4f\x11\xe1\x5f\x2d\x60\xb0\x53\x23\xa8\x12\x54\xf6\x00\x75\xc3\x4a\x61\xef\x12\x4d", { .std = "3825806101695195923560" }, 1524611221 }, - { 6000000, "\xbe\x84\x7b\xe2\xbc\xeb\x74\xe6\x60\xda\xf9\x6b\x3f\x06\x69\xd5\x8f\x59\xdc\x91\x01\x71\x56\x89\xa0\x0e\xf8\x64\xa5\x40\x8f\x43", { .std = "5484495551037046114587" }, 1532118564 }, - { 6500000, "\x70\xc8\x1c\x3c\xb2\x56\xb5\xb9\x30\xf0\x5b\x24\x4d\x09\x5c\xb4\x84\x5e\x98\x08\xc4\x8d\x88\x1e\x3c\xc3\x1d\x18\xae\x4c\x3a\xe5", { .std = "7174074700595750315193" }, 1539330275 }, + { 0, HASH_INIT("6afc2eb01956dfe192dc4cd065efdf6c3c80448776ca367a7246d279e228ff0a"), { .std = "0x1" }, 0x1 }, }; #define CHECKPOINT_MAINNET_COUNT (sizeof (ethereumMainnetCheckpoints) / sizeof (BREthereumBlockCheckpoint)) static BREthereumBlockCheckpoint ethereumTestnetCheckpoints [] = { -// { 0, "\x41\x94\x10\x23\x68\x09\x23\xe0\xfe\x4d\x74\xa3\x4b\xda\xc8\x14\x1f\x25\x40\xe3\xae\x90\x62\x37\x18\xe4\x7d\x66\xd1\xca\x4a\x2d", { .std = "0x100000" }, 0 }, // 1061 days 6 hrs ago (Jul-30-2015 03:26:13 PM +UTC) - { 0, "\x69\x8e\x5e\xc1\x33\x06\x4d\xab\xb7\xc4\x2e\xb4\xb2\xbd\xfa\x21\xe7\xb7\xc2\x32\x6b\x0b\x71\x9d\x5a\xb7\xf4\x52\xae\x8f\x5e\xe4", { .std = "0x1" }, 0}, // 1061 days 6 hrs ago (Jul-30-2015 03:26:13 PM +UTC) + { 0, HASH_INIT("698e5ec133064dabb7c42eb4b2bdfa21e7b7c2326b0b719d5ab7f452ae8f5ee4"), { .std = "0x1" }, 1541053856 }, // 1061 days 6 hrs ago (Jul-30-2015 03:26:13 PM +UTC) }; #define CHECKPOINT_TESTNET_COUNT (sizeof (ethereumTestnetCheckpoints) / sizeof (BREthereumBlockCheckpoint)) static BREthereumBlockCheckpoint ethereumRinkebyCheckpoints [] = { -// { 0, "\x63\x41\xfd\x3d\xaf\x94\xb7\x48\xc7\x2c\xed\x5a\x5b\x26\x02\x8f\x24\x74\xf5\xf0\x0d\x82\x45\x04\xe4\xfa\x37\xa7\x57\x67\xe1\x77", { .std = "0x01" }, 0x58ee40ba }, // 439 days 6 hrs ago (Apr-12-2017 03:20:50 PM +UTC) - { 0, "\x69\x40\xd4\xee\x80\x21\x8d\x11\x09\x8c\x99\xdb\x11\xe9\x97\x68\x6e\x58\x7e\xc4\x82\xc7\x28\x1e\x01\x5c\x12\xf1\x15\x2e\x71\xb5", { .std = "0x01" }, 1541053864 }, // 439 days 6 hrs ago (Apr-12-2017 03:20:50 PM +UTC) + { 0, HASH_INIT("6940d4ee80218d11098c99db11e997686e587ec482c7281e015c12f1152e71b5"), { .std = "0x1" }, 1541053864 }, // 439 days 6 hrs ago (Apr-12-2017 03:20:50 PM +UTC) }; #define CHECKPOINT_RINKEBY_COUNT (sizeof (ethereumRinkebyCheckpoints) / sizeof (BREthereumBlockCheckpoint)) diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.h index a5c5eb81a..ef45a7ced 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlock.h @@ -1,9 +1,9 @@ // // BBREthereumBlock.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/23/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -180,8 +180,8 @@ extern uint64_t chtRootNumberGetFromNumber (uint64_t number); -#define BLOCK_HEADER_CHT_ROOT_INTERVAL (2048) -#define BLOCK_HEADER_CHT_ROOT_INTERVAL_SHIFT (11) // 2048 == (1 << 11) +#define BLOCK_HEADER_CHT_ROOT_INTERVAL (32768) +#define BLOCK_HEADER_CHT_ROOT_INTERVAL_SHIFT (15) // 2048 == (1 << 11) change to 32768 == (1 << 15) /// MARK: - Block diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlockChain.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlockChain.h index e061a4fdc..1960e4297 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlockChain.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBlockChain.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/17/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.c index 9b6d28e83..ede87621c 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/10/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.h index 7c0073258..f2c47336a 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumBloomFilter.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/10/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.c index 17a270be9..f44d71f72 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/10/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -455,7 +455,11 @@ logRlpDecode (BRRlpItem item, assert ((3 == itemsCount && RLP_TYPE_NETWORK == type) || (6 == itemsCount && RLP_TYPE_ARCHIVE == type)); - log->address = addressRlpDecode(items[0], coder); + BREthereumAddress *tmpAddress = addressRlpDecode(items[0], coder); + if (tmpAddress != NULL) { + log->address = *tmpAddress; + free(tmpAddress); + } log->topics = logTopicsRlpDecode (items[1], coder); log->data = rlpGetData (coder, items[2]); // rlpDecodeBytes(coder, items[2]); @@ -482,7 +486,7 @@ logRlpEncode(BREthereumLog log, BRRlpItem items[6]; // more than enough - items[0] = addressRlpEncode(log->address, coder); + items[0] = addressRlpEncode(&log->address, coder); items[1] = logTopicsRlpEncode(log, coder); items[2] = rlpGetItem(coder, log->data); // rlpEncodeBytes(coder, log->data.bytes, log->data.bytesCount); diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.h index 504140dad..7b04c53e4 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumLog.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/10/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.c index 36550690b..ff168c1bb 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.c @@ -1,13 +1,14 @@ // // BREthereumNetwork -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/13/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. +#include #include #include "BREthereumNetwork.h" @@ -36,8 +37,19 @@ struct BREthereumNetworkRecord { extern BREthereumChainId networkGetChainId (BREthereumNetwork network) { - networkInitilizeAllIfAppropriate(); - return network->chainId; + networkInitilizeAllIfAppropriate(); + return network->chainId; +} + +extern BREthereumChainId +networkGetChainIdOld(BREthereumNetwork network) { + if (!strcmp(network->name, "mainnet")) { + return 1; + } else if (!strcmp(network->name, "testnet")) { + return 3; + } + + return 0; } extern BREthereumNetworkId @@ -63,6 +75,17 @@ networkGetName (BREthereumNetwork network) { return network->name; } +extern char * +networkCopyNameAsLowercase (BREthereumNetwork network) { + char *networkName = strdup (network-> name); + size_t networkNameLength = strlen (networkName); + + for (size_t index = 0; index < networkNameLength; index++) + networkName[index] = tolower (networkName[index]); + + return networkName; +} + extern const char** networkGetSeeds (BREthereumNetwork network) { return network->seeds; @@ -96,9 +119,9 @@ networkGetEnodesLocal (BREthereumNetwork network, int parity) { // Mainnet // static struct BREthereumNetworkRecord ethereumMainnetRecord = { - "Mainnet", - 0, - 1, + "mainnet", + 20, + 20, EMPTY_HASH_INIT, EMPTY_HASH_INIT, // Seeds @@ -147,9 +170,9 @@ MainnetChainConfig = &ChainConfig{ // Testnet // static struct BREthereumNetworkRecord ethereumTestnetRecord = { - "Testnet", - 3, - 3, + "testnet", // aka "ropsten" + 21, + 21, EMPTY_HASH_INIT, EMPTY_HASH_INIT, // Seeds @@ -189,7 +212,7 @@ TestnetChainConfig = &ChainConfig{ // Rinkeby // static struct BREthereumNetworkRecord ethereumRinkebyRecord = { - "Rinkeby", + "rinkeby", 4, 4, EMPTY_HASH_INIT, diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.h index 2b4c73aed..4bb81b1cb 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumNetwork.h @@ -1,9 +1,9 @@ // // BREthereumNetwork -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/13/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -37,9 +37,15 @@ typedef int BREthereumNetworkId; extern const char * networkGetName (BREthereumNetwork network); +extern char * +networkCopyNameAsLowercase (BREthereumNetwork network); + extern BREthereumChainId networkGetChainId (BREthereumNetwork network); +extern BREthereumChainId +networkGetChainIdOld(BREthereumNetwork network); + extern BREthereumNetworkId networkGetNetworkId (BREthereumNetwork network); diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumProofOfWork.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumProofOfWork.c index 44a4ac750..47e15ab85 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumProofOfWork.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumProofOfWork.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 12/14/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.c index b5b1d41bf..2185c1dfd 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.c @@ -1,9 +1,9 @@ // // BBREthereumTransaction.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -40,7 +40,7 @@ struct BREthereumTransactionRecord { /** * The target address - recvs 'amount' */ - BREthereumAddress targetAddress; + BREthereumAddress *targetAddress; /** * The amount transferred from sourceAddress to targetAddress. Note that this is not @@ -93,7 +93,7 @@ struct BREthereumTransactionRecord { extern BREthereumTransaction transactionCreate(BREthereumAddress sourceAddress, - BREthereumAddress targetAddress, + BREthereumAddress *targetAddress, BREthereumEther amount, BREthereumGasPrice gasPrice, BREthereumGas gasLimit, @@ -103,7 +103,14 @@ transactionCreate(BREthereumAddress sourceAddress, transactionSetStatus(transaction, transactionStatusCreate (TRANSACTION_STATUS_UNKNOWN)); transaction->sourceAddress = sourceAddress; - transaction->targetAddress = targetAddress; + + if (targetAddress != NULL) { + transaction->targetAddress = calloc(1, sizeof(BREthereumAddress)); + *transaction->targetAddress = *targetAddress; + } else { + transaction->targetAddress = NULL; + } + transaction->amount = amount; transaction->gasPrice = gasPrice; transaction->gasLimit = gasLimit; // Must not be changed. @@ -127,6 +134,10 @@ transactionCopy (BREthereumTransaction transaction) { BREthereumTransaction copy = calloc (1, sizeof (struct BREthereumTransactionRecord)); memcpy (copy, transaction, sizeof (struct BREthereumTransactionRecord)); copy->data = (NULL == transaction->data ? NULL : strdup(transaction->data)); + if (NULL != transaction->targetAddress) { + copy->targetAddress = calloc(1, sizeof(BREthereumAddress)); + *copy->targetAddress = *transaction->targetAddress; + } #if defined (TRANSACTION_LOG_ALLOC_COUNT) eth_log ("MEM", "TX Copy - Count: %d", ++transactionAllocCount); @@ -139,6 +150,7 @@ extern void transactionRelease (BREthereumTransaction transaction) { if (NULL != transaction) { if (NULL != transaction->data) free (transaction->data); + if (NULL != transaction->targetAddress) free (transaction->targetAddress); #if defined (TRANSACTION_LOG_ALLOC_COUNT) eth_log ("MEM", "TX Release - Count: %d", --transactionAllocCount); #endif @@ -156,7 +168,7 @@ transactionGetSourceAddress(BREthereumTransaction transaction) { return transaction->sourceAddress; } -extern BREthereumAddress +extern BREthereumAddress* transactionGetTargetAddress(BREthereumTransaction transaction) { return transaction->targetAddress; } @@ -165,7 +177,7 @@ extern BREthereumBoolean transactionHasAddress (BREthereumTransaction transaction, BREthereumAddress address) { return (ETHEREUM_BOOLEAN_IS_TRUE(addressEqual(address, transaction->sourceAddress)) - || ETHEREUM_BOOLEAN_IS_TRUE(addressEqual(address, transaction->targetAddress)) + || (transaction->targetAddress != NULL && ETHEREUM_BOOLEAN_IS_TRUE(addressEqual(address, *transaction->targetAddress))) ? ETHEREUM_BOOLEAN_TRUE : ETHEREUM_BOOLEAN_FALSE); } @@ -175,22 +187,34 @@ transactionGetAmount(BREthereumTransaction transaction) { return transaction->amount; } +extern BREthereumFeeBasis +transactionGetFeeBasis (BREthereumTransaction transaction) { + BREthereumGas gas = (ETHEREUM_BOOLEAN_IS_TRUE(transactionIsConfirmed(transaction)) + ? transaction->status.u.included.gasUsed + : transaction->gasLimit); + + return (BREthereumFeeBasis) { + FEE_BASIS_GAS, + { gas, transaction->gasPrice } + }; +} + extern BREthereumEther transactionGetFee (BREthereumTransaction transaction, int *overflow) { - return etherCreate - (mulUInt256_Overflow(transaction->gasPrice.etherPerGas.valueInWEI, - createUInt256 (ETHEREUM_BOOLEAN_IS_TRUE(transactionIsConfirmed(transaction)) - ? transaction->status.u.included.gasUsed.amountOfGas - : transaction->gasLimit.amountOfGas), - overflow)); + return feeBasisGetFee (transactionGetFeeBasis(transaction), overflow); +} + +extern BREthereumFeeBasis +transactionGetFeeBasisLimit (BREthereumTransaction transaction) { + return (BREthereumFeeBasis) { + FEE_BASIS_GAS, + { transaction-> gasLimit, transaction->gasPrice } + }; } extern BREthereumEther transactionGetFeeLimit (BREthereumTransaction transaction, int *overflow) { - return etherCreate - (mulUInt256_Overflow(transaction->gasPrice.etherPerGas.valueInWEI, - createUInt256 (transaction->gasLimit.amountOfGas), - overflow)); + return feeBasisGetFee (transactionGetFeeBasisLimit(transaction), overflow); } extern BREthereumGasPrice @@ -351,7 +375,8 @@ transactionRlpEncode(BREthereumTransaction transaction, // scheme using v = 27 and v = 28 remains valid and continues to operate under the same rules // as it does now. - transaction->chainId = networkGetChainId(network); + if (ETHEREUM_BOOLEAN_IS_FALSE(transactionIsSigned(transaction))) + transaction->chainId = networkGetChainId(network); switch (type) { case RLP_TYPE_TRANSACTION_UNSIGNED: @@ -379,7 +404,7 @@ transactionRlpEncode(BREthereumTransaction transaction, // For ARCHIVE add in a few things beyond 'SIGNED / NETWORK' if (RLP_TYPE_ARCHIVE == type) { - items[ 9] = addressRlpEncode(transaction->sourceAddress, coder); + items[ 9] = addressRlpEncode(&transaction->sourceAddress, coder); items[10] = hashRlpEncode(transaction->hash, coder); items[11] = transactionStatusRLPEncode(transaction->status, coder); itemsCount += 3; @@ -446,6 +471,13 @@ transactionRlpDecode (BRRlpItem item, transaction->signature.sig.vrs.v = (eipChainId > 30 ? eipChainId - 8 - 2 * transaction->chainId : eipChainId); + if (transaction->signature.sig.vrs.v != 0x1b && transaction->signature.sig.vrs.v != 0x1c) { + transaction->chainId = networkGetChainIdOld(network); + transaction->signature.sig.vrs.v = (eipChainId > 30 + ? eipChainId - 8 - 2 * transaction->chainId + : eipChainId); + assert(transaction->signature.sig.vrs.v == 27 || transaction->signature.sig.vrs.v == 28); + } BRRlpData rData = rlpDecodeBytesSharedDontRelease (coder, items[7]); assert (32 >= rData.bytesCount); @@ -460,12 +492,17 @@ transactionRlpDecode (BRRlpItem item, } switch (type) { - case RLP_TYPE_ARCHIVE: - // Extract the archive-specific data - transaction->sourceAddress = addressRlpDecode(items[9], coder); - transaction->hash = hashRlpDecode(items[10], coder); - transaction->status = transactionStatusRLPDecode(items[11], NULL, coder); - break; + case RLP_TYPE_ARCHIVE: { + // Extract the archive-specific data + BREthereumAddress *tmpAddress = addressRlpDecode(items[9], coder); + if (NULL != tmpAddress) { + transaction->sourceAddress = *tmpAddress; + free(tmpAddress); + } + transaction->hash = hashRlpDecode(items[10], coder); + transaction->status = transactionStatusRLPDecode(items[11], NULL, coder); + break; + } case RLP_TYPE_TRANSACTION_SIGNED: { // With a SIGNED RLP encoding, we can extract the source address and compute the hash. @@ -595,7 +632,7 @@ transactionShow (BREthereumTransaction transaction, const char *topic) { int overflow; char *hash = hashAsString (transaction->hash); - char *source = addressGetEncodedString(transaction->sourceAddress, 1); + char *source = addressGetEncodedString(&transaction->sourceAddress, 1); char *target = addressGetEncodedString(transactionGetTargetAddress(transaction), 1); char *amount = etherGetValueString (transactionGetAmount(transaction), ETHER); char *gasP = etherGetValueString (transactionGetGasPrice(transaction).etherPerGas, GWEI); @@ -625,9 +662,9 @@ transactionShow (BREthereumTransaction transaction, const char *topic) { char *funcAddr = functionERC20TransferDecodeAddress (function, transaction->data); char *funcAmt = coerceString(funcAmount, 10); - BREthereumToken token = tokenLookup(target); + // BREthereumToken token = tokenLookup(target); - eth_log (topic, " Token : %s", (NULL == token ? "???" : tokenGetSymbol(token))); + eth_log (topic, " Token : %s", target); // (NULL == token ? "???" : tokenGetSymbol(token))); eth_log (topic, " TokFnc: %s", "erc20 transfer"); eth_log (topic, " TokAmt: %s", funcAmt); eth_log (topic, " TokAdr: %s", funcAddr); @@ -636,8 +673,9 @@ transactionShow (BREthereumTransaction transaction, const char *topic) { } free (totalWEI); free (total); free (fee); free (gasP); - free (amount); free (target); free (source); free (hash); - + free (amount); free (hash); + if (target) free (target); + if (source) free (source); } extern void diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.h index 10933a81a..f38806ca1 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransaction.h @@ -1,9 +1,9 @@ // // BBREthereumTransaction.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -45,7 +45,7 @@ typedef struct BREthereumTransactionRecord *BREthereumTransaction; extern BREthereumTransaction transactionCreate(BREthereumAddress sourceAddress, - BREthereumAddress targetAddress, + BREthereumAddress *targetAddress, BREthereumEther amount, BREthereumGasPrice gasPrice, BREthereumGas gasLimit, @@ -64,7 +64,7 @@ transactionReleaseForSet (void *ignore, void *item); extern BREthereumAddress transactionGetSourceAddress(BREthereumTransaction transaction); -extern BREthereumAddress +extern BREthereumAddress* transactionGetTargetAddress(BREthereumTransaction transaction); extern BREthereumBoolean @@ -75,15 +75,28 @@ extern BREthereumEther transactionGetAmount(BREthereumTransaction transaction); /** - * Return the fee (in Ether) for transaction. If the transaction is confirmed (aka blocked) then - * the value returned is the actual fee paid (as gasUsed * gasPrice); if the transaction is not - * confirmed then an estimated fee is returned (as gasEstimate * gasPrice). + * Return the feeBais for transaction. If the transaction is confirmed (aka blocked) then + * the value returned is the actual feeBasis paid {gasUsed, gasPrice}; if the transaction is not + * confirmed then an estimated feeBasis is returned {gasEstimate, gasPrice}. + */ +extern BREthereumFeeBasis +transactionGetFeeBasis (BREthereumTransaction transaction); + +/** + * Return the fee (in Ether) for transaction based on `transactionGetFeeBasis()` */ extern BREthereumEther transactionGetFee (BREthereumTransaction transaction, int *overflow); /** - * Return the maximum fee (in Ether) for transaction (as gasLimit * gasPrice). + * Return the feeBasis for transaction {gasLimit, gasPrice} + */ +extern BREthereumFeeBasis +transactionGetFeeBasisLimit (BREthereumTransaction transaction); + +/** + * Return the maximum fee (in Ether) for transaction (as gasLimit * gasPrice) from + * transactionGetFeeBasisLimit() */ extern BREthereumEther transactionGetFeeLimit (BREthereumTransaction transaction, int *overflow); diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.c index ddfd8d807..c8ac42569 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/10/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.h index 9fa4acb18..0517091f8 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionReceipt.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/10/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.c b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.c index bec6f1f65..8a9bd558b 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.c @@ -3,13 +3,12 @@ // BRCore // // Created by Ed Gamble on 5/15/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. #include -#include #include #include "BREthereumTransactionStatus.h" @@ -60,8 +59,7 @@ transactionStatusCreateErrored (BREthereumTransactionErrorType type, BREthereumTransactionStatus status; status.type = TRANSACTION_STATUS_ERRORED; status.u.errored.type = type; - strncpy (status.u.errored.detail, detail, sizeof(status.u.errored.detail)); - status.u.errored.detail[TRANSACTION_STATUS_DETAIL_BYTES] = '\0'; + strlcpy (status.u.errored.detail, detail, TRANSACTION_STATUS_DETAIL_BYTES); return status; } @@ -100,7 +98,6 @@ transactionStatusEqual (BREthereumTransactionStatus ts1, 0 == strcmp (ts1.u.errored.detail, ts2.u.errored.detail)))); } - extern BREthereumTransactionErrorType lookupTransactionErrorType (const char *reasons[], const char *reason) { @@ -114,9 +111,6 @@ lookupTransactionErrorType (const char *reasons[], return TRANSACTION_ERROR_UNKNOWN; } -// -// NOTE: THIS IS LES SPECIFIC -// extern BREthereumTransactionStatus transactionStatusRLPDecode (BRRlpItem item, const char *reasons[], @@ -149,13 +143,20 @@ transactionStatusRLPDecode (BRRlpItem item, case TRANSACTION_STATUS_INCLUDED: { size_t othersCount; const BRRlpItem *others = rlpDecodeList(coder, items[1], &othersCount); - assert (3 == othersCount); + + // The 'encode' function produces '5' others; however, for consistency with delivered + // code with an existing archival value, we keep '3' around. + assert (5 == othersCount || 3 == othersCount); return transactionStatusCreateIncluded (hashRlpDecode(others[0], coder), rlpDecodeUInt64(coder, others[1], 0), rlpDecodeUInt64(coder, others[2], 0), - TRANSACTION_STATUS_BLOCK_TIMESTAMP_UNKNOWN, - gasCreate(0)); + (3 == othersCount + ? TRANSACTION_STATUS_BLOCK_TIMESTAMP_UNKNOWN + : rlpDecodeUInt64(coder, others[3], 0)), + (3 == othersCount + ? gasCreate(0) + : gasRlpDecode(others[4], coder))); } case TRANSACTION_STATUS_ERRORED: { @@ -168,9 +169,6 @@ transactionStatusRLPDecode (BRRlpItem item, } } -// -// NOTE: THIS IS LES SPECIFIC -// extern BRRlpItem transactionStatusRLPEncode (BREthereumTransactionStatus status, BRRlpCoder coder) { @@ -187,10 +185,12 @@ transactionStatusRLPEncode (BREthereumTransactionStatus status, break; case TRANSACTION_STATUS_INCLUDED: - items[1] = rlpEncodeList(coder, 3, - hashRlpEncode(status.u.included.blockHash, coder), - rlpEncodeUInt64(coder, status.u.included.blockNumber, 0), - rlpEncodeUInt64(coder, status.u.included.transactionIndex, 0)); + items[1] = rlpEncodeList (coder, 5, + hashRlpEncode(status.u.included.blockHash, coder), + rlpEncodeUInt64(coder, status.u.included.blockNumber, 0), + rlpEncodeUInt64(coder, status.u.included.transactionIndex, 0), + rlpEncodeUInt64(coder, status.u.included.blockTimestamp, 0), + gasRlpEncode(status.u.included.gasUsed, coder)); items[2] = rlpEncodeString(coder, ""); break; diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.h b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.h index 8be9505f7..21537e8ee 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.h +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/BREthereumTransactionStatus.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/15/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/blockchain/testBc.c b/ThirdParty/breadwallet-core/ethereum/blockchain/testBc.c index a2950c3be..60e9985f6 100644 --- a/ThirdParty/breadwallet-core/ethereum/blockchain/testBc.c +++ b/ThirdParty/breadwallet-core/ethereum/blockchain/testBc.c @@ -3,13 +3,15 @@ // CoreTests // // Created by Ed Gamble on 7/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #include #include #include -#include "BREthereumBlockChain.h" +#include "ethereum/blockchain/BREthereumBlockChain.h" // // Bloom Test diff --git a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.c b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.c index 88d0a10ad..3b48704d2 100644 --- a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.c +++ b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.c @@ -1,9 +1,9 @@ // // BREthereumContract -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/5/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.h b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.h index 1346dd165..b6a7d3e6f 100644 --- a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.h +++ b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumContract.h @@ -1,9 +1,9 @@ // // BREthereumContract -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/5/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.c b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.c index 941397222..97865cf5f 100644 --- a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.c +++ b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.c @@ -1,9 +1,9 @@ // // BREthereumToken -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/25/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -70,11 +70,22 @@ tokenGetAddressRaw (BREthereumToken token) { return token->raw; } +extern BREthereumAddress* +tokenGetAddressPtrRaw (BREthereumToken token) { + return &token->raw; +} + extern const char * tokenGetAddress(BREthereumToken token) { return token->address; } +extern BREthereumBoolean +tokenHasAddress (BREthereumToken token, + const char *address) { + return addressEqual (token->raw, addressCreate (address)); +} + extern const char * tokenGetSymbol(BREthereumToken token) { return token->symbol; @@ -111,6 +122,59 @@ tokenGetContract(BREthereumToken token) { return contractERC20; } +extern BREthereumHash +tokenGetHash (BREthereumToken token) { + return addressGetHash(token->raw); +} + +extern BREthereumToken +tokenCreate (const char *address, + const char *symbol, + const char *name, + const char *description, + int decimals, + BREthereumGas defaultGasLimit, + BREthereumGasPrice defaultGasPrice) { + BREthereumToken token = malloc (sizeof(struct BREthereumTokenRecord)); + + token->address = strdup (address); + token->symbol = strdup (symbol); + token->name = strdup (name); + token->description = strdup (description); + token->decimals = decimals; + token->gasLimit = defaultGasLimit; + token->gasPrice = defaultGasPrice; + token->raw = addressCreate (address); + + return token; +} + +extern void +tokenRelease (BREthereumToken token) { + free (token->address); + free (token->symbol); + free (token->name); + free (token->description); + free (token); +} + +extern void +tokenUpdate (BREthereumToken token, + const char *symbol, + const char *name, + const char *description, + int decimals, + BREthereumGas defaultGasLimit, + BREthereumGasPrice defaultGasPrice) { + + if (0 != strcasecmp (symbol , token->symbol )) { free (token->symbol ); token->symbol = strdup (symbol ); } + if (0 != strcasecmp (name , token->name )) { free (token->name ); token->name = strdup (name ); } + if (0 != strcasecmp (description, token->description)) { free (token->description); token->description = strdup (description); } + token->decimals = decimals; + token->gasLimit = defaultGasLimit; + token->gasPrice = defaultGasPrice; +} + static inline size_t tokenHashValue (const void *t) { @@ -123,6 +187,52 @@ tokenHashEqual (const void *t1, const void *t2) { ((BREthereumToken) t2)->raw); } +extern BRSetOf(BREthereumToken) +tokenSetCreate (size_t capacity) { + return BRSetNew (tokenHashValue, tokenHashEqual, capacity); +} + +extern BRRlpItem +tokenEncode (BREthereumToken token, + BRRlpCoder coder) { + return rlpEncodeList (coder, 7, + addressRlpEncode (&token->raw, coder), + rlpEncodeString (coder, token->symbol), + rlpEncodeString (coder, token->name), + rlpEncodeString (coder, token->description), + rlpEncodeUInt64 (coder, token->decimals, 0), + gasRlpEncode (token->gasLimit, coder), + gasPriceRlpEncode (token->gasPrice, coder)); +} + +extern BREthereumToken +tokenDecode (BRRlpItem item, + BRRlpCoder coder) { + BREthereumToken token = malloc (sizeof(struct BREthereumTokenRecord)); + + size_t itemsCount = 0; + const BRRlpItem *items = rlpDecodeList (coder, item, &itemsCount); + assert (7 == itemsCount); + + BREthereumAddress *addr = addressRlpDecode (items[0], coder); + if (NULL != addr) { + token->raw = *addr; + free(addr); + } else { + token->raw = EMPTY_ADDRESS_INIT; + } + token->address = addressGetEncodedString(&token->raw, 1); + token->symbol = rlpDecodeString (coder, items[1]); + token->name = rlpDecodeString (coder, items[2]); + token->description = rlpDecodeString(coder, items[3]); + token->decimals = (unsigned int) rlpDecodeUInt64 (coder, items[4], 0); + token->gasLimit = gasRlpDecode (items[5], coder); + token->gasPrice = gasPriceRlpDecode (items[6], coder); + + return token; +} + + // // Token Quantity // @@ -212,76 +322,3 @@ return "static struct BREthereumTokenRecord tokens[] = \n{" + var result = tokensInJSONToC (tokens); console.log (result) */ - -static BRSet *tokenSet = NULL; - -static void -tokenInitializeIfAppropriate (void) { - if (NULL == tokenSet) { - tokenSet = BRSetNew(tokenHashValue, tokenHashEqual, TOKEN_DEFAULT_INITIALIZATION_COUNT); - } -} - -extern BREthereumToken -tokenLookupByAddress (BREthereumAddress address) { - tokenInitializeIfAppropriate(); - return (BREthereumToken) BRSetGet(tokenSet, &address); -} - -extern BREthereumToken -tokenLookup(const char *address) { - return ((NULL == address || '\0' == address[0]) - ? NULL - : tokenLookupByAddress (addressCreate(address))); -} - -extern int -tokenCount() { - tokenInitializeIfAppropriate(); - return (int) BRSetCount (tokenSet); -} - -extern BREthereumToken * -tokenGetAll (void) { - int tokensCount = tokenCount(); - BREthereumToken *tokens = calloc (tokensCount, sizeof (BREthereumToken)); - BRSetAll (tokenSet, (void**) tokens, tokensCount); - return tokens; -} - -extern void -tokenInstall (const char *address, - const char *symbol, - const char *name, - const char *description, - int decimals, - BREthereumGas defaultGasLimit, - BREthereumGasPrice defaultGasPrice) { - BREthereumAddress raw = addressCreate (address); - BREthereumToken token = tokenLookupByAddress (raw); - - if (NULL == token) { - token = malloc (sizeof(struct BREthereumTokenRecord)); - - token->address = strdup (address); - token->symbol = strdup (symbol); - token->name = strdup (name); - token->description = strdup (description); - token->decimals = decimals; - token->gasLimit = defaultGasLimit; - token->gasPrice = defaultGasPrice; - - token->raw = raw; - BRSetAdd (tokenSet, token); - } - - else { - if (0 != strcasecmp (address , token->address )) { free (token->address ); token->address = strdup (address ); } - if (0 != strcasecmp (symbol , token->symbol )) { free (token->symbol ); token->symbol = strdup (symbol ); } - if (0 != strcasecmp (name , token->name )) { free (token->name ); token->name = strdup (name ); } - if (0 != strcasecmp (description, token->description)) { free (token->description); token->description = strdup (description); } - token->decimals = decimals; - token->gasLimit = defaultGasLimit; - token->gasPrice = defaultGasPrice; - } -} diff --git a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.h b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.h index 91c63cbbd..806953794 100644 --- a/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.h +++ b/ThirdParty/breadwallet-core/ethereum/contract/BREthereumToken.h @@ -1,9 +1,9 @@ // // BREthereumToken -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/15/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -30,13 +30,23 @@ typedef struct BREthereumTokenRecord *BREthereumToken; extern BREthereumAddress tokenGetAddressRaw (BREthereumToken token); - + +extern BREthereumAddress* +tokenGetAddressPtrRaw (BREthereumToken token); + /** * Return the token address as a '0x'-prefixed string. DO NOT FREE THIS. */ extern const char * tokenGetAddress (BREthereumToken token); +/** + * Check if `token` has `address` which must be a '0x'-prefixed hex string. + */ +extern BREthereumBoolean +tokenHasAddress (BREthereumToken token, + const char *address); + extern const char * tokenGetSymbol (BREthereumToken token); @@ -58,30 +68,40 @@ tokenGetGasPrice (BREthereumToken token); extern BREthereumContract tokenGetContract (BREthereumToken token); -extern BREthereumToken -tokenLookupByAddress (BREthereumAddress address); - -extern BREthereumToken -tokenLookup (const char *address); +extern BREthereumHash +tokenGetHash (BREthereumToken token); -extern int -tokenCount (void); +extern BREthereumToken +tokenCreate (const char *address, + const char *symbol, + const char *name, + const char *description, + int decimals, + BREthereumGas defaultGasLimit, + BREthereumGasPrice defaultGasPrice); -/** - * Return a newly allocated array with references to all tokens - */ -extern BREthereumToken * -tokenGetAll (void); +extern void +tokenRelease (BREthereumToken token); extern void -tokenInstall (const char *address, - const char *symbol, - const char *name, - const char *description, - int decimals, - BREthereumGas defaultGasLimit, - BREthereumGasPrice defaultGasPrice); +tokenUpdate (BREthereumToken token, + const char *symbol, + const char *name, + const char *description, + int decimals, + BREthereumGas defaultGasLimit, + BREthereumGasPrice defaultGasPrice); + +extern BRRlpItem +tokenEncode (BREthereumToken token, + BRRlpCoder coder); + +extern BREthereumToken +tokenDecode (BRRlpItem item, + BRRlpCoder coder); +extern BRSetOf(BREthereumToken) +tokenSetCreate (size_t capacity); // // Token Quantity diff --git a/ThirdParty/breadwallet-core/ethereum/contract/testContract.c b/ThirdParty/breadwallet-core/ethereum/contract/testContract.c index bb665663a..99ec34c3d 100644 --- a/ThirdParty/breadwallet-core/ethereum/contract/testContract.c +++ b/ThirdParty/breadwallet-core/ethereum/contract/testContract.c @@ -3,14 +3,16 @@ // CoreTests // // Created by Ed Gamble on 7/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #include #include -#include "BREthereumContract.h" -#include "BREthereumToken.h" +#include "ethereum/contract/BREthereumContract.h" +#include "ethereum/contract/BREthereumToken.h" #if defined (BITCOIN_TESTNET) const char *tokenBRDAddress = "0x7108ca7c4718efa810457f228305c9c71390931a"; // testnet @@ -29,6 +31,14 @@ static const char *tokenTSTAddress = "0x3efd578b271d034a69499e4a2d933c631d44b9ad static const char *tokenEOSAddress = "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"; static const char *tokenKNCAddress = "0xdd974d5c2e2928dea5f71b9825b8b646686bd200"; +static BRSetOf(BREthereumToken) tokens; + +extern BREthereumToken +tokenLookupTest (const char *address) { + BREthereumAddress addr = addressCreate(address); + return BRSetGet (tokens, &addr); +} + extern void installTokensForTest (void) { static int needInstall = 1; @@ -37,37 +47,48 @@ installTokensForTest (void) { BREthereumGas defaultGasLimit = gasCreate(TOKEN_BRD_DEFAULT_GAS_LIMIT); BREthereumGasPrice defaultGasPrice = gasPriceCreate(etherCreateNumber(TOKEN_BRD_DEFAULT_GAS_PRICE_IN_WEI_UINT64, WEI)); - tokenInstall (tokenBRDAddress, - "BRD", - "BRD Token", - "", - 18, - defaultGasLimit, - defaultGasPrice); + + tokens = tokenSetCreate(10); + + BREthereumToken token; + + token = tokenCreate (tokenBRDAddress, + "BRD", + "BRD Token", + "", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); + #if defined (BITCOIN_DEBUG) - tokenInstall (tokenTSTAddress, - "TST", - "Test Standard Token", - "TeST Standard Token (TST) for TeSTing (TST)", - 18, - defaultGasLimit, - defaultGasPrice); + token = tokenCreate (tokenTSTAddress, + "TST", + "Test Standard Token", + "TeST Standard Token (TST) for TeSTing (TST)", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); + #endif - tokenInstall (tokenEOSAddress, - "EOS", - "EOS Token", - "", - 18, - defaultGasLimit, - defaultGasPrice); - - tokenInstall (tokenKNCAddress, - "KNC", - "KNC token", - "", - 18, - defaultGasLimit, - defaultGasPrice); + token = tokenCreate (tokenEOSAddress, + "EOS", + "EOS Token", + "", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); + + token = tokenCreate (tokenKNCAddress, + "KNC", + "KNC token", + "", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); } static void @@ -78,7 +99,10 @@ runTokenParseTests () { // UInt256 valueParseInt = parseTokenQuantity("5968770000000000000000", TOKEN_QUANTITY_TYPE_INTEGER, 18, &error); // UInt256 valueParseDec = parseTokenQuantity("5968770000000000000000", TOKEN_QUANTITY_TYPE_INTEGER, 18, &error); - BREthereumToken token = tokenLookup(tokenBRDAddress); + BREthereumAddress address; + + address = addressCreate (tokenBRDAddress); + BREthereumToken token = BRSetGet (tokens, &address); BREthereumTokenQuantity valueInt = createTokenQuantityString(token, "5968770000000000000000", TOKEN_QUANTITY_TYPE_INTEGER, &status); assert (CORE_PARSE_OK == status && eqUInt256(value, valueInt.valueAsInteger)); @@ -90,17 +114,22 @@ void runTokenLookupTests () { printf ("==== Token\n"); BREthereumToken token; + BREthereumAddress address; + + address = addressCreate (tokenBRDAddress); - token = tokenLookup (tokenBRDAddress); // BRD + token = BRSetGet (tokens, &address); // BRD assert (NULL != token); #if defined (BITCOIN_DEBUG) - token = tokenLookup(tokenTSTAddress); // TST: mainnet + address = addressCreate (tokenTSTAddress); + token = BRSetGet (tokens, &address); // TST assert (NULL != token); #endif #if !defined(BITCOIN_TESTNET) - token = tokenLookup("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"); // EOI + address = addressCreate ("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0"); + token = BRSetGet (tokens, &address); // EOI assert (NULL != token); #endif } diff --git a/ThirdParty/breadwallet-core/ethereum/event/BREvent.c b/ThirdParty/breadwallet-core/ethereum/event/BREvent.c index a5bb54d61..769c34499 100644 --- a/ThirdParty/breadwallet-core/ethereum/event/BREvent.c +++ b/ThirdParty/breadwallet-core/ethereum/event/BREvent.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -62,24 +62,21 @@ struct BREventHandlerRecord { /// BREventAlarmId timeoutAlarmId; - // Pthread - + // The thread handling events. pthread_t thread; - pthread_cond_t cond; - pthread_mutex_t lock; - pthread_mutex_t lockOnStartStop; - pthread_mutex_t *lockToUse; + // A lock on internal state + pthread_mutex_t lock; - // Boolean to identify if the thread should quit. - int threadQuit; + // A lock for protecting the dispatch call. Optional but recommended. + pthread_mutex_t *lockOnDispatch; }; extern BREventHandler eventHandlerCreate (const char *name, const BREventType *types[], unsigned int typesCount, - pthread_mutex_t *lock) { + pthread_mutex_t *lockOnDispatch) { BREventHandler handler = calloc (1, sizeof (struct BREventHandlerRecord)); // Fill in the timeout event. Leave the dispatcher NULL until the dispatcher is provided. @@ -92,8 +89,7 @@ eventHandlerCreate (const char *name, handler->types = types; handler->eventSize = handler->timeoutEventType.eventSize; - strncpy (handler->name, name, PTHREAD_NAME_SIZE); - handler->name[PTHREAD_NAME_SIZE - 1] = '\0'; + strlcpy (handler->name, name, PTHREAD_NAME_SIZE); // Update `eventSize` with the largest sized event for (int i = 0; i < handler->typesCount; i++) { @@ -104,35 +100,15 @@ eventHandlerCreate (const char *name, } handler->timeoutAlarmId = ALARM_ID_NONE; - - // Create the PTHREAD CONDition variable - { - pthread_condattr_t attr; - pthread_condattr_init(&attr); - pthread_cond_init(&handler->cond, &attr); - pthread_condattr_destroy(&attr); - } + handler->lockOnDispatch = lockOnDispatch; // Create the PTHREAD LOCK variable - if (NULL != lock) handler->lockToUse = lock; - else { - // The cacheLock is a normal, non-recursive lock - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&handler->lock, &attr); - pthread_mutexattr_destroy(&attr); - - handler->lockToUse = &handler->lock; - } - - // Create the PTHREAD LOCK-ON-START-STOP variable { // The cacheLock is a normal, non-recursive lock pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); - pthread_mutex_init(&handler->lockOnStartStop, &attr); + pthread_mutex_init(&handler->lock, &attr); pthread_mutexattr_destroy(&attr); } @@ -149,15 +125,12 @@ eventHandlerSetTimeoutDispatcher (BREventHandler handler, unsigned int timeInMilliseconds, BREventDispatcher dispatcher, BREventTimeoutContext context) { - pthread_mutex_lock (handler->lockToUse); + pthread_mutex_lock (&handler->lock); handler->timeout.tv_sec = timeInMilliseconds / 1000; handler->timeout.tv_nsec = 1000000 * (timeInMilliseconds % 1000); handler->timeoutContext = context; handler->timeoutEventType.eventDispatcher = dispatcher; - pthread_mutex_unlock (handler->lockToUse); - - // Signal an event - so that the 'timedwait' starts. - pthread_cond_signal(&handler->cond); + pthread_mutex_unlock (&handler->lock); } static void @@ -169,48 +142,54 @@ eventHandlerAlarmCallback (BREventHandler handler, eventHandlerSignalEventOOB (handler, (BREvent*) &event); } - typedef void* (*ThreadRoutine) (void*); static void * eventHandlerThread (BREventHandler handler) { - #if defined (__DARWIN__) pthread_setname_np(handler->name); #else - pthread_setname_np(handler->thread, handler->name); + pthread_setname_np(pthread_self(), handler->name); #endif - pthread_mutex_lock (handler->lockToUse); - - handler->threadQuit = 0; + int timeToQuit = 0; - while (!handler->threadQuit) { + while (!timeToQuit) { // Check for a queued event - switch (eventQueueDequeue(handler->queue, handler->scratch)) { - case EVENT_STATUS_SUCCESS: { - // If we have one, dispatch - BREventType *type = handler->scratch->type; - type->eventDispatcher (handler, handler->scratch); + switch (eventQueueDequeueWait (handler->queue, handler->scratch)) { + case EVENT_STATUS_SUCCESS: + // We got an event, dispatch + if (handler->lockOnDispatch) pthread_mutex_lock (handler->lockOnDispatch); + handler->scratch->type->eventDispatcher (handler, handler->scratch); + if (handler->lockOnDispatch) pthread_mutex_unlock (handler->lockOnDispatch); + + // Yield here so that we don't have a situation where we repeatedly acquire + // the `lockOnDispatch`, thereby starving other threads, when there are many + // events queued (ex: on startup) +#if defined (__DARWIN__) + pthread_yield_np (); +#else + nanosleep (&(struct timespec) {0, 1}, NULL); // pthread_yield() isn't POSIX standard :( +#endif + break; + + case EVENT_STATUS_WAIT_ABORT: + timeToQuit = 1; + break; + + case EVENT_STATUS_WAIT_ERROR: + // Just try again. break; - } case EVENT_STATUS_NOT_STARTED: case EVENT_STATUS_UNKNOWN_TYPE: case EVENT_STATUS_NULL_EVENT: - // impossible? - // fall through - case EVENT_STATUS_NONE_PENDING: - // ... otherwise wait for an event ... - pthread_cond_wait(&handler->cond, handler->lockToUse); + assert (0); break; } } - // Requires as `cond_wait` takes its mutex when signalled. - pthread_mutex_unlock (handler->lockToUse); - return NULL; } @@ -221,10 +200,7 @@ eventHandlerDestroy (BREventHandler handler) { // ... then kill assert (PTHREAD_NULL == handler->thread); - pthread_cond_destroy(&handler->cond); - if (handler->lockToUse == &handler->lock) - pthread_mutex_destroy(&handler->lock); - pthread_mutex_destroy(&handler->lockOnStartStop); + pthread_mutex_destroy(&handler->lock); // release memory eventQueueDestroy(handler->queue); @@ -245,7 +221,7 @@ eventHandlerDestroy (BREventHandler handler) { extern void eventHandlerStart (BREventHandler handler) { alarmClockCreateIfNecessary(1); - pthread_mutex_lock(&handler->lockOnStartStop); + pthread_mutex_lock(&handler->lock); if (PTHREAD_NULL == handler->thread) { // If we have an timeout event dispatcher, then add an alarm. if (NULL != handler->timeoutEventType.eventDispatcher) { @@ -263,10 +239,17 @@ eventHandlerStart (BREventHandler handler) { pthread_attr_setstacksize(&attr, PTHREAD_STACK_SIZE); pthread_create(&handler->thread, &attr, (ThreadRoutine) eventHandlerThread, handler); + // "Before returning, a successful call to pthread_create() stores the ID of the new + // thread in the buffer pointed to by thread; this identifier is used to refer to the + // thread in subsequent calls to other pthreads functions." + // + // TODO: Handle an unsuccessfull call. + // assert (NULL != handler->thread); + pthread_attr_destroy(&attr); } } - pthread_mutex_unlock(&handler->lockOnStartStop); + pthread_mutex_unlock(&handler->lock); } @@ -283,28 +266,34 @@ eventHandlerStart (BREventHandler handler) { */ extern void eventHandlerStop (BREventHandler handler) { - pthread_mutex_lock(&handler->lockOnStartStop); + pthread_mutex_lock(&handler->lock); if (PTHREAD_NULL != handler->thread) { - pthread_mutex_lock (handler->lockToUse); - // Remove a timeout alarm, if it exists. if (ALARM_ID_NONE != handler->timeoutAlarmId) { alarmClockRemAlarm (alarmClock, handler->timeoutAlarmId); handler->timeoutAlarmId = ALARM_ID_NONE; } - // Quit the thread. - handler->threadQuit = 1; - pthread_cond_signal(&handler->cond); - pthread_mutex_unlock (handler->lockToUse); - pthread_join(handler->thread, NULL); + // Quit the thread by aborting the queue wait. + eventQueueDequeueWaitAbort (handler->queue); + + // Wait for the thread. + pthread_join (handler->thread, NULL); // A mini-race here? handler->thread = PTHREAD_NULL; - // Empty the queue completely. + // TODO: Empty the queue completely? Or not? + eventQueueDequeueWaitAbortReset (handler->queue); eventHandlerClear (handler); } - pthread_mutex_unlock(&handler->lockOnStartStop); + pthread_mutex_unlock(&handler->lock); +} + +extern int +eventHandlerIsCurrentThread (BREventHandler handler) { + // TODO(fix): This is a hack; fix the ordering such that `handler->thread` is + // is properly set by the time `eventHandlerThread()` runs (CORE-564) + return PTHREAD_NULL == handler->thread || pthread_self() == handler->thread; } extern int @@ -315,16 +304,14 @@ eventHandlerIsRunning (BREventHandler handler) { extern BREventStatus eventHandlerSignalEvent (BREventHandler handler, BREvent *event) { - eventQueueEnqueueTail(handler->queue, event); - pthread_cond_signal(&handler->cond); + eventQueueEnqueueTailSignal (handler->queue, event); return EVENT_STATUS_SUCCESS; } extern BREventStatus eventHandlerSignalEventOOB (BREventHandler handler, BREvent *event) { - eventQueueEnqueueHead(handler->queue, event); - pthread_cond_signal(&handler->cond); + eventQueueEnqueueHeadSignal (handler->queue, event); return EVENT_STATUS_SUCCESS; } diff --git a/ThirdParty/breadwallet-core/ethereum/event/BREvent.h b/ThirdParty/breadwallet-core/ethereum/event/BREvent.h index 27af52d3f..37a0d9296 100644 --- a/ThirdParty/breadwallet-core/ethereum/event/BREvent.h +++ b/ThirdParty/breadwallet-core/ethereum/event/BREvent.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -71,6 +71,8 @@ typedef enum { EVENT_STATUS_SUCCESS, EVENT_STATUS_NOT_STARTED, EVENT_STATUS_UNKNOWN_TYPE, + EVENT_STATUS_WAIT_ERROR, + EVENT_STATUS_WAIT_ABORT, EVENT_STATUS_NULL_EVENT, EVENT_STATUS_NONE_PENDING } BREventStatus; @@ -134,6 +136,9 @@ eventHandlerStart (BREventHandler handler); extern void eventHandlerStop (BREventHandler handler); +extern int +eventHandlerIsCurrentThread (BREventHandler handler); + extern int eventHandlerIsRunning (BREventHandler handler); diff --git a/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.c b/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.c index 8345b4d1c..07c15257a 100644 --- a/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.c +++ b/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -137,8 +137,12 @@ alarmIsPeriodic (BREventAlarm *alarm) { static void alarmPeriodUpdate (BREventAlarm *alarm) { - if (alarmIsPeriodic(alarm)) { - timespecInc(&alarm->expiration, &alarm->period); + timespecInc(&alarm->expiration, &alarm->period); + + // ensure that expiration does not occur in the past + struct timespec now = getTime(); + if (-1 == timespecCompare(&alarm->expiration, &now)) { + alarm->expiration = now; } } @@ -278,6 +282,13 @@ alarmClockThread (BREventAlarmClock clock) { switch (pthread_cond_timedwait (&clock->cond, &clock->lock, &clock->timeout)) { case ETIMEDOUT: { + // Check if alarm was removed while we slept... + if (0 == array_count(clock->alarms) || + 0 != timespecCompare(&clock->alarms[0].expiration, &clock->timeout)) { + // ... ignore the timeout, its alarm is for the birds now + break; + } + // If we timed-out, then get the alarm that has expired... BREventAlarm alarm = clock->alarms[0]; // ... and remove it from the clock's alarms (for now; if periodic, add it back) diff --git a/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.h b/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.h index dc1e923e6..e7b6f5af5 100644 --- a/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.h +++ b/ThirdParty/breadwallet-core/ethereum/event/BREventAlarm.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.c b/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.c index d17da5ea2..8b7a3ec64 100644 --- a/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.c +++ b/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -25,6 +25,12 @@ struct BREventQueueRecord { // If not provided with a lock, use this one. pthread_mutex_t lock; + // A 'cond var' + pthread_cond_t cond; + + // An 'abort wait' flag + int abort; + // The size of each event size_t size; }; @@ -35,7 +41,8 @@ eventQueueCreate (size_t size) { queue->pending = NULL; queue->available = NULL; - queue->size = size; + queue->abort = 0; + queue->size = size; for (int i = 0; i < EVENT_QUEUE_DEFAULT_INITIAL_CAPACITY; i++) { BREvent *event = calloc (1, queue->size); @@ -43,6 +50,14 @@ eventQueueCreate (size_t size) { queue->available = event; } + // Create the PTHREAD CONDition variable + { + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_cond_init(&queue->cond, &attr); + pthread_condattr_destroy(&attr); + } + { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); @@ -90,6 +105,7 @@ eventQueueDestroy (BREventQueue queue) { // Clear the pending and available queues. eventQueueClear (queue); + pthread_cond_destroy(&queue->cond); pthread_mutex_destroy(&queue->lock); memset (queue, 0, sizeof (struct BREventQueueRecord)); @@ -99,7 +115,8 @@ eventQueueDestroy (BREventQueue queue) { static void eventQueueEnqueue (BREventQueue queue, const BREvent *event, - int tail) { + int tail, + int signal) { pthread_mutex_lock(&queue->lock); // Get the next available event @@ -129,56 +146,107 @@ eventQueueEnqueue (BREventQueue queue, queue->pending = this; } + if (signal) pthread_cond_signal (&queue->cond); pthread_mutex_unlock(&queue->lock); } extern void eventQueueEnqueueTail (BREventQueue queue, const BREvent *event) { - eventQueueEnqueue (queue, event, 1); + eventQueueEnqueue (queue, event, 1, 0); } extern void eventQueueEnqueueHead (BREventQueue queue, const BREvent *event) { - eventQueueEnqueue (queue, event, 0); + eventQueueEnqueue (queue, event, 0, 0); +} +extern void +eventQueueEnqueueTailSignal (BREventQueue queue, + const BREvent *event) { + eventQueueEnqueue (queue, event, 1, 1); +} + +extern void +eventQueueEnqueueHeadSignal (BREventQueue queue, + const BREvent *event) { + eventQueueEnqueue (queue, event, 0, 1); +} + +static int +_eventQueueDequeue (BREventQueue queue, + BREvent *event) { + // Get the next pending event + BREvent *this = queue->pending; + + // if there is one, process it + if (NULL == this) return 0; + + // Remove `this` from the pending list. + queue->pending = this->next; + + // Fill in the provided event; + this->next = NULL; + memcpy (event, this, queue->size); + + // Return `this` to the available list. + this->next = queue->available; + queue->available = this; + + return 1; } extern BREventStatus eventQueueDequeue (BREventQueue queue, BREvent *event) { - BREventStatus status = EVENT_STATUS_SUCCESS; - + if (NULL == event) + return EVENT_STATUS_NULL_EVENT; + + pthread_mutex_lock (&queue->lock); + BREventStatus status = (_eventQueueDequeue (queue, event) + ? EVENT_STATUS_SUCCESS + : EVENT_STATUS_NONE_PENDING); + pthread_mutex_unlock(&queue->lock); + + return status; +} + +extern BREventStatus +eventQueueDequeueWait (BREventQueue queue, + BREvent *event) { if (NULL == event) return EVENT_STATUS_NULL_EVENT; - - pthread_mutex_lock(&queue->lock); - - // Get the next pending event - BREvent *this = queue->pending; - - // there is none, just return - if (NULL == this) - status = EVENT_STATUS_NONE_PENDING; - - // otherwise process it. - else { - // Remove `this` from the pending list. - queue->pending = this->next; - - // Fill in the provided event; - this->next = NULL; - memcpy (event, this, queue->size); - - // Return `this` to the available list. - this->next = queue->available; - queue->available = this; - } + + BREventStatus status = EVENT_STATUS_SUCCESS; + + pthread_mutex_lock (&queue->lock); + while (!queue->abort && !_eventQueueDequeue (queue, event)) + if (0 != pthread_cond_wait (&queue->cond, &queue->lock)) { + status = EVENT_STATUS_WAIT_ERROR; + break; /* from while */ + } + if (queue->abort) status = EVENT_STATUS_WAIT_ABORT; pthread_mutex_unlock(&queue->lock); - + return status; } +extern void +eventQueueDequeueWaitAbort (BREventQueue queue) { + pthread_mutex_lock (&queue->lock); + queue->abort = 1; + pthread_cond_signal (&queue->cond); + pthread_mutex_unlock (&queue->lock); +} + +extern void +eventQueueDequeueWaitAbortReset (BREventQueue queue) { + pthread_mutex_lock (&queue->lock); + queue->abort = 0; + pthread_cond_signal (&queue->cond); + pthread_mutex_unlock (&queue->lock); +} + extern int eventQueueHasPending (BREventQueue queue) { int pending = 0; diff --git a/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.h b/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.h index cc16abe9f..9c4108326 100644 --- a/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.h +++ b/ThirdParty/breadwallet-core/ethereum/event/BREventQueue.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -33,14 +33,33 @@ eventQueueDestroy (BREventQueue queue); extern void eventQueueEnqueueTail (BREventQueue queue, const BREvent *event); + extern void eventQueueEnqueueHead (BREventQueue queue, const BREvent *event); - extern BREventStatus eventQueueDequeue (BREventQueue queue, BREvent *event); +extern void +eventQueueEnqueueTailSignal (BREventQueue queue, + const BREvent *event); + +extern void +eventQueueEnqueueHeadSignal (BREventQueue queue, + const BREvent *event); + + +extern BREventStatus +eventQueueDequeueWait (BREventQueue queue, + BREvent *event); + +extern void +eventQueueDequeueWaitAbort (BREventQueue queue); + +extern void +eventQueueDequeueWaitAbortReset (BREventQueue queue); + extern int eventQueueHasPending (BREventQueue queue); diff --git a/ThirdParty/breadwallet-core/ethereum/event/testEvent.c b/ThirdParty/breadwallet-core/ethereum/event/testEvent.c index 8adf444fa..fce7523be 100644 --- a/ThirdParty/breadwallet-core/ethereum/event/testEvent.c +++ b/ThirdParty/breadwallet-core/ethereum/event/testEvent.c @@ -3,14 +3,16 @@ // CoreTests // // Created by Ed Gamble on 7/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #include #include #include -#include "BREvent.h" -#include "BREventAlarm.h" +#include "ethereum/event/BREvent.h" +#include "ethereum/event/BREventAlarm.h" static pthread_cond_t testEventAlarmConditional = PTHREAD_COND_INITIALIZER; static pthread_mutex_t testEventAlarmMutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.c index 1c382d932..98e6d177e 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.c @@ -1,9 +1,9 @@ // // BBREthereumAddress.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -11,11 +11,9 @@ #include #include #include -#include "support/BRCrypto.h" #include "support/BRKey.h" #include "support/BRBIP32Sequence.h" #include "support/BRBIP39Mnemonic.h" -#include "support/BRBase58.h" #include "support/BRBIP39WordsEn.h" #include "ethereum/base/BREthereumBase.h" #include "BREthereumAccount.h" @@ -122,7 +120,7 @@ addressDetailFillKey(BREthereumAddressDetail *address, const BRKey *key, uint32_ address->raw = addressCreateKey(key); - char *string = addressGetEncodedString(address->raw, 1); + char *string = addressGetEncodedString(&address->raw, 1); memcpy (address->string, string, 42); address->string[42] = '\0'; free (string); @@ -139,8 +137,9 @@ addressDetailFillSeed (BREthereumAddressDetail *address, UInt512 seed, uint32_t // "The private key must be 32 bytes and not begin with 0x00 and the public one must be // uncompressed and 64 bytes long or 65 with the constant 0x04 prefix. More on that in the // next section. ... - - assert (65 == BRKeyPubKey(&key, NULL, 0)); + + size_t keyLen = BRKeyPubKey(&key, NULL, 0); + assert (65 == keyLen); // "The public key is what we need in order to derive its Ethereum address. Every EC public key // begins with the 0x04 prefix before giving the location of the two point on the curve. You diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.h index 91ccb6aef..969f65843 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAccount.h @@ -1,9 +1,9 @@ // // BBREthereumAddress.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.c index b31a3a0e2..63faef782 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.c @@ -1,9 +1,9 @@ // // BREthereumamount -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/25/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.h index 608bb3132..2974c069e 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumAmount.h @@ -1,9 +1,9 @@ // // BREthereumAmount -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/25/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumBase.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumBase.h index ffc1d8cd7..4d755fbb7 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumBase.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumBase.h @@ -3,8 +3,10 @@ // BRCore // // Created by Ed Gamble on 11/19/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #ifndef BREthereumBase_h #define BREthereumBase_h @@ -108,31 +110,17 @@ typedef struct BREthereumWalletRecord *BREthereumWallet; */ typedef struct BREthereumEWMRecord *BREthereumEWM; -typedef enum { - FEE_BASIS_NONE, - FEE_BASIS_GAS -} BREthereumFeeBasisType; - -typedef struct { - BREthereumFeeBasisType type; - union { - struct { - BREthereumGas limit; - BREthereumGasPrice price; - } gas; - } u; -} BREthereumFeeBasis; - -extern BREthereumFeeBasis -feeBasisCreate (BREthereumGas limit, - BREthereumGasPrice price); - // // Errors - Right Up Front - 'The Emperor Has No Clothes' ?? // typedef enum { SUCCESS, + // Generic catch-all failure. This should only be used as if creating a + // specific error code does not make sense (you really should create + // a specifc error code...). + ERROR_FAILED, + // Reference access ERROR_UNKNOWN_NODE, ERROR_UNKNOWN_TRANSACTION, diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumClient.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumClient.h index 959a00d90..6cc55b646 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumClient.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumClient.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 11/20/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -12,11 +12,16 @@ #define BR_Ethereum_Client_H #include "BREthereumBase.h" +#include "BRCryptoSync.h" #ifdef __cplusplus extern "C" { #endif + // Cookies are used as markers to match up an asynchronous operation + // request with its corresponding event. + typedef void *BREthereumCookie; + // // BREthereumClient // @@ -28,7 +33,6 @@ extern "C" { // typedef void *BREthereumClientContext; - /// MARK: - Balance typedef void @@ -55,7 +59,7 @@ extern "C" { extern BREthereumStatus ewmAnnounceGasPrice(BREthereumEWM ewm, BREthereumWallet wid, - const char *gasEstimate, + const char *gasPrice, int rid); extern void @@ -68,25 +72,34 @@ extern "C" { (*BREthereumClientHandlerEstimateGas) (BREthereumClientContext context, BREthereumEWM ewm, BREthereumWallet wid, - BREthereumTransfer tid, + BREthereumCookie cookie, const char *from, const char *to, const char *amount, + const char *gasPrice, const char *data, int rid); extern BREthereumStatus - ewmAnnounceGasEstimate (BREthereumEWM ewm, - BREthereumWallet wid, - BREthereumTransfer tid, - const char *gasEstimate, - int rid); + ewmAnnounceGasEstimateSuccess (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + const char *gasEstimate, + const char *gasPrice, + int rid); + extern BREthereumStatus + ewmAnnounceGasEstimateFailure (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status, + int rid); extern void - ewmUpdateGasEstimate (BREthereumEWM ewm, - BREthereumWallet wid, - BREthereumTransfer tid); + ewmGetGasEstimate (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumTransfer transfer, + BREthereumCookie cookie); /// MARK: - Submit Transfer @@ -205,19 +218,19 @@ extern "C" { extern void ewmAnnounceToken(BREthereumEWM ewm, + int rid, const char *address, const char *symbol, const char *name, const char *description, unsigned int decimals, const char *strDefaultGasLimit, - const char *strDefaultGasPrice, - int rid); + const char *strDefaultGasPrice); extern void ewmAnnounceTokenComplete (BREthereumEWM ewm, - BREthereumBoolean success, - int rid); + int rid, + BREthereumBoolean success); extern void ewmUpdateTokens (BREthereumEWM ewm); @@ -273,18 +286,36 @@ extern "C" { WALLET_EVENT_BALANCE_UPDATED, WALLET_EVENT_DEFAULT_GAS_LIMIT_UPDATED, WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, - WALLET_EVENT_DELETED + WALLET_EVENT_FEE_ESTIMATED, + WALLET_EVENT_DELETED, + } BREthereumWalletEventType; + + typedef struct { + BREthereumWalletEventType type; + BREthereumStatus status; + union { + struct { + BREthereumCookie cookie; + BREthereumGas gasEstimate; + BREthereumGasPrice gasPrice; + } feeEstimate; + } u; + char errorDescription[16]; } BREthereumWalletEvent; -#define WALLET_NUMBER_OF_EVENTS (1 + WALLET_EVENT_DELETED) +#define WALLET_NUMBER_OF_EVENT_TYPES (1 + WALLET_EVENT_DELETED) + extern BREthereumWalletEvent + walletEventCreateError (BREthereumWalletEventType type, + BREthereumStatus status, + const char *errorDescription); + typedef void (*BREthereumClientHandlerWalletEvent) (BREthereumClientContext context, BREthereumEWM ewm, BREthereumWallet wid, - BREthereumWalletEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumWalletEvent event); +#if defined (NEVER_DEFINED) typedef enum { BLOCK_EVENT_CREATED = 0, @@ -292,17 +323,20 @@ extern "C" { BLOCK_EVENT_ORPHANED, BLOCK_EVENT_DELETED - } BREthereumBlockEvent; + } BREthereumBlockEventType; -#define BLOCK_NUMBER_OF_EVENTS (1 + BLOCK_EVENT_DELETED) +#define BLOCK_NUMBER_OF_EVENT_TYPES (1 + BLOCK_EVENT_DELETED) + + typedef struct { + BREthereumBlockEventType type; + BREthereumStatus status; + char errorDescription[16]; + } BREthereumBlockEvent; -#if defined (NEVER_DEFINED) typedef void (*BREthereumClientHandlerBlockEvent) (BREthereumClientContext context, BREthereumEWM ewm, BREthereumBlock bid, - BREthereumBlockEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumBlockEvent event); #endif typedef enum { @@ -315,70 +349,118 @@ extern "C" { TRANSFER_EVENT_INCLUDED, // aka confirmed TRANSFER_EVENT_ERRORED, - TRANSFER_EVENT_GAS_ESTIMATE_UPDATED, - TRANSFER_EVENT_BLOCK_CONFIRMATIONS_UPDATED, TRANSFER_EVENT_DELETED + } BREthereumTransferEventType; + +#define TRANSACTION_NUMBER_OF_EVENT_TYPES (1 + TRANSACTION_EVENT_DELETED) + typedef struct { + BREthereumTransferEventType type; + BREthereumStatus status; + // uniont {} u; + char errorDescription[16]; } BREthereumTransferEvent; -#define TRANSACTION_NUMBER_OF_EVENTS (1 + TRANSACTION_EVENT_DELETED) + extern BREthereumTransferEvent + transferEventCreateError (BREthereumTransferEventType type, + BREthereumStatus status, + const char *errorDescription); typedef void (*BREthereumClientHandlerTransferEvent) (BREthereumClientContext context, BREthereumEWM ewm, BREthereumWallet wid, BREthereumTransfer tid, - BREthereumTransferEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumTransferEvent event); typedef enum { PEER_EVENT_CREATED = 0, PEER_EVENT_DELETED // added/removed/updated - } BREthereumPeerEvent; + } BREthereumPeerEventType; -#define PEER_NUMBER_OF_EVENTS (1 + PEER_EVENT_DELETED) +#define PEER_NUMBER_OF_EVENT_TYPES (1 + PEER_EVENT_DELETED) + typedef struct { + BREthereumPeerEventType type; + BREthereumStatus status; + // union { } u; + char errorDescription[16]; + } BREthereumPeerEvent; typedef void (*BREthereumClientHandlerPeerEvent) (BREthereumClientContext context, BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumPeerEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumPeerEvent event); typedef enum { TOKEN_EVENT_CREATED = 0, TOKEN_EVENT_DELETED - } BREthereumTokenEvent; + } BREthereumTokenEventType; -#define TOKEN_NUMBER_OF_EVENTS (1 + TOKEN_EVENT_DELETED) +#define TOKEN_NUMBER_OF_EVENT_TYPES (1 + TOKEN_EVENT_DELETED) + typedef struct { + BREthereumTokenEventType type; + BREthereumStatus status; + // union {} u; + char errorDescription[16]; + } BREthereumTokenEvent; + typedef void (*BREthereumClientHandlerTokenEvent) (BREthereumClientContext context, BREthereumEWM ewm, BREthereumToken token, BREthereumTokenEvent event); + typedef enum { + EWM_STATE_CREATED, + EWM_STATE_CONNECTED, + EWM_STATE_SYNCING, + EWM_STATE_DISCONNECTED, + EWM_STATE_DELETED + } BREthereumEWMState; + typedef enum { EWM_EVENT_CREATED = 0, - EWM_EVENT_SYNC_STARTED, - EWM_EVENT_SYNC_CONTINUES, - EWM_EVENT_SYNC_STOPPED, + EWM_EVENT_CHANGED, + + EWM_EVENT_SYNC_PROGRESS, + EWM_EVENT_BLOCK_HEIGHT_UPDATED, EWM_EVENT_NETWORK_UNAVAILABLE, - EWM_EVENT_DELETED + + EWM_EVENT_DELETED, + } BREthereumEWMEventType; + +#define EWM_NUMBER_OF_EVENT_TYPES (1 + EWM_EVENT_DELETED) + + typedef struct { + BREthereumEWMEventType type; + BREthereumStatus status; + union { + struct { + BREthereumEWMState oldState; + BREthereumEWMState newState; + } changed; + struct { + BRCryptoSyncTimestamp timestamp; + BRCryptoSyncPercentComplete percentComplete; + } syncProgress; + struct { + uint64_t value; + } blockHeight; + } u; + char errorDescription[16]; } BREthereumEWMEvent; -#define EWM_NUMBER_OF_EVENTS (1 + EWM_EVENT_DELETED) + extern BREthereumEWMEvent + ewmEventCreateError (BREthereumEWMEventType type, + BREthereumStatus status, + const char *errorDescription); typedef void (*BREthereumClientHandlerEWMEvent) (BREthereumClientContext context, BREthereumEWM ewm, // BREthereumWallet wid, // BREthereumTransaction tid, - BREthereumEWMEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumEWMEvent event); // // EWM Configuration diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.c index d9d8e8915..db8f2712a 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.c @@ -1,9 +1,9 @@ // // BREthereumEWM -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/5/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -30,6 +30,17 @@ // When using a BRD sync, offset the start block by N days of Ethereum blocks #define EWM_BRD_SYNC_START_BLOCK_OFFSET (3 * 24 * 60 * 4) /* 4 per minute (every 15 seconds) */ +// An ongoing sync is one that has a `end - beg` block difference of +// EWM_BRD_SYNC_START_BLOCK_OFFSET or so (with some slop). If the block difference is large +// enough we'll transition to an EWM state of SYNCING; otherwise we'll consider the sync as an +// ongoing sync (as when the blockchain is extended). +static int +ewmIsNotAnOngoingSync (BREthereumEWM ewm) { + return ewm->brdSync.endBlockNumber - ewm->brdSync.begBlockNumber > + (EWM_BRD_SYNC_START_BLOCK_OFFSET + EWM_BRD_SYNC_START_BLOCK_OFFSET/10); +} + + #define EWM_INITIAL_SET_SIZE_DEFAULT (25) /* Forward Declaration */ @@ -39,254 +50,68 @@ ewmPeriodicDispatcher (BREventHandler handler, /* Forward Implementation */ -/// MARK: - Transaction File Service - -static const char *fileServiceTypeTransactions = "transactions"; - -enum { - EWM_TRANSACTION_VERSION_1 -}; - -static UInt256 -fileServiceTypeTransactionV1Identifier (BRFileServiceContext context, - BRFileService fs, - const void *entity) { - BREthereumTransaction transaction = (BREthereumTransaction) entity; - BREthereumHash hash = transactionGetHash(transaction); - - UInt256 result; - memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); - return result; -} - -static uint8_t * -fileServiceTypeTransactionV1Writer (BRFileServiceContext context, - BRFileService fs, - const void* entity, - uint32_t *bytesCount) { - BREthereumEWM ewm = context; - BREthereumTransaction transaction = (BREthereumTransaction) entity; - - BRRlpItem item = transactionRlpEncode(transaction, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); - BRRlpData data = rlpGetData (ewm->coder, item); - rlpReleaseItem (ewm->coder, item); - - *bytesCount = (uint32_t) data.bytesCount; - return data.bytes; -} - -static void * -fileServiceTypeTransactionV1Reader (BRFileServiceContext context, - BRFileService fs, - uint8_t *bytes, - uint32_t bytesCount) { - BREthereumEWM ewm = context; - - BRRlpData data = { bytesCount, bytes }; - BRRlpItem item = rlpGetItem (ewm->coder, data); - - BREthereumTransaction transaction = transactionRlpDecode(item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); - rlpReleaseItem (ewm->coder, item); - - return transaction; -} +/// MARK: - File Service, Initial Load static BRSetOf(BREthereumTransaction) initialTransactionsLoad (BREthereumEWM ewm) { BRSetOf(BREthereumTransaction) transactions = BRSetNew(transactionHashValue, transactionHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - if (1 != fileServiceLoad (ewm->fs, transactions, fileServiceTypeTransactions, 1)) { - BRSetFree(transactions); + if (NULL != transactions && 1 != fileServiceLoad (ewm->fs, transactions, ewmFileServiceTypeTransactions, 1)) { + BRSetFreeAll (transactions, (void (*) (void*)) transactionRelease); return NULL; } return transactions; } -/// MARK: - Log File Service - -static const char *fileServiceTypeLogs = "logs"; - -enum { - EWM_LOG_VERSION_1 -}; - -static UInt256 -fileServiceTypeLogV1Identifier (BRFileServiceContext context, - BRFileService fs, - const void *entity) { - const BREthereumLog log = (BREthereumLog) entity; - BREthereumHash hash = logGetHash( log); - - UInt256 result; - memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); - return result; -} - -static uint8_t * -fileServiceTypeLogV1Writer (BRFileServiceContext context, - BRFileService fs, - const void* entity, - uint32_t *bytesCount) { - BREthereumEWM ewm = context; - BREthereumLog log = (BREthereumLog) entity; - - BRRlpItem item = logRlpEncode (log, RLP_TYPE_ARCHIVE, ewm->coder); - BRRlpData data = rlpGetData (ewm->coder, item); - rlpReleaseItem (ewm->coder, item); - - *bytesCount = (uint32_t) data.bytesCount; - return data.bytes; -} - -static void * -fileServiceTypeLogV1Reader (BRFileServiceContext context, - BRFileService fs, - uint8_t *bytes, - uint32_t bytesCount) { - BREthereumEWM ewm = context; - - BRRlpData data = { bytesCount, bytes }; - BRRlpItem item = rlpGetItem (ewm->coder, data); - - BREthereumLog log = logRlpDecode(item, RLP_TYPE_ARCHIVE, ewm->coder); - rlpReleaseItem (ewm->coder, item); - - return log; -} - static BRSetOf(BREthereumLog) initialLogsLoad (BREthereumEWM ewm) { BRSetOf(BREthereumLog) logs = BRSetNew(logHashValue, logHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - if (1 != fileServiceLoad (ewm->fs, logs, fileServiceTypeLogs, 1)) { - BRSetFree(logs); + if (NULL != logs && 1 != fileServiceLoad (ewm->fs, logs, ewmFileServiceTypeLogs, 1)) { + BRSetFreeAll (logs, (void (*) (void*)) logRelease); return NULL; } return logs; } -/// MARK: - Block File Service - -static const char *fileServiceTypeBlocks = "blocks"; -enum { - EWM_BLOCK_VERSION_1 -}; - -static UInt256 -fileServiceTypeBlockV1Identifier (BRFileServiceContext context, - BRFileService fs, - const void *entity) { - const BREthereumBlock block = (BREthereumBlock) entity; - BREthereumHash hash = blockGetHash(block); - - UInt256 result; - memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); - return result; -} - -static uint8_t * -fileServiceTypeBlockV1Writer (BRFileServiceContext context, - BRFileService fs, - const void* entity, - uint32_t *bytesCount) { - BREthereumEWM ewm = context; - BREthereumBlock block = (BREthereumBlock) entity; - - BRRlpItem item = blockRlpEncode(block, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); - BRRlpData data = rlpGetData (ewm->coder, item); - rlpReleaseItem (ewm->coder, item); - - *bytesCount = (uint32_t) data.bytesCount; - return data.bytes; -} - -static void * -fileServiceTypeBlockV1Reader (BRFileServiceContext context, - BRFileService fs, - uint8_t *bytes, - uint32_t bytesCount) { - BREthereumEWM ewm = context; - - BRRlpData data = { bytesCount, bytes }; - BRRlpItem item = rlpGetItem (ewm->coder, data); - - BREthereumBlock block = blockRlpDecode (item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); - rlpReleaseItem (ewm->coder, item); - - return block; -} - static BRSetOf(BREthereumBlock) initialBlocksLoad (BREthereumEWM ewm) { BRSetOf(BREthereumBlock) blocks = BRSetNew(blockHashValue, blockHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - if (1 != fileServiceLoad (ewm->fs, blocks, fileServiceTypeBlocks, 1)) { - BRSetFree(blocks); + if (NULL != blocks && 1 != fileServiceLoad (ewm->fs, blocks, ewmFileServiceTypeBlocks, 1)) { + BRSetFreeAll (blocks, (void (*) (void*)) blockRelease); return NULL; } return blocks; } -/// MARK: - Node File Service - -static const char *fileServiceTypeNodes = "nodes"; -enum { - EWM_NODE_VERSION_1 -}; - -static UInt256 -fileServiceTypeNodeV1Identifier (BRFileServiceContext context, - BRFileService fs, - const void *entity) { - const BREthereumNodeConfig node = (BREthereumNodeConfig) entity; - - BREthereumHash hash = nodeConfigGetHash(node); - - UInt256 result; - memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); - return result; -} - -static uint8_t * -fileServiceTypeNodeV1Writer (BRFileServiceContext context, - BRFileService fs, - const void* entity, - uint32_t *bytesCount) { - BREthereumEWM ewm = context; - const BREthereumNodeConfig node = (BREthereumNodeConfig) entity; - - BRRlpItem item = nodeConfigEncode (node, ewm->coder); - BRRlpData data = rlpGetData (ewm->coder, item); - rlpReleaseItem (ewm->coder, item); - - *bytesCount = (uint32_t) data.bytesCount; - return data.bytes; -} - -static void * -fileServiceTypeNodeV1Reader (BRFileServiceContext context, - BRFileService fs, - uint8_t *bytes, - uint32_t bytesCount) { - BREthereumEWM ewm = context; - - BRRlpData data = { bytesCount, bytes }; - BRRlpItem item = rlpGetItem (ewm->coder, data); - - BREthereumNodeConfig node = nodeConfigDecode (item, ewm->coder); - rlpReleaseItem (ewm->coder, item); - - return node; -} - static BRSetOf(BREthereumNodeConfig) initialNodesLoad (BREthereumEWM ewm) { BRSetOf(BREthereumNodeConfig) nodes = BRSetNew(nodeConfigHashValue, nodeConfigHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - if (1 != fileServiceLoad (ewm->fs, nodes, fileServiceTypeNodes, 1)) { - BRSetFree(nodes); + if (NULL != nodes && 1 != fileServiceLoad (ewm->fs, nodes, ewmFileServiceTypeNodes, 1)) { + BRSetFreeAll (nodes, (void (*) (void*)) nodeConfigRelease); return NULL; } return nodes; } +static BRSetOf(BREthereumToken) +initialTokensLoad (BREthereumEWM ewm) { + BRSetOf(BREthereumToken) tokens = tokenSetCreate (EWM_INITIAL_SET_SIZE_DEFAULT); + if (NULL != tokens && 1 != fileServiceLoad (ewm->fs, tokens, ewmFileServiceTypeTokens, 1)) { + BRSetFreeAll (tokens, (void (*) (void*)) tokenRelease); + return NULL; + } + return tokens; +} + +static BRSetOf(BREthereumWalletState) +initialWalletsLoad (BREthereumEWM ewm) { + BRSetOf(BREthereumWalletState) states = walletStateSetCreate (EWM_INITIAL_SET_SIZE_DEFAULT); + if (NULL != states && 1 != fileServiceLoad (ewm->fs, states, ewmFileServiceTypeWallets, 1)) { + BRSetFreeAll (states, (void (*) (void*)) walletStateRelease); + return NULL; + } + return states; +} /** * @@ -315,6 +140,11 @@ ewmFileServiceErrorHandler (BRFileServiceContext context, error.u.entity.type, error.u.entity.reason); break; + case FILE_SERVICE_SDB: + eth_log ("EWM", "FileService Error: SDB: (%d): %s", + error.u.sdb.code, + error.u.sdb.reason); + break; } eth_log ("EWM", "FileService Error: FORCED SYNC%s", ""); @@ -337,21 +167,95 @@ ewmCreateErrorHandler (BREthereumEWM ewm, int fileService, const char* reason) { static void ewmAssertRecovery (BREthereumEWM ewm); +static BREthereumBCSListener +ewmCreateBCSListener (BREthereumEWM ewm) { + return (BREthereumBCSListener) { + (BREthereumBCSCallbackContext) ewm, + (BREthereumBCSCallbackBlockchain) ewmSignalBlockChain, + (BREthereumBCSCallbackAccountState) ewmSignalAccountState, + (BREthereumBCSCallbackTransaction) ewmSignalTransaction, + (BREthereumBCSCallbackLog) ewmSignalLog, + (BREthereumBCSCallbackSaveBlocks) ewmSignalSaveBlocks, + (BREthereumBCSCallbackSavePeers) ewmSignalSaveNodes, + (BREthereumBCSCallbackSync) ewmSignalSync, + (BREthereumBCSCallbackGetBlocks) ewmSignalGetBlocks + }; +} + +static void +ewmCreateInitialSets (BREthereumEWM ewm, + BREthereumNetwork network, + BREthereumTimestamp accountTimestamp, + BRSetOf(BREthereumTransaction) *transactions, + BRSetOf(BREthereumLog) *logs, + BRSetOf(BREthereumNodeConfig) *nodes, + BRSetOf(BREthereumBlock) *blocks, + BRSetOf(BREthereumToken) *tokens, + BRSetOf(BREthereumWalletState) *states) { + + *transactions = initialTransactionsLoad(ewm); + *logs = initialLogsLoad(ewm); + *nodes = initialNodesLoad(ewm); + *blocks = initialBlocksLoad(ewm); + *tokens = initialTokensLoad(ewm); + *states = initialWalletsLoad(ewm); + + // If any are NULL, then we have an error and a full sync is required. The sync will be + // started automatically, as part of the normal processing, of 'blocks' (we'll use a checkpoint, + // before the `accountTimestamp, which will be well in the past and we'll sync up to the + // head of the blockchain). + if (NULL == *transactions || NULL == *logs || NULL == *nodes || NULL == *blocks || NULL == *tokens || NULL == *states) { + // If the set exists, clear it out completely and then create another one. Note, since + // we have `BRSetFreeAll()` we'll use that even though it frees the set and then we + // create one again, minimally wastefully. + if (NULL != *transactions) { BRSetFreeAll (*transactions, (void (*) (void*)) transactionRelease); } + *transactions = BRSetNew (transactionHashValue, transactionHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); + + if (NULL != *logs) { BRSetFreeAll (*logs, (void (*) (void*)) logRelease); } + *logs = BRSetNew (logHashValue, logHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); + + if (NULL != *blocks) { BRSetFreeAll (*blocks, (void (*) (void*)) blockRelease); } + *blocks = BRSetNew (blockHashValue, blockHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); + + if (NULL != *nodes) { BRSetFreeAll (*nodes, (void (*) (void*)) nodeConfigRelease); } + *nodes = BRSetNew (nodeConfigHashValue, nodeConfigHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); + + if (NULL != *tokens) { BRSetFreeAll (*tokens, (void (*) (void*)) tokenRelease); } + *tokens = tokenSetCreate (EWM_INITIAL_SET_SIZE_DEFAULT); + + if (NULL != *states) { BRSetFreeAll (*states, (void (*) (void*)) walletStateRelease); } + *states = walletStateSetCreate(EWM_INITIAL_SET_SIZE_DEFAULT); + } + + // If we have no blocks; then add a checkpoint + if (0 == BRSetCount(*blocks)) { + const BREthereumBlockCheckpoint *checkpoint = blockCheckpointLookupByTimestamp (network, accountTimestamp); + BREthereumBlock block = blockCreate (blockCheckpointCreatePartialBlockHeader (checkpoint)); + blockSetTotalDifficulty (block, checkpoint->u.td); + BRSetAdd (*blocks, block); + } +} + extern BREthereumEWM ewmCreate (BREthereumNetwork network, BREthereumAccount account, BREthereumTimestamp accountTimestamp, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumClient client, - const char *storagePath) { + const char *storagePath, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) { BREthereumEWM ewm = (BREthereumEWM) calloc (1, sizeof (struct BREthereumEWMRecord)); - ewm->state = LIGHT_NODE_CREATED; + ewm->state = EWM_STATE_CREATED; ewm->mode = mode; ewm->network = network; ewm->account = account; + ewm->accountTimestamp = accountTimestamp; ewm->bcs = NULL; - ewm->blockHeight = 0; + ewm->blockHeight = blockHeight; + ewm->confirmationsUntilFinal = confirmationsUntilFinal; + ewm->requestId = 0; { char address [ADDRESS_ENCODED_CHARS]; @@ -360,12 +264,12 @@ ewmCreate (BREthereumNetwork network, } // Initialize the `brdSync` struct - ewm->brdSync.ridTransaction = -1; - ewm->brdSync.ridLog = -1; + ewm->brdSync.ridTransaction = EWM_REQUEST_ID_UNKNOWN; + ewm->brdSync.ridLog = EWM_REQUEST_ID_UNKNOWN; ewm->brdSync.begBlockNumber = 0; - ewm->brdSync.endBlockNumber = 0; - ewm->brdSync.completedTransaction = 0; - ewm->brdSync.completedLog = 0; + ewm->brdSync.endBlockNumber = ewm->blockHeight; + ewm->brdSync.completedTransaction = 1; + ewm->brdSync.completedLog = 1; // Get the client assigned early; callbacks as EWM/BCS state is re-establish, regarding // blocks, peers, transactions and logs, will be invoked. @@ -386,83 +290,26 @@ ewmCreate (BREthereumNetwork network, // The file service. Initialize {nodes, blocks, transactions and logs} from the FileService - ewm->fs = fileServiceCreate (storagePath, "eth", networkGetName(network), - ewm, - ewmFileServiceErrorHandler); + ewm->fs = fileServiceCreateFromTypeSpecfications (storagePath, "eth", networkGetName(network), + ewm, + ewmFileServiceErrorHandler, + ewmFileServiceSpecificationsCount, + ewmFileServiceSpecifications); if (NULL == ewm->fs) return ewmCreateErrorHandler(ewm, 1, "create"); - /// Transaction - if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeTransactions, EWM_TRANSACTION_VERSION_1, - (BRFileServiceContext) ewm, - fileServiceTypeTransactionV1Identifier, - fileServiceTypeTransactionV1Reader, - fileServiceTypeTransactionV1Writer) || - 1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeTransactions, - EWM_TRANSACTION_VERSION_1)) - return ewmCreateErrorHandler(ewm, 1, fileServiceTypeTransactions); - - /// Log - if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeLogs, EWM_LOG_VERSION_1, - (BRFileServiceContext) ewm, - fileServiceTypeLogV1Identifier, - fileServiceTypeLogV1Reader, - fileServiceTypeLogV1Writer) || - 1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeLogs, - EWM_LOG_VERSION_1)) - return ewmCreateErrorHandler(ewm, 1, fileServiceTypeLogs); - - /// Peer - if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeNodes, EWM_NODE_VERSION_1, - (BRFileServiceContext) ewm, - fileServiceTypeNodeV1Identifier, - fileServiceTypeNodeV1Reader, - fileServiceTypeNodeV1Writer) || - 1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeNodes, - EWM_NODE_VERSION_1)) - return ewmCreateErrorHandler(ewm, 1, fileServiceTypeNodes); - - - /// Block - if (1 != fileServiceDefineType (ewm->fs, fileServiceTypeBlocks, EWM_BLOCK_VERSION_1, - (BRFileServiceContext) ewm, - fileServiceTypeBlockV1Identifier, - fileServiceTypeBlockV1Reader, - fileServiceTypeBlockV1Writer) || - 1 != fileServiceDefineCurrentVersion (ewm->fs, fileServiceTypeBlocks, - EWM_BLOCK_VERSION_1)) - return ewmCreateErrorHandler(ewm, 1, fileServiceTypeBlocks); - // Load all the persistent entities - BRSetOf(BREthereumTransaction) transactions = initialTransactionsLoad(ewm); - BRSetOf(BREthereumLog) logs = initialLogsLoad(ewm); - BRSetOf(BREthereumNodeConfig) nodes = initialNodesLoad(ewm); - BRSetOf(BREthereumBlock) blocks = initialBlocksLoad(ewm); - - // If any are NULL, then we have an error and a full sync is required. The sync will be - // started automatically, as part of the normal processing, of 'blocks' (we'll use a checkpoint, - // before the `accountTimestamp, which will be well in the past and we'll sync up to the - // head of the blockchain). - if (NULL == transactions || NULL == logs || NULL == nodes || NULL == blocks) { - if (NULL == transactions) transactions = BRSetNew(transactionHashValue, transactionHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - else BRSetClear(transactions); - - if (NULL == logs) logs = BRSetNew(logHashValue, logHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - else BRSetClear(logs); - - if (NULL == blocks) blocks = BRSetNew(blockHashValue, blockHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - else BRSetClear(blocks); + BRSetOf(BREthereumTransaction) transactions; + BRSetOf(BREthereumLog) logs; + BRSetOf(BREthereumNodeConfig) nodes; + BRSetOf(BREthereumBlock) blocks; + BRSetOf(BREthereumToken) tokens; + BRSetOf(BREthereumWalletState) walletStates; - if (NULL == nodes) nodes = BRSetNew(nodeConfigHashValue, nodeConfigHashEqual, EWM_INITIAL_SET_SIZE_DEFAULT); - else BRSetClear(nodes); - } + ewmCreateInitialSets (ewm, ewm->network, ewm->accountTimestamp, + &transactions, &logs, &nodes, &blocks, &tokens, &walletStates); - // If we have no blocks; then add a checkpoint - if (0 && 0 == BRSetCount(blocks)) { - const BREthereumBlockCheckpoint *checkpoint = blockCheckpointLookupByTimestamp (network, accountTimestamp); - BREthereumBlock block = blockCreate (blockCheckpointCreatePartialBlockHeader (checkpoint)); - blockSetTotalDifficulty (block, checkpoint->u.td); - BRSetAdd (blocks, block); - } + // Save the recovered tokens + ewm->tokens = tokens; // Create the alarm clock, but don't start it. alarmClockCreateIfNecessary(0); @@ -474,29 +321,57 @@ ewmCreate (BREthereumNetwork network, ewmEventTypesCount, &ewm->lock); - array_new(ewm->wallets, DEFAULT_WALLET_CAPACITY); + array_new (ewm->wallets, DEFAULT_WALLET_CAPACITY); - // Create a default ETH wallet; other wallets will be created 'on demand' - ewm->walletHoldingEther = walletCreate(ewm->account, - ewm->network); - ewmInsertWallet(ewm, ewm->walletHoldingEther); + // Queue the CREATED event so that it is the first event delievered to the BREthereumClient + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_CREATED, + SUCCESS + }); + + // Create a default ETH wallet; other wallets will be created 'on demand'. This will signal + // a WALLET_EVENT_CREATED event. + ewm->walletHoldingEther = walletCreate (ewm->account, ewm->network); + ewmInsertWallet (ewm, ewm->walletHoldingEther); // Create the BCS listener - allows EWM to handle block, peer, transaction and log events. - BREthereumBCSListener listener = { - (BREthereumBCSCallbackContext) ewm, - (BREthereumBCSCallbackBlockchain) ewmSignalBlockChain, - (BREthereumBCSCallbackAccountState) ewmSignalAccountState, - (BREthereumBCSCallbackTransaction) ewmSignalTransaction, - (BREthereumBCSCallbackLog) ewmSignalLog, - (BREthereumBCSCallbackSaveBlocks) ewmSignalSaveBlocks, - (BREthereumBCSCallbackSavePeers) ewmSignalSaveNodes, - (BREthereumBCSCallbackSync) ewmSignalSync, - (BREthereumBCSCallbackGetBlocks) ewmSignalGetBlocks - }; + BREthereumBCSListener listener = ewmCreateBCSListener (ewm); BRAssertDefineRecovery ((BRAssertRecoveryInfo) ewm, (BRAssertRecoveryHandler) ewmAssertRecovery); + // Having restored the WalletState. restore that Wallet (balance). + FOR_SET (BREthereumWalletState, state, walletStates) { + BREthereumAddress address = walletStateGetAddress (state); + + // If the WalletState address is EMPTY_ADDRESS_INIT, then the state is for ETHER + BREthereumBoolean addressIsForEther = addressEqual (EMPTY_ADDRESS_INIT, address); + + // See if we have a token. + BREthereumToken token = (ETHEREUM_BOOLEAN_IS_TRUE (addressIsForEther) + ? NULL + : ewmLookupToken (ewm, address)); + + // If we don't have a token and the address is not for ether, then ignore the state + // This would occur if we have a state for a token that is no longer supported. + if (NULL == token && ETHEREUM_BOOLEAN_IS_FALSE(addressIsForEther)) continue; + + // Get the balance + BREthereumAmount balance = (NULL == token + ? amountCreateEther (etherCreate (walletStateGetAmount (state))) + : amountCreateToken (createTokenQuantity (token, walletStateGetAmount (state)))); + + // Finally, update the balance; this will create TOK wallets as required. + ewmHandleBalance (ewm, balance); + + if (NULL == token) { + accountSetAddressNonce (ewm->account, + accountGetPrimaryAddress(ewm->account), + walletStateGetNonce(state), + ETHEREUM_BOOLEAN_TRUE); + } + } + // Create BCS - note: when BCS processes blocks, peers, transactions, and logs there // will be callbacks made to the EWM client. Because we've defined `handlerForMain` // any callbacks will be queued and then handled when EWM actually starts @@ -504,8 +379,8 @@ ewmCreate (BREthereumNetwork network, // Support the requested mode switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { // Note: We'll create BCS even for the mode where we don't use it (BRD_ONLY). ewm->bcs = bcsCreate (network, accountGetPrimaryAddress (account), @@ -520,10 +395,34 @@ ewmCreate (BREthereumNetwork network, FOR_SET (BREthereumTransaction, transaction, transactions) ewmSignalTransaction (ewm, BCS_CALLBACK_TRANSACTION_ADDED, transaction); - // ... as well as the provided logs... + // ... as well as the provided logs... however, in handling an announced log we perform + // ``` + // BREthereumToken token = tokenLookupByAddress(logGetAddress(log)); + // if (NULL == token) { logRelease(log); return;} + // ``` + // and thus very single log is discared immediately. They only come back with the + // first sync. We have no choice but to discard (until tokens are persistently stored) FOR_SET (BREthereumLog, log, logs) ewmSignalLog (ewm, BCS_CALLBACK_LOG_ADDED, log); + // Previously both `ewmSignalTransaction()` and `ewmSignalLog` would iterate over + // all the transfers to compute the wallet's balance. (see `walletUpdateBalance()` + // and its call sites (commented out currently)). The balance was updated for each + // and every added transaction and an 'BALANCE_UPDATED' event was generated for each. + // + // But, now, we do not rely on summing transfers amounts - instead, since Ethereum is + // 'account based' we use the account state (ETH or ERC20) to get the wallet's + // balance. Note, this might need to change as it is not currently clear to me + // how to get an ERC20 balance (execute a (free) transaction for 'balance'??); this + // later case applies for `bcsCreate()` below in P2P modes. + // + // In addition, because we DO NOT handle 'internal transactions' the sum of transfers + // cannot reliably produce the account balance. + // + // So, we have all the logs and transactions and we won't modify the wallet balances + // that have been restored from persistent state. + +#if 0 // ... and then the latest block. BREthereumBlock lastBlock = NULL; FOR_SET (BREthereumBlock, block, blocks) @@ -533,11 +432,31 @@ ewmCreate (BREthereumNetwork network, blockGetHash( lastBlock), blockGetNumber (lastBlock), blockGetTimestamp (lastBlock)); +#endif + // Use the provided `blockHeight` in API modes. We only have the `blockHeight` as a + // parameter, thus calling `ewmSignalBlockChain()` with 'empty' values for `blockHash` + // and `blockTimestamp` works because we know that `ewmHandleBlockChain` doesn't use + // those parameters. + // + // What was the expectation of the above `lastBlock` code in API modes? It might have + // been from a time when `blockHeight` was not passed in as a parameters and thus we + // need some block height and the best we could do was the saved blocks (like from a + // P2P mode) or the checkpoint which is always there, at least. + // + // Our current API appraoch is to use BlockSet where a) we have the current block height + // or b) if BlockSet, is down we hardcode the block height at the time of the software + // distribution (See System.supportedBlockchains) - either way we have a better + // current block height than any checkpoint. + + ewmSignalBlockChain (ewm, EMPTY_HASH_INIT, ewm->blockHeight, 0); // ... and then just ignore nodes // Free sets... BUT DO NOT free 'nodes' as those had 'OwnershipGiven' in bcsCreate() BRSetFreeAll(blocks, (void (*) (void*)) blockRelease); + + // We must not free the individual `transactions` and `logs` as they were OwnershipGiven + // in the above `ewmSignalTransaction()` and `ewmSignalLog()` calls. BRSetFree (transactions); BRSetFree (logs); @@ -559,8 +478,8 @@ ewmCreate (BREthereumNetwork network, break; } - case P2P_WITH_BRD_SYNC: // - case P2P_ONLY: { + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: // + case CRYPTO_SYNC_MODE_P2P_ONLY: { ewm->bcs = bcsCreate (network, accountGetPrimaryAddress (account), listener, @@ -573,8 +492,9 @@ ewmCreate (BREthereumNetwork network, } } - // mark as 'sync in progress' - we can't sent transactions until we have the nonce. + BRSetFreeAll (walletStates, (void (*) (void*)) walletStateRelease); + // mark as 'sync in progress' - we can't sent transactions until we have the nonce. return ewm; } @@ -583,42 +503,60 @@ extern BREthereumEWM ewmCreateWithPaperKey (BREthereumNetwork network, const char *paperKey, BREthereumTimestamp accountTimestamp, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumClient client, - const char *storagePath) { + const char *storagePath, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) { return ewmCreate (network, createAccount (paperKey), accountTimestamp, mode, client, - storagePath); + storagePath, + blockHeight, + confirmationsUntilFinal); } extern BREthereumEWM ewmCreateWithPublicKey (BREthereumNetwork network, BRKey publicKey, BREthereumTimestamp accountTimestamp, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumClient client, - const char *storagePath) { + const char *storagePath, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal) { return ewmCreate (network, createAccountWithPublicKey(publicKey), accountTimestamp, mode, client, - storagePath); + storagePath, + blockHeight, + confirmationsUntilFinal); } extern void ewmDestroy (BREthereumEWM ewm) { + // Stop, including disconnect. This WILL take `ewm->lock` and it MUST be available. + ewmStop (ewm); + pthread_mutex_lock(&ewm->lock); - ewmDisconnect(ewm); + + // + // Begin destroy + // bcsDestroy(ewm->bcs); walletsRelease (ewm->wallets); ewm->wallets = NULL; + BRSetFreeAll (ewm->tokens, (void (*) (void*)) tokenRelease); + ewm->tokens = NULL; + + fileServiceRelease (ewm->fs); eventHandlerDestroy(ewm->handler); rlpCoderRelease(ewm->coder); @@ -627,9 +565,42 @@ ewmDestroy (BREthereumEWM ewm) { pthread_mutex_unlock (&ewm->lock); pthread_mutex_destroy (&ewm->lock); + + memset (ewm, 0, sizeof(*ewm)); free (ewm); } +/// MARK: - Start/Stop + +extern void +ewmStart (BREthereumEWM ewm) { + // TODO: Check on a current state before starting. + + // Start the alarm clock. + alarmClockStart(alarmClock); + + // Start the EWM thread + eventHandlerStart(ewm->handler); +} + +extern void +ewmStop (BREthereumEWM ewm) { + // TODO: Check on a current state before stopping. + + // Disconnect + ewmDisconnect(ewm); + // TODO: Are their disconnect events that we need to process before stopping the handler? + + // Stop the alarm clock + alarmClockStop (alarmClock); + + // Stop the EWM thread + eventHandlerStop(ewm->handler); + + // Close the file service. + fileServiceClose (ewm->fs); +} + /// MARK: - Connect / Disconnect /** @@ -644,33 +615,44 @@ extern BREthereumBoolean ewmConnect(BREthereumEWM ewm) { BREthereumBoolean result = ETHEREUM_BOOLEAN_FALSE; - ewmLock (ewm); + pthread_mutex_lock(&ewm->lock); + + BREthereumEWMState oldState = ewm->state; + BREthereumEWMState newState = ewm->state; // Nothing to do if already connected if (ETHEREUM_BOOLEAN_IS_FALSE (ewmIsConnected(ewm))) { - // Set ewm {client,state} prior to bcs/event start. Avoid race conditions, particularly - // with `ewmPeriodicDispatcher`. - ewm->state = LIGHT_NODE_CONNECTED; - // Start the alarm clock, if needed. - alarmClockStart(alarmClock); + // Set ewm {client,state} prior to bcs/event start. Avoid race conditions, particularly + // with `ewmPeriodicDispatcher`. + ewm->state = EWM_STATE_CONNECTED; + newState = ewm->state; switch (ewm->mode) { - case BRD_ONLY: + case CRYPTO_SYNC_MODE_API_ONLY: + // Immediately start an API sync + ewmSignalSyncAPI (ewm, ETHEREUM_BOOLEAN_TRUE); break; - case BRD_WITH_P2P_SEND: - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: + ewmSignalSyncAPI (ewm, ETHEREUM_BOOLEAN_TRUE); + // fall-through + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: bcsStart(ewm->bcs); break; } - eventHandlerStart(ewm->handler); - result = ETHEREUM_BOOLEAN_TRUE; } - ewmUnlock (ewm); + if (oldState != newState) + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { oldState, newState }} + }); + + pthread_mutex_unlock (&ewm->lock); return result; } @@ -685,34 +667,64 @@ extern BREthereumBoolean ewmDisconnect (BREthereumEWM ewm) { BREthereumBoolean result = ETHEREUM_BOOLEAN_FALSE; - ewmLock (ewm); + pthread_mutex_lock(&ewm->lock); + + BREthereumEWMState oldState = ewm->state; + BREthereumEWMState newState = ewm->state; if (ETHEREUM_BOOLEAN_IS_TRUE (ewmIsConnected(ewm))) { // Set ewm->state thereby stopping handlers (in a race with bcs/event calls). - ewm->state = LIGHT_NODE_DISCONNECTED; + ewm->state = EWM_STATE_DISCONNECTED; + newState = ewm->state; - // What order for these stop functions? See comment in `bcsStop()`. - alarmClockStop (alarmClock); + // Stop an ongoing sync + switch (ewm->mode) { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: + // If we are in the middle of a sync, the end it. + if (!ewm->brdSync.completedTransaction || !ewm->brdSync.completedLog) { + + // but only announce if it is not an 'ongoing' sync + if (ewmIsNotAnOngoingSync(ewm)) { + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { EWM_STATE_SYNCING, EWM_STATE_CONNECTED }} + }); + oldState = EWM_STATE_CONNECTED; + } + + ewm->brdSync.begBlockNumber = 0; + ewm->brdSync.endBlockNumber = ewm->blockHeight; + ewm->brdSync.completedTransaction = 1; + ewm->brdSync.completedLog = 1; + } + break; + default: break; + } + // Stop BCS switch (ewm->mode) { - case BRD_ONLY: + case CRYPTO_SYNC_MODE_API_ONLY: break; - case BRD_WITH_P2P_SEND: - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: bcsStop(ewm->bcs); break; } - // Unlock here - required for eventHandlerStop() to run and succeed on pthread_join(). This - // could be moved to immediately after `ewm->state = ...` as the lock *only* protects - // that EWM field. - ewmUnlock(ewm); - eventHandlerStop(ewm->handler); - result = ETHEREUM_BOOLEAN_TRUE; } - else ewmUnlock(ewm); + + if (oldState != newState) + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { oldState, newState }} + }); + + pthread_mutex_unlock (&ewm->lock); return result; } @@ -721,22 +733,23 @@ extern BREthereumBoolean ewmIsConnected (BREthereumEWM ewm) { BREthereumBoolean result = ETHEREUM_BOOLEAN_FALSE; - ewmLock (ewm); + pthread_mutex_lock(&ewm->lock); - if (LIGHT_NODE_CONNECTED == ewm->state) { + if (EWM_STATE_CONNECTED == ewm->state || EWM_STATE_SYNCING == ewm->state) { switch (ewm->mode) { - case BRD_ONLY: + case CRYPTO_SYNC_MODE_API_ONLY: result = ETHEREUM_BOOLEAN_TRUE; break; - case BRD_WITH_P2P_SEND: - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: result = bcsIsStarted (ewm->bcs); break; } } - ewmUnlock (ewm); + + pthread_mutex_unlock (&ewm->lock); return result; } @@ -748,34 +761,33 @@ ewmAssertRecovery (BREthereumEWM ewm) { extern BREthereumNetwork ewmGetNetwork (BREthereumEWM ewm) { - return ewm->network; + return ewm->network; // constant } extern BREthereumAccount ewmGetAccount (BREthereumEWM ewm) { - return ewm->account; + return ewm->account; // constant } extern char * ewmGetAccountPrimaryAddress(BREthereumEWM ewm) { - return accountGetPrimaryAddressString(ewmGetAccount(ewm)); + return accountGetPrimaryAddressString(ewmGetAccount(ewm)); // constant } extern BRKey // key.pubKey ewmGetAccountPrimaryAddressPublicKey(BREthereumEWM ewm) { - return accountGetPrimaryAddressPublicKey(ewmGetAccount(ewm)); + return accountGetPrimaryAddressPublicKey(ewmGetAccount(ewm)); // constant } extern BRKey ewmGetAccountPrimaryAddressPrivateKey(BREthereumEWM ewm, const char *paperKey) { - return accountGetPrimaryAddressPrivateKey (ewmGetAccount(ewm), paperKey); + return accountGetPrimaryAddressPrivateKey (ewmGetAccount(ewm), paperKey); // constant } -/// -/// Sync -/// +/// MARK: - Sync + typedef struct { BREthereumEWM ewm; uint64_t begBlockNumber; @@ -792,7 +804,7 @@ ewmSyncUpdateTransferPredicate (BREthereumSyncTransferContext *context, // answer is 'no - because the blockchain has no information about non-included transactios // and logs'. The other status types (created, submitted, etc) will either be resolved by // another sync or won't matter. - + return (transferExtractStatusIncluded (transfer, NULL, &blockNumber, NULL, NULL, NULL) && context->begBlockNumber <= blockNumber && blockNumber <= context->endBlockNumber); } @@ -824,259 +836,312 @@ ewmSyncUpdateTransfer (BREthereumSyncTransferContext *context, } extern BREthereumBoolean -ewmSync (BREthereumEWM ewm) { - if (LIGHT_NODE_CONNECTED != ewm->state) return ETHEREUM_BOOLEAN_FALSE; - - switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { - pthread_mutex_lock(&ewm->lock); - - BREthereumSyncTransferContext context = { ewm, 0, ewm->blockHeight }; - BREthereumWallet *wallets = ewmGetWallets(ewm); +ewmSync (BREthereumEWM ewm, + BREthereumBoolean pendExistingTransfers) { + return ewmSyncToDepth (ewm, pendExistingTransfers, CRYPTO_SYNC_DEPTH_FROM_CREATION); +} - // Walk each wallet, set all transfers to 'pending' - for (size_t wid = 0; NULL != wallets[wid]; wid++) - walletWalkTransfers (wallets[wid], &context, - (BREthereumTransferPredicate) ewmSyncUpdateTransferPredicate, - (BREthereumTransferWalker) ewmSyncUpdateTransfer); +typedef struct { + BREthereumEWM ewm; + uint64_t transferBlockHeight; + uint64_t confirmedBlockHeight; +} ewmSyncToDepthGetLastConfirmedSendTransferHeightContext; - free (wallets); +static int +ewmSyncToDepthGetLastConfirmedSendTransferHeightPredicate (ewmSyncToDepthGetLastConfirmedSendTransferHeightContext *context, + BREthereumTransfer transfer, + unsigned int index) { + BREthereumAccount account = ewmGetAccount (context->ewm); + BREthereumAddress accountAddress = accountGetPrimaryAddress (account); - // Start a sync from block 0 - ewm->brdSync.begBlockNumber = 0; + BREthereumAddress source = transferGetSourceAddress (transfer); + BREthereumAddress *target = transferGetTargetAddress (transfer); - // Try to avoid letting a nearly completed sync from continuing. - ewm->brdSync.completedTransaction = 0; - ewm->brdSync.completedLog = 0; - pthread_mutex_unlock(&ewm->lock); - return ETHEREUM_BOOLEAN_TRUE; - } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: - bcsSync (ewm->bcs, 0); - return ETHEREUM_BOOLEAN_TRUE; - } -} + BREthereumBoolean accountIsSource = addressEqual (source, accountAddress); + BREthereumBoolean accountIsTarget = NULL == target ? ETHEREUM_BOOLEAN_FALSE : addressEqual (*target, accountAddress); -extern void -ewmLock (BREthereumEWM ewm) { - pthread_mutex_lock (&ewm->lock); + uint64_t blockNumber = 0; + // check that the transfer has been included, is a send and has been confirmed as final + return (transferExtractStatusIncluded (transfer, NULL, &blockNumber, NULL, NULL, NULL) && + accountIsSource == ETHEREUM_BOOLEAN_TRUE && accountIsTarget == ETHEREUM_BOOLEAN_FALSE && + blockNumber < (context->confirmedBlockHeight)); } -extern void -ewmUnlock (BREthereumEWM ewm) { - pthread_mutex_unlock (&ewm->lock); +static void +ewmSyncToDepthGetLastConfirmedSendTransferHeightWalker (ewmSyncToDepthGetLastConfirmedSendTransferHeightContext *context, + BREthereumTransfer transfer, + unsigned int index) { + uint64_t blockNumber = 0; + transferExtractStatusIncluded (transfer, NULL, &blockNumber, NULL, NULL, NULL); + context->transferBlockHeight = (blockNumber > context->transferBlockHeight ? + blockNumber : context->transferBlockHeight); + return; } -/// MARK: - Blocks - -#if defined (NEVER_DEFINED) -extern BREthereumBlock -ewmLookupBlockByHash(BREthereumEWM ewm, - const BREthereumHash hash) { - BREthereumBlock block = NULL; +static uint64_t +ewmSyncToDepthCalculateBlockHeight (BREthereumEWM ewm, + BRCryptoSyncDepth depth) { + uint64_t blockHeight = 0; pthread_mutex_lock(&ewm->lock); - for (int i = 0; i < array_count(ewm->blocks); i++) - if (ETHEREUM_COMPARISON_EQ == hashCompare(hash, blockGetHash(ewm->blocks[i]))) { - block = ewm->blocks[i]; - break; - } - pthread_mutex_unlock(&ewm->lock); - return block; -} + switch (depth) { + case CRYPTO_SYNC_DEPTH_FROM_LAST_CONFIRMED_SEND: { + if (ewm->blockHeight >= ewm->confirmationsUntilFinal) { + ewmSyncToDepthGetLastConfirmedSendTransferHeightContext context = { ewm, 0, ewm->blockHeight - ewm->confirmationsUntilFinal }; + BREthereumWallet *wallets = ewmGetWallets(ewm); -extern BREthereumBlock -ewmLookupBlock(BREthereumEWM ewm, - BREthereumBlockId bid) { - BREthereumBlock block = NULL; - - pthread_mutex_lock(&ewm->lock); - block = (0 <= bid && bid < array_count(ewm->blocks) - ? ewm->blocks[bid] - : NULL); - pthread_mutex_unlock(&ewm->lock); - return block; -} + for (size_t wid = 0; NULL != wallets[wid]; wid++) + walletWalkTransfers (wallets[wid], &context, + (BREthereumTransferPredicate) ewmSyncToDepthGetLastConfirmedSendTransferHeightPredicate, + (BREthereumTransferWalker) ewmSyncToDepthGetLastConfirmedSendTransferHeightWalker); -extern BREthereumBlockId -ewmLookupBlockId (BREthereumEWM ewm, - BREthereumBlock block) { - BREthereumBlockId bid = -1; + free (wallets); - pthread_mutex_lock(&ewm->lock); - for (int i = 0; i < array_count(ewm->blocks); i++) - if (block == ewm->blocks[i]) { - bid = i; + blockHeight = context.transferBlockHeight; + } + break; + } + case CRYPTO_SYNC_DEPTH_FROM_LAST_TRUSTED_BLOCK: { + const BREthereumBlockCheckpoint *checkpoint = blockCheckpointLookupByNumber (ewm->network, ewm->blockHeight); + blockHeight = NULL == checkpoint ? 0 : checkpoint->number; break; } + case CRYPTO_SYNC_DEPTH_FROM_CREATION: { + // Start a sync from block 0 + blockHeight = 0; + break; + } + } pthread_mutex_unlock(&ewm->lock); - return bid; -} -extern BREthereumBlockId -ewmInsertBlock (BREthereumEWM ewm, - BREthereumBlock block) { - BREthereumBlockId bid = -1; - pthread_mutex_lock(&ewm->lock); - array_add(ewm->blocks, block); - bid = (BREthereumBlockId) (array_count(ewm->blocks) - 1); - pthread_mutex_unlock(&ewm->lock); - ewmSignalBlockEvent(ewm, bid, BLOCK_EVENT_CREATED, SUCCESS, NULL); - return bid; + return blockHeight; } -#endif -extern uint64_t -ewmGetBlockHeight(BREthereumEWM ewm) { - return ewm->blockHeight; -} +extern BREthereumBoolean +ewmSyncToDepth (BREthereumEWM ewm, + BREthereumBoolean pendExistingTransfers, + BRCryptoSyncDepth depth) { + if (EWM_STATE_CONNECTED != ewm->state) return ETHEREUM_BOOLEAN_FALSE; -extern void -ewmUpdateBlockHeight(BREthereumEWM ewm, - uint64_t blockHeight) { - if (blockHeight > ewm->blockHeight) - ewm->blockHeight = blockHeight; -} + uint64_t blockHeight = ewmSyncToDepthCalculateBlockHeight (ewm, depth); -/// MARK: - Transfers + switch (ewm->mode) { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { + pthread_mutex_lock(&ewm->lock); -#if defined (NEVER_DEFINED) -extern BREthereumTransfer -ewmLookupTransfer (BREthereumEWM ewm, - BREthereumTransfer transfer) { - BREthereumTransfer transfer = NULL; + // set the beginning block number to the minimum between the calculated height and + // the known block height + blockHeight = blockHeight < ewm->blockHeight ? blockHeight : ewm->blockHeight; - pthread_mutex_lock(&ewm->lock); - transfer = (0 <= tid && tid < array_count(ewm->transfers) - ? ewm->transfers[tid] - : NULL); - pthread_mutex_unlock(&ewm->lock); - return transfer; -} + if (ETHEREUM_BOOLEAN_IS_TRUE (pendExistingTransfers)) { + BREthereumSyncTransferContext context = { ewm, blockHeight, ewm->blockHeight }; + BREthereumWallet *wallets = ewmGetWallets(ewm); -extern BREthereumTransfer -ewmLookupTransferByHash (BREthereumEWM ewm, - const BREthereumHash hash) { - BREthereumTransfer transfer = NULL; + // Walk each wallet, set all transfers to 'pending' + for (size_t wid = 0; NULL != wallets[wid]; wid++) + walletWalkTransfers (wallets[wid], &context, + (BREthereumTransferPredicate) ewmSyncUpdateTransferPredicate, + (BREthereumTransferWalker) ewmSyncUpdateTransfer); - pthread_mutex_lock(&ewm->lock); - for (int i = 0; i < array_count(ewm->transfers); i++) - if (ETHEREUM_COMPARISON_EQ == hashCompare(hash, transferGetHash(ewm->transfers[i]))) { - transfer = ewm->transfers[i]; - break; - } - pthread_mutex_unlock(&ewm->lock); - return transfer; -} + free (wallets); + } -extern BREthereumTransferId -ewmLookupTransferId (BREthereumEWM ewm, - BREthereumTransfer transfer) { - BREthereumTransfer transfer = -1; + // Abort an in progress sync + if (!ewm->brdSync.completedTransaction || !ewm->brdSync.completedLog) { + // With the following, any callback to `ewmHandleAnnounceComplete` WILL skip out + // before changing `brdSync` state. Of course, the call to ewmHandleSyncAPI() + // (that is below) will immediately reassign new rids thereby mooting the need for + // assignin -1 here. However, assigning -1 here does no harm and allows replacing + // of `ewmHandleSyncAPI` with `ewmSignalSyncAPI` if need be. + ewm->brdSync.ridLog = EWM_REQUEST_ID_UNKNOWN; + ewm->brdSync.ridTransaction = EWM_REQUEST_ID_UNKNOWN; + + // If this was not an 'ongoing' sync, then signal back to 'connected'. This will + // appear as syncEnded(success) - that is okay. + if (ewmIsNotAnOngoingSync (ewm)) + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { EWM_STATE_SYNCING, EWM_STATE_CONNECTED }} + }); + + // Start anew as 'completed' + ewm->brdSync.completedTransaction = 1; + ewm->brdSync.completedLog = 1; + } - pthread_mutex_lock(&ewm->lock); - for (int i = 0; i < array_count(ewm->transfers); i++) - if (transfer == ewm->transfers[i]) { - tid = i; - break; + // don't allow the sync to jump forward so that we don't have a situation where + // we miss transfers + ewm->brdSync.begBlockNumber = (ewm->brdSync.begBlockNumber < blockHeight ? + ewm->brdSync.begBlockNumber : blockHeight); + + // Immediately sync - inline call (not via 'signal'; directly 'handle') + ewmHandleSyncAPI (ewm); + + pthread_mutex_unlock(&ewm->lock); + return ETHEREUM_BOOLEAN_TRUE; } - pthread_mutex_unlock(&ewm->lock); - return tid; + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: + bcsSync (ewm->bcs, blockHeight); + return ETHEREUM_BOOLEAN_TRUE; + } } -extern BREthereumTransferId -ewmInsertTransfer (BREthereumEWM ewm, - BREthereumTransfer transfer) { - BREthereumTransfer transfer; - - pthread_mutex_lock(&ewm->lock); - array_add (ewm->transfers, transfer); - tid = (BREthereumTransferId) (array_count(ewm->transfers) - 1); - pthread_mutex_unlock(&ewm->lock); +/// MARK: - Mode - return tid; +extern BRCryptoSyncMode +ewmGetMode (BREthereumEWM ewm) { + pthread_mutex_lock (&ewm->lock); + BRCryptoSyncMode mode = ewm->mode; + pthread_mutex_unlock (&ewm->lock); + return mode; } extern void -ewmDeleteTransfer (BREthereumEWM ewm, - BREthereumTransfer transfer) { - BREthereumTransfer transfer = ewm->transfers[tid]; - if (NULL == transfer) return; - - // Remove from any (and all - should be but one) wallet - for (int wid = 0; wid < array_count(ewm->wallets); wid++) - if (walletHasTransfer(ewm->wallets[wid], transfer)) { - walletUnhandleTransfer(ewm->wallets[wid], transfer); - ewmSignalTransferEvent(ewm, wid, tid, TRANSFER_EVENT_DELETED, SUCCESS, NULL); - } +ewmUpdateMode (BREthereumEWM ewm, + BRCryptoSyncMode mode) { + pthread_mutex_lock (&ewm->lock); - // Null the ewm's `tid` - MUST NOT array_rm() as all `tid` holders will be dead. - ewm->transfers[tid] = NULL; - transferRelease(transfer); -} -#endif + BRCryptoSyncMode oldMode = ewm->mode; + BRCryptoSyncMode newMode = mode; + + + if (oldMode != newMode) { + + // Disconnect if connected; reconnect if connected. + if (ETHEREUM_BOOLEAN_IS_TRUE(ewmIsConnected(ewm))) + ewmDisconnect(ewm); // Stops periodic dispatch too. + + BRSetOf(BREthereumToken) tokens; + BRSetOf(BREthereumNodeConfig) nodes; + BRSetOf(BREthereumBlock) blocks; + BRSetOf(BREthereumTransaction) transactions; + BRSetOf(BREthereumLog) logs; + BRSetOf(BREthereumWalletState) states; + + // We have BCS in all modes but in BRD_ONLY mode it is never started. + + + // + // This `bcsStop()` is going a) to call `lesStop()` and b) then stop *and clear* + // the BCS event handler. When LES stops, the current LES nodes will be saved by + // calling `bcsSignalNodes()` which, when handled, will then call + // `ewmSignalSaveNodes()`. Note that `lesStop()` will block until `lesThread()` + // actually completes. Thus upon completion there might be at leasat ONE `bcsSignalNodes()` + // event in the BCS event queue... and then the BCS event handler is stopped + // and cleared. + // + // Will the nodes actually get written? There is quite a bit of computation that happens + // in `lesThread()` after `bcsSignalNodes()` is called - including printing to log and + // deactivating TCP/UDP sockets - so it is possible that the ONE event will get dispatched + // and nodes written to file. + // + bcsStop (ewm->bcs); + + // Everything gone at this point. Should not be any references to BCS still using + // BCS at this point. Surely none. + bcsDestroy (ewm->bcs); + + // Get some current state that we'll use when recreating BCS. + BREthereumAddress primaryAddress = accountGetPrimaryAddress(ewm->account); + BREthereumBCSListener listener = ewmCreateBCSListener (ewm); + + // Set the new mode + ewm->mode = newMode; + + // + // We'll create a node-specific BCS here; this parallels how BCS is created in ewmCreat(). + // The pimary difference being that in ewmCreate() we announce newly-recovered transactions + // and logs (recovered from persistent storage). We don't need to reannounce those here + // as they are already in EWM. + // + + switch (newMode) { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: + ewm->bcs = bcsCreate (ewm->network, + primaryAddress, + listener, + newMode, + NULL, + NULL, + NULL, + NULL); + break; -/// MARK: - Wallets + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: + ewmCreateInitialSets (ewm, ewm->network, ewm->accountTimestamp, + &transactions, &logs, &nodes, &blocks, &tokens, &states); + + ewm->bcs = bcsCreate (ewm->network, + primaryAddress, + listener, + newMode, + nodes, + blocks, + transactions, + logs); + + BRSetFreeAll (states, (void (*) (void*)) walletStateRelease); + break; + } -#if defined (NEVER_DEFINED) -extern BREthereumWallet -ewmLookupWallet(BREthereumEWM ewm, - BREthereumWalletId wid) { - BREthereumWallet wallet = NULL; + // Don't reestablish a connection + } + pthread_mutex_unlock (&ewm->lock); +} - pthread_mutex_lock(&ewm->lock); - wallet = (0 <= wid && wid < array_count(ewm->wallets) - ? ewm->wallets[wid] - : NULL); - pthread_mutex_unlock(&ewm->lock); - return wallet; +extern void +ewmWipe (BREthereumNetwork network, + const char *storagePath) { + fileServiceWipe (storagePath, "eth", networkGetName(network)); } -extern BREthereumWalletId -ewmLookupWalletId(BREthereumEWM ewm, - BREthereumWallet wallet) { - BREthereumWalletId wid = -1; +/// MARK: - Blocks +extern uint64_t +ewmGetBlockHeight(BREthereumEWM ewm) { pthread_mutex_lock(&ewm->lock); - for (int i = 0; i < array_count (ewm->wallets); i++) - if (wallet == ewm->wallets[i]) { - wid = i; - break; - } + uint64_t height = ewm->blockHeight; pthread_mutex_unlock(&ewm->lock); - return wid; + return height; } -extern BREthereumWallet -ewmLookupWalletByTransfer (BREthereumEWM ewm, - BREthereumTransfer transfer) { - BREthereumWallet wallet = NULL; +extern void +ewmUpdateBlockHeight(BREthereumEWM ewm, + uint64_t blockHeight) { pthread_mutex_lock(&ewm->lock); - for (int i = 0; i < array_count (ewm->wallets); i++) - if (walletHasTransfer(ewm->wallets[i], transfer)) { - wallet = ewm->wallets[i]; - break; - } + if (blockHeight != ewm->blockHeight) { + ewm->blockHeight = blockHeight; +#if 0 + ewmSignalEWMEvent (ewm, ((BREthereumEWMEvent) { + EWM_EVENT_BLOCK_HEIGHT_UPDATED, + SUCCESS, + { .blockHeight = { blockHeight }} + })); +#endif + } pthread_mutex_unlock(&ewm->lock); - return wallet; } -#endif + +/// MARK: - Transfers +/// MARK: - Wallets extern void ewmInsertWallet (BREthereumEWM ewm, BREthereumWallet wallet) { pthread_mutex_lock(&ewm->lock); array_add (ewm->wallets, wallet); - ewmSignalWalletEvent (ewm, wallet, WALLET_EVENT_CREATED, SUCCESS, NULL); - ewmSignalWalletEvent (ewm, wallet, WALLET_EVENT_BALANCE_UPDATED, SUCCESS, NULL); + ewmSignalWalletEvent (ewm, wallet, (BREthereumWalletEvent) { + WALLET_EVENT_CREATED, + SUCCESS + }); pthread_mutex_unlock(&ewm->lock); } -// -// Wallet (Actions) -// extern BREthereumWallet * ewmGetWallets (BREthereumEWM ewm) { pthread_mutex_lock(&ewm->lock); @@ -1093,14 +1158,17 @@ ewmGetWallets (BREthereumEWM ewm) { return wallets; } -extern unsigned int +extern size_t ewmGetWalletsCount (BREthereumEWM ewm) { - return (unsigned int) array_count(ewm->wallets); + pthread_mutex_lock(&ewm->lock); + size_t count = array_count(ewm->wallets); + pthread_mutex_unlock(&ewm->lock); + return count; } extern BREthereumWallet ewmGetWallet(BREthereumEWM ewm) { - return ewm->walletHoldingEther; + return ewm->walletHoldingEther; // constant } extern BREthereumWallet @@ -1132,16 +1200,22 @@ ewmWalletCreateTransfer(BREthereumEWM ewm, const char *recvAddress, BREthereumAmount amount) { BREthereumTransfer transfer = NULL; + BREthereumAddress *addressPtr = NULL, brAddress; + if (strlen(recvAddress) > 0) { + brAddress = addressCreate(recvAddress); + addressPtr = &brAddress; + } pthread_mutex_lock(&ewm->lock); - - transfer = walletCreateTransfer(wallet, addressCreate(recvAddress), amount); - + transfer = walletCreateTransfer(wallet, addressPtr, amount); pthread_mutex_unlock(&ewm->lock); // Transfer DOES NOT have a hash yet because it is not signed; but it is inserted in the // wallet and can be display, in order, w/o the hash - ewmSignalTransferEvent (ewm, wallet, transfer, TRANSFER_EVENT_CREATED, SUCCESS, NULL); + ewmSignalTransferEvent (ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_CREATED, + SUCCESS + }); return transfer; } @@ -1156,20 +1230,27 @@ ewmWalletCreateTransferGeneric(BREthereumEWM ewm, const char *data) { BREthereumTransfer transfer = NULL; - pthread_mutex_lock(&ewm->lock); + BREthereumAddress *addressPtr = NULL, brAddress; + if (strlen(recvAddress) > 0) { + brAddress = addressCreate(recvAddress); + addressPtr = &brAddress; + } + pthread_mutex_lock(&ewm->lock); transfer = walletCreateTransferGeneric(wallet, - addressCreate(recvAddress), + addressPtr, amount, gasPrice, gasLimit, data); - pthread_mutex_unlock(&ewm->lock); // Transfer DOES NOT have a hash yet because it is not signed; but it is inserted in the // wallet and can be display, in order, w/o the hash - ewmSignalTransferEvent(ewm, wallet, transfer, TRANSFER_EVENT_CREATED, SUCCESS, NULL); + ewmSignalTransferEvent(ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_CREATED, + SUCCESS + }); return transfer; } @@ -1182,32 +1263,81 @@ ewmWalletCreateTransferWithFeeBasis (BREthereumEWM ewm, BREthereumFeeBasis feeBasis) { BREthereumTransfer transfer = NULL; + BREthereumAddress *addressPtr = NULL, brAddress; + if (strlen(recvAddress) > 0) { + brAddress = addressCreate(recvAddress); + addressPtr = &brAddress; + } + pthread_mutex_lock(&ewm->lock); - { - transfer = walletCreateTransferWithFeeBasis (wallet, addressCreate(recvAddress), amount, feeBasis); - } + transfer = walletCreateTransferWithFeeBasis (wallet, addressPtr, amount, feeBasis); pthread_mutex_unlock(&ewm->lock); // Transfer DOES NOT have a hash yet because it is not signed; but it is inserted in the // wallet and can be display, in order, w/o the hash - ewmSignalTransferEvent (ewm, wallet, transfer, TRANSFER_EVENT_CREATED, SUCCESS, NULL); + ewmSignalTransferEvent (ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_CREATED, + SUCCESS + }); return transfer; } -extern BREthereumEther -ewmWalletEstimateTransferFee(BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumAmount amount, - int *overflow) { - return walletEstimateTransferFee(wallet, amount, overflow); +extern BREthereumEther +ewmWalletEstimateTransferFee(BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumAmount amount, + int *overflow) { + pthread_mutex_lock(&ewm->lock); + BREthereumEther fee = walletEstimateTransferFee(wallet, amount, overflow); + pthread_mutex_unlock(&ewm->lock); + return fee; +} + +extern BREthereumEther +ewmWalletEstimateTransferFeeForBasis(BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumAmount amount, + BREthereumGasPrice price, + BREthereumGas gas, + int *overflow) { + pthread_mutex_lock(&ewm->lock); + BREthereumEther fee = walletEstimateTransferFeeDetailed (wallet, amount, price, gas, overflow); + pthread_mutex_unlock(&ewm->lock); + return fee; +} + +extern void +ewmWalletEstimateTransferFeeForTransfer (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumAddress source, + BREthereumAddress *target, + BREthereumAmount amount, + BREthereumGasPrice gasPrice, + BREthereumGas gasLimit) { + BREthereumToken ethToken = ewmWalletGetToken (ewm, wallet); + + // use transfer, instead of transaction, due to the need to fill out the transaction data based on if + // it is a token transfer or not + BREthereumTransfer transfer = transferCreate (source, + target, + amount, + (BREthereumFeeBasis) {FEE_BASIS_GAS, {.gas = {gasLimit, gasPrice}}}, + (NULL == ethToken ? TRANSFER_BASIS_TRANSACTION : TRANSFER_BASIS_LOG)); + + ewmGetGasEstimate (ewm, wallet, transfer, cookie); + + transferRelease (transfer); } extern BREthereumBoolean ewmWalletCanCancelTransfer (BREthereumEWM ewm, BREthereumWallet wallet, BREthereumTransfer oldTransfer) { + pthread_mutex_lock(&ewm->lock); BREthereumTransaction oldTransaction = transferGetOriginatingTransaction(oldTransfer); + pthread_mutex_unlock(&ewm->lock); // TODO: Something about the 'status' (not already cancelled, etc) return AS_ETHEREUM_BOOLEAN (NULL != oldTransaction); @@ -1217,6 +1347,7 @@ extern BREthereumTransfer // status, error ewmWalletCreateTransferToCancel(BREthereumEWM ewm, BREthereumWallet wallet, BREthereumTransfer oldTransfer) { + pthread_mutex_lock(&ewm->lock); BREthereumTransaction oldTransaction = transferGetOriginatingTransaction(oldTransfer); assert (NULL != oldTransaction); @@ -1227,9 +1358,10 @@ ewmWalletCreateTransferToCancel(BREthereumEWM ewm, // Create a new transaction with: a) targetAddress to self (sourceAddress), b) 0 ETH, c) // gasPrice increased (to replacement value). + BREthereumAddress sourceAddress = transactionGetSourceAddress(oldTransaction); BREthereumTransaction transaction = - transactionCreate (transactionGetSourceAddress(oldTransaction), - transactionGetSourceAddress(oldTransaction), + transactionCreate (sourceAddress, + &sourceAddress, etherCreateZero(), gasPriceCreate(newGasPrice), transactionGetGasLimit(oldTransaction), @@ -1244,6 +1376,8 @@ ewmWalletCreateTransferToCancel(BREthereumEWM ewm, ? TRANSFER_BASIS_TRANSACTION : TRANSFER_BASIS_LOG)); walletHandleTransfer(wallet, transfer); + pthread_mutex_unlock(&ewm->lock); + return transfer; } @@ -1251,7 +1385,9 @@ extern BREthereumBoolean ewmWalletCanReplaceTransfer (BREthereumEWM ewm, BREthereumWallet wid, BREthereumTransfer oldTransfer) { + pthread_mutex_lock(&ewm->lock); BREthereumTransaction oldTransaction = transferGetOriginatingTransaction(oldTransfer); + pthread_mutex_unlock(&ewm->lock); // TODO: Something about the 'status' (not already replaced, etc) return AS_ETHEREUM_BOOLEAN (NULL != oldTransaction); @@ -1265,6 +1401,7 @@ ewmWalletCreateTransferToReplace (BREthereumEWM ewm, BREthereumBoolean updateGasPrice, BREthereumBoolean updateGasLimit, BREthereumBoolean updateNonce) { + pthread_mutex_lock(&ewm->lock); BREthereumTransaction oldTransaction = transferGetOriginatingTransaction(oldTransfer); assert (NULL != oldTransaction); @@ -1312,6 +1449,7 @@ ewmWalletCreateTransferToReplace (BREthereumEWM ewm, ? TRANSFER_BASIS_TRANSACTION : TRANSFER_BASIS_LOG)); walletHandleTransfer(wallet, transfer); + pthread_mutex_unlock(&ewm->lock); return transfer; } @@ -1320,7 +1458,10 @@ static void ewmWalletSignTransferAnnounce (BREthereumEWM ewm, BREthereumWallet wallet, BREthereumTransfer transfer) { - ewmSignalTransferEvent (ewm, wallet, transfer, TRANSFER_EVENT_SIGNED, SUCCESS, NULL); + ewmSignalTransferEvent (ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_SIGNED, + SUCCESS + }); } extern void // status, error @@ -1373,16 +1514,32 @@ ewmWalletGetTransferCount(BREthereumEWM ewm, return count; } +extern BREthereumAddress +ewmWalletGetAddress (BREthereumEWM ewm, + BREthereumWallet wallet) { + return walletGetAddress(wallet); +} + +extern BREthereumBoolean +ewmWalletHasAddress (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumAddress address) { + return addressEqual(address, walletGetAddress(wallet)); +} + extern BREthereumToken ewmWalletGetToken (BREthereumEWM ewm, BREthereumWallet wallet) { - return walletGetToken(wallet); + return walletGetToken(wallet); // constant } extern BREthereumAmount ewmWalletGetBalance(BREthereumEWM ewm, BREthereumWallet wallet) { - return walletGetBalance(wallet); + pthread_mutex_lock(&ewm->lock); + BREthereumAmount balance = walletGetBalance(wallet); + pthread_mutex_unlock(&ewm->lock); + return balance; } @@ -1390,44 +1547,61 @@ extern BREthereumGas ewmWalletGetGasEstimate(BREthereumEWM ewm, BREthereumWallet wallet, BREthereumTransfer transfer) { - return transferGetGasEstimate(transfer); + pthread_mutex_lock(&ewm->lock); + BREthereumGas gas = transferGetGasEstimate(transfer); + pthread_mutex_unlock(&ewm->lock); + return gas; } extern BREthereumGas ewmWalletGetDefaultGasLimit(BREthereumEWM ewm, BREthereumWallet wallet) { - return walletGetDefaultGasLimit(wallet); + pthread_mutex_lock(&ewm->lock); + BREthereumGas gas = walletGetDefaultGasLimit(wallet); + pthread_mutex_unlock(&ewm->lock); + return gas; } extern void ewmWalletSetDefaultGasLimit(BREthereumEWM ewm, BREthereumWallet wallet, BREthereumGas gasLimit) { + pthread_mutex_lock(&ewm->lock); walletSetDefaultGasLimit(wallet, gasLimit); + pthread_mutex_unlock(&ewm->lock); + ewmSignalWalletEvent(ewm, - wallet, - WALLET_EVENT_DEFAULT_GAS_LIMIT_UPDATED, - SUCCESS, - NULL); + wallet, + (BREthereumWalletEvent) { + WALLET_EVENT_DEFAULT_GAS_LIMIT_UPDATED, + SUCCESS + }); } extern BREthereumGasPrice ewmWalletGetDefaultGasPrice(BREthereumEWM ewm, - BREthereumWallet wallet) { - return walletGetDefaultGasPrice(wallet); + BREthereumWallet wallet) { + pthread_mutex_lock(&ewm->lock); + BREthereumGasPrice price = walletGetDefaultGasPrice(wallet); + pthread_mutex_unlock(&ewm->lock); + return price; } extern void ewmWalletSetDefaultGasPrice(BREthereumEWM ewm, BREthereumWallet wallet, BREthereumGasPrice gasPrice) { + pthread_mutex_lock(&ewm->lock); walletSetDefaultGasPrice(wallet, gasPrice); + pthread_mutex_unlock(&ewm->lock); + ewmSignalWalletEvent(ewm, - wallet, - WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, - SUCCESS, - NULL); + wallet, + (BREthereumWalletEvent) { + WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, + SUCCESS + }); } @@ -1444,19 +1618,17 @@ extern void ewmHandleGasPrice (BREthereumEWM ewm, BREthereumWallet wallet, BREthereumGasPrice gasPrice) { - pthread_mutex_lock(&ewm->lock); - walletSetDefaultGasPrice(wallet, gasPrice); - + ewmSignalWalletEvent(ewm, - wallet, - WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, - SUCCESS, NULL); - - pthread_mutex_unlock(&ewm->lock); + wallet, + (BREthereumWalletEvent) { + WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, + SUCCESS + }); } - +#if defined (NEVER_DEFINED) /** * Handle a `gasEstimate` for `transaction` in `wallet` * @@ -1470,20 +1642,17 @@ ewmHandleGasEstimate (BREthereumEWM ewm, BREthereumWallet wallet, BREthereumTransfer transfer, BREthereumGas gasEstimate) { - pthread_mutex_lock(&ewm->lock); - transferSetGasEstimate(transfer, gasEstimate); - + ewmSignalTransferEvent(ewm, - wallet, - transfer, - TRANSFER_EVENT_GAS_ESTIMATE_UPDATED, - SUCCESS, NULL); - - pthread_mutex_unlock(&ewm->lock); - + wallet, + transfer, + (BREthereumTransferEvent) { + TRANSFER_EVENT_GAS_ESTIMATE_UPDATED, + SUCCESS + }); } - +#endif // ============================================================================================== // // LES(BCS)/BRD Handlers @@ -1509,19 +1678,19 @@ ewmHandleBlockChain (BREthereumEWM ewm, uint64_t headBlockNumber, uint64_t headBlockTimestamp) { // Don't report during BCS sync. - if (BRD_ONLY == ewm->mode || ETHEREUM_BOOLEAN_IS_FALSE(bcsSyncInProgress (ewm->bcs))) + if (CRYPTO_SYNC_MODE_API_ONLY == ewm->mode || ETHEREUM_BOOLEAN_IS_FALSE(bcsSyncInProgress (ewm->bcs))) eth_log ("EWM", "BlockChain: %" PRIu64, headBlockNumber); // At least this - allows for: ewmGetBlockHeight - ewm->blockHeight = headBlockNumber; + ewmUpdateBlockHeight (ewm, headBlockNumber); #if defined (NEVER_DEFINED) // TODO: Need a 'block id' - or axe the need of 'block id'? ewmSignalBlockEvent (ewm, - (BREthereumBlockId) 0, - BLOCK_EVENT_CHAINED, - SUCCESS, - NULL); + (BREthereumBlockId) 0, + BLOCK_EVENT_CHAINED, + SUCCESS, + NULL); #endif } @@ -1535,38 +1704,39 @@ ewmHandleBlockChain (BREthereumEWM ewm, extern void ewmHandleAccountState (BREthereumEWM ewm, BREthereumAccountState accountState) { - pthread_mutex_lock(&ewm->lock); - eth_log("EWM", "AccountState: Nonce: %" PRIu64, accountState.nonce); - - accountSetAddressNonce(ewm->account, accountGetPrimaryAddress(ewm->account), - accountState.nonce, - ETHEREUM_BOOLEAN_FALSE); - + ewmHandleAnnounceNonce (ewm, accountGetPrimaryAddress(ewm->account), accountState.nonce, 0); ewmSignalBalance(ewm, amountCreateEther(accountState.balance)); - pthread_mutex_unlock(&ewm->lock); } extern void ewmHandleBalance (BREthereumEWM ewm, BREthereumAmount amount) { - pthread_mutex_lock(&ewm->lock); - BREthereumWallet wallet = (AMOUNT_ETHER == amountGetType(amount) ? ewmGetWallet(ewm) : ewmGetWalletHoldingToken(ewm, amountGetToken (amount))); - + int amountTypeMismatch; - + if (ETHEREUM_COMPARISON_EQ != amountCompare(amount, walletGetBalance(wallet), &amountTypeMismatch)) { walletSetBalance(wallet, amount); ewmSignalWalletEvent (ewm, wallet, - WALLET_EVENT_BALANCE_UPDATED, - SUCCESS, - NULL); + (BREthereumWalletEvent) { + WALLET_EVENT_BALANCE_UPDATED, + SUCCESS + }); + + { + char *amountAsString = (AMOUNT_ETHER == amountGetType(amount) + ? etherGetValueString (amountGetEther(amount), WEI) + : tokenQuantityGetValueString (amountGetTokenQuantity(amount), TOKEN_QUANTITY_TYPE_INTEGER)); + eth_log("EWM", "Balance: %s %s (%s)", amountAsString, + (AMOUNT_ETHER == amountGetType(amount) ? "ETH" : tokenGetName(amountGetToken(amount))), + (AMOUNT_ETHER == amountGetType(amount) ? "WEI" : "INTEGER")); + free (amountAsString); + } } - pthread_mutex_unlock(&ewm->lock); } static int @@ -1585,25 +1755,26 @@ ewmReportTransferStatusAsEvent (BREthereumEWM ewm, BREthereumWallet wallet, BREthereumTransfer transfer) { if (ETHEREUM_BOOLEAN_IS_TRUE (transferHasStatus (transfer, TRANSFER_STATUS_SUBMITTED))) - ewmSignalTransferEvent(ewm, wallet, transfer, - TRANSFER_EVENT_SUBMITTED, - SUCCESS, NULL); + ewmSignalTransferEvent(ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_SUBMITTED, + SUCCESS + }); else if (ETHEREUM_BOOLEAN_IS_TRUE (transferHasStatus (transfer, TRANSFER_STATUS_INCLUDED))) - ewmSignalTransferEvent(ewm, wallet, transfer, - TRANSFER_EVENT_INCLUDED, - SUCCESS, NULL); + ewmSignalTransferEvent(ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_INCLUDED, + SUCCESS + }); else if (ETHEREUM_BOOLEAN_IS_TRUE (transferHasStatus (transfer, TRANSFER_STATUS_ERRORED))) { char *reason = NULL; transferExtractStatusError (transfer, &reason); - ewmSignalTransferEvent(ewm, wallet, transfer, - TRANSFER_EVENT_ERRORED, - ERROR_TRANSACTION_SUBMISSION, - (NULL == reason ? "" : reason)); - // TODO: free(reason)? - // Note: ewmSignalTransferEvent expects the 'reason' to stick around an never frees it. - // If we free here, the string will be gone by the time it is handled. + ewmSignalTransferEvent (ewm, wallet, transfer, + transferEventCreateError (TRANSFER_EVENT_ERRORED, + ERROR_TRANSACTION_SUBMISSION, + reason)); + + if (NULL != reason) free (reason); } } @@ -1647,18 +1818,53 @@ ewmHandleTransactionOriginatingLog (BREthereumEWM ewm, } } +static void +ewmHandleLogFeeBasis (BREthereumEWM ewm, + BREthereumHash hash, + BREthereumTransfer transferTransaction, + BREthereumTransfer transferLog) { + + // Find the ETH transfer, if needed + if (NULL == transferTransaction) + transferTransaction = walletGetTransferByIdentifier (ewmGetWallet(ewm), hash); + + // If none exists, then the transaction hasn't been 'synced' yet. + if (NULL == transferTransaction) return; + + // If we have a TOK transfer, set the fee basis. + if (NULL != transferLog) + transferSetFeeBasis(transferLog, transferGetFeeBasis(transferTransaction)); + + // but if we don't have a TOK transfer, find every transfer referencing `hash` and set the basis. + else + for (size_t wid = 0; wid < array_count(ewm->wallets); wid++) { + BREthereumWallet wallet = ewm->wallets[wid]; + + // We are only looking for TOK transfers (non-ETH). + if (wallet == ewm->walletHoldingEther) continue; + + size_t tidLimit = walletGetTransferCount (wallet); + for (size_t tid = 0; tid < tidLimit; tid++) { + transferLog = walletGetTransferByIndex (wallet, tid); + + // Look for a log that has a matching transaction hash + BREthereumLog log = transferGetBasisLog(transferLog); + if (NULL != log) { + BREthereumHash transactionHash; + if (ETHEREUM_BOOLEAN_TRUE == logExtractIdentifier (log, &transactionHash, NULL) && + ETHEREUM_BOOLEAN_TRUE == hashEqual (transactionHash, hash)) + ewmHandleLogFeeBasis (ewm, hash, transferTransaction, transferLog); + } + } + } +} + extern void ewmHandleTransaction (BREthereumEWM ewm, BREthereumBCSCallbackTransactionType type, OwnershipGiven BREthereumTransaction transaction) { BREthereumHash hash = transactionGetHash(transaction); - BREthereumHashString hashString; - hashFillString(hash, hashString); - eth_log ("EWM", "Transaction: \"%s\", Change: %s, Status: %d", hashString, - BCS_CALLBACK_TRANSACTION_TYPE_NAME(type), - transactionGetStatus(transaction).type); - // Find the wallet BREthereumWallet wallet = ewmGetWallet(ewm); assert (NULL != wallet); @@ -1686,15 +1892,20 @@ ewmHandleTransaction (BREthereumEWM ewm, transfer = transferCreateWithTransaction (transaction); // transaction ownership given walletHandleTransfer (wallet, transfer); - walletUpdateBalance (wallet); - - ewmSignalTransferEvent (ewm, wallet, transfer, - TRANSFER_EVENT_CREATED, - SUCCESS, NULL); - ewmSignalWalletEvent (ewm, wallet, WALLET_EVENT_BALANCE_UPDATED, - SUCCESS, - NULL); + // We've added a transfer and arguably we should update the wallet's balance. But don't. + // Ethereum is 'account based'; we'll only update the balance based on a account state + // change (based on a P2P or API callback). + // + // walletUpdateBalance (wallet); + + ewmSignalTransferEvent (ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_CREATED, + SUCCESS + }); + + // If this transfer is referenced by a log, fill out the log's fee basis. + ewmHandleLogFeeBasis (ewm, hash, transfer, NULL); needStatusEvent = 1; } @@ -1713,8 +1924,15 @@ ewmHandleTransaction (BREthereumEWM ewm, transferSetBasisForTransaction (transfer, transaction); // transaction ownership given } - if (needStatusEvent) + if (needStatusEvent) { + BREthereumHashString hashString; + hashFillString(hash, hashString); + eth_log ("EWM", "Transaction: \"%s\", Change: %s, Status: %d", hashString, + BCS_CALLBACK_TRANSACTION_TYPE_NAME(type), + transactionGetStatus(transaction).type); + ewmReportTransferStatusAsEvent(ewm, wallet, transfer); + } ewmHandleTransactionOriginatingLog (ewm, type, transaction); } @@ -1725,23 +1943,14 @@ ewmHandleLog (BREthereumEWM ewm, OwnershipGiven BREthereumLog log) { BREthereumHash logHash = logGetHash(log); - // Assert that we always have an identifier for `log`. BREthereumHash transactionHash; size_t logIndex; - assert (ETHEREUM_BOOLEAN_IS_TRUE (logExtractIdentifier(log, &transactionHash, &logIndex))); - - BREthereumHashString logHashString; - hashFillString(logHash, logHashString); - - BREthereumHashString transactionHashString; - hashFillString(transactionHash, transactionHashString); - - eth_log ("EWM", "Log: %s { %8s @ %zu }, Change: %s, Status: %d", - logHashString, transactionHashString, logIndex, - BCS_CALLBACK_TRANSACTION_TYPE_NAME(type), - logGetStatus(log).type); - BREthereumToken token = tokenLookupByAddress(logGetAddress(log)); + // Assert that we always have an identifier for `log`. + BREthereumBoolean extractedIdentifier = logExtractIdentifier (log, &transactionHash, &logIndex); + assert (ETHEREUM_BOOLEAN_IS_TRUE (extractedIdentifier)); + + BREthereumToken token = ewmLookupToken (ewm, logGetAddress(log)); if (NULL == token) { logRelease(log); return;} // TODO: Confirm LogTopic[0] is 'transfer' @@ -1761,15 +1970,20 @@ ewmHandleLog (BREthereumEWM ewm, transfer = transferCreateWithLog (log, token, ewm->coder); // log ownership given walletHandleTransfer (wallet, transfer); - walletUpdateBalance (wallet); - ewmSignalTransferEvent (ewm, wallet, transfer, - TRANSFER_EVENT_CREATED, - SUCCESS, NULL); + // We've added a transfer and arguably we should update the wallet's balance. But don't. + // Ethereum is 'account based'; we'll only update the balance based on a account state + // change (based on a P2P or API callback). + // + // walletUpdateBalance (wallet); + + ewmSignalTransferEvent (ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_CREATED, + SUCCESS + }); - ewmSignalWalletEvent (ewm, wallet, WALLET_EVENT_BALANCE_UPDATED, - SUCCESS, - NULL); + // If this transfer references a transaction, fill out this log's fee basis + ewmHandleLogFeeBasis (ewm, transactionHash, NULL, transfer); needStatusEvent = 1; } @@ -1785,8 +1999,20 @@ ewmHandleLog (BREthereumEWM ewm, transferSetBasisForLog (transfer, log); // log ownership given } - if (needStatusEvent) + if (needStatusEvent) { + BREthereumHashString logHashString; + hashFillString(logHash, logHashString); + + BREthereumHashString transactionHashString; + hashFillString(transactionHash, transactionHashString); + + eth_log ("EWM", "Log: %s { %8s @ %zu }, Change: %s, Status: %d", + logHashString, transactionHashString, logIndex, + BCS_CALLBACK_TRANSACTION_TYPE_NAME(type), + logGetStatus(log).type); + ewmReportTransferStatusAsEvent (ewm, wallet, transfer); + } } extern void @@ -1795,10 +2021,10 @@ ewmHandleSaveBlocks (BREthereumEWM ewm, size_t count = array_count(blocks); eth_log("EWM", "Save Blocks (Storage): %zu", count); - fileServiceClear(ewm->fs, fileServiceTypeBlocks); + fileServiceReplace (ewm->fs, ewmFileServiceTypeBlocks, + (const void **) blocks, + count); - for (size_t index = 0; index < count; index++) - fileServiceSave (ewm->fs, fileServiceTypeBlocks, blocks[index]); array_free (blocks); } @@ -1808,10 +2034,9 @@ ewmHandleSaveNodes (BREthereumEWM ewm, size_t count = array_count(nodes); eth_log("EWM", "Save Nodes (Storage): %zu", count); - fileServiceClear(ewm->fs, fileServiceTypeNodes); - - for (size_t index = 0; index < count; index++) - fileServiceSave(ewm->fs, fileServiceTypeNodes, nodes[index]); + fileServiceReplace (ewm->fs, ewmFileServiceTypeNodes, + (const void **) nodes, + count); array_free (nodes); } @@ -1829,11 +2054,11 @@ ewmHandleSaveTransaction (BREthereumEWM ewm, fileName); if (CLIENT_CHANGE_REM == type || CLIENT_CHANGE_UPD == type) - fileServiceRemove (ewm->fs, fileServiceTypeTransactions, - fileServiceTypeTransactionV1Identifier (ewm, ewm->fs, transaction)); + fileServiceRemove (ewm->fs, ewmFileServiceTypeTransactions, + fileServiceGetIdentifier(ewm->fs, ewmFileServiceTypeTransactions, transaction)); if (CLIENT_CHANGE_ADD == type || CLIENT_CHANGE_UPD == type) - fileServiceSave (ewm->fs, fileServiceTypeTransactions, transaction); + fileServiceSave (ewm->fs, ewmFileServiceTypeTransactions, transaction); } extern void @@ -1849,11 +2074,47 @@ ewmHandleSaveLog (BREthereumEWM ewm, filename); if (CLIENT_CHANGE_REM == type || CLIENT_CHANGE_UPD == type) - fileServiceRemove (ewm->fs, fileServiceTypeLogs, - fileServiceTypeLogV1Identifier(ewm, ewm->fs, log)); + fileServiceRemove (ewm->fs, ewmFileServiceTypeLogs, + fileServiceGetIdentifier (ewm->fs, ewmFileServiceTypeLogs, log)); if (CLIENT_CHANGE_ADD == type || CLIENT_CHANGE_UPD == type) - fileServiceSave (ewm->fs, fileServiceTypeLogs, log); + fileServiceSave (ewm->fs, ewmFileServiceTypeLogs, log); +} + +extern void +ewmHandleSaveWallet (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumClientChangeType type) { + BREthereumWalletState state = walletStateCreate (wallet); + + // If this is the primaryWallet, hack in the nonce + if (wallet == ewm->walletHoldingEther) { + walletStateSetNonce (state, + accountGetAddressNonce (ewm->account, + accountGetPrimaryAddress(ewm->account))); + } + + BREthereumHash hash = walletStateGetHash(state); + BREthereumHashString filename; + hashFillString(hash, filename); + + eth_log ("EWM", "Wallet: Save: %s: %s", + CLIENT_CHANGE_TYPE_NAME (type), + filename); + + switch (type) { + case CLIENT_CHANGE_REM: + fileServiceRemove (ewm->fs, ewmFileServiceTypeWallets, + fileServiceGetIdentifier (ewm->fs, ewmFileServiceTypeWallets, state)); + break; + + case CLIENT_CHANGE_ADD: + case CLIENT_CHANGE_UPD: + fileServiceSave (ewm->fs, ewmFileServiceTypeWallets, state); + break; + } + + walletStateRelease (state); } extern void @@ -1862,16 +2123,38 @@ ewmHandleSync (BREthereumEWM ewm, uint64_t blockNumberStart, uint64_t blockNumberCurrent, uint64_t blockNumberStop) { - assert (P2P_ONLY == ewm->mode || P2P_WITH_BRD_SYNC == ewm->mode); - - BREthereumEWMEvent event = (blockNumberCurrent == blockNumberStart - ? EWM_EVENT_SYNC_STARTED - : (blockNumberCurrent == blockNumberStop - ? EWM_EVENT_SYNC_STOPPED - : EWM_EVENT_SYNC_CONTINUES)); - double syncCompletePercent = 100.0 * (blockNumberCurrent - blockNumberStart) / (blockNumberStop - blockNumberStart); - - ewmSignalEWMEvent (ewm, event, SUCCESS, NULL); + assert (CRYPTO_SYNC_MODE_P2P_ONLY == ewm->mode || CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == ewm->mode); + + BRCryptoSyncPercentComplete syncCompletePercent = AS_CRYPTO_SYNC_PERCENT_COMPLETE (100.0 * (blockNumberCurrent - blockNumberStart) / (blockNumberStop - blockNumberStart)); + // We do not have blockTimestampCurrent + + BREthereumEWMEvent event; + + if (blockNumberCurrent == blockNumberStart) { + event = (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { ewm->state, EWM_STATE_SYNCING }} + }; + } + else if (blockNumberCurrent == blockNumberStop) { + event = (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { ewm->state, EWM_STATE_CONNECTED }} + }; + } + else { + event = (BREthereumEWMEvent) { + EWM_EVENT_SYNC_PROGRESS, + SUCCESS, + { .syncProgress = { + NO_CRYPTO_SYNC_TIMESTAMP, // We do not have a timestamp + syncCompletePercent }} + }; + } + + ewmSignalEWMEvent (ewm, event); eth_log ("EWM", "Sync: %d, %.2f%%", type, syncCompletePercent); } @@ -1883,7 +2166,7 @@ ewmHandleGetBlocks (BREthereumEWM ewm, uint64_t blockStart, uint64_t blockStop) { - char *strAddress = addressGetEncodedString(address, 0); + char *strAddress = addressGetEncodedString(&address, 0); ewm->client.funcGetBlocks (ewm->client.context, ewm, @@ -1904,19 +2187,22 @@ ewmUpdateWalletBalance(BREthereumEWM ewm, BREthereumWallet wallet) { if (NULL == wallet) { - ewmSignalWalletEvent(ewm, wallet, WALLET_EVENT_BALANCE_UPDATED, - ERROR_UNKNOWN_WALLET, - NULL); + ewmSignalWalletEvent (ewm, wallet, + walletEventCreateError (WALLET_EVENT_BALANCE_UPDATED, + ERROR_UNKNOWN_WALLET, + NULL)); } else if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) { - ewmSignalWalletEvent(ewm, wallet, WALLET_EVENT_BALANCE_UPDATED, - ERROR_NODE_NOT_CONNECTED, - NULL); + ewmSignalWalletEvent(ewm, wallet, + walletEventCreateError (WALLET_EVENT_BALANCE_UPDATED, + ERROR_NODE_NOT_CONNECTED, + NULL)); } else { switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { - char *address = addressGetEncodedString(walletGetAddress(wallet), 0); + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { + BREthereumAddress brAddress = walletGetAddress(wallet); + char *address = addressGetEncodedString(&brAddress, 0); ewm->client.funcGetBalance (ewm->client.context, ewm, @@ -1928,8 +2214,8 @@ ewmUpdateWalletBalance(BREthereumEWM ewm, break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: LES Update Wallet Balance break; } @@ -1940,16 +2226,16 @@ static void ewmUpdateBlockNumber (BREthereumEWM ewm) { if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) return; switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { ewm->client.funcGetBlockNumber (ewm->client.context, ewm, ++ewm->requestId); break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: LES Update Wallet Balance break; } @@ -1959,9 +2245,10 @@ static void ewmUpdateNonce (BREthereumEWM ewm) { if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) return; switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { - char *address = addressGetEncodedString(accountGetPrimaryAddress(ewm->account), 0); + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { + BREthereumAddress primaryAddress = accountGetPrimaryAddress(ewm->account); + char *address = addressGetEncodedString(&primaryAddress, 0); ewm->client.funcGetNonce (ewm->client.context, ewm, @@ -1972,8 +2259,8 @@ ewmUpdateNonce (BREthereumEWM ewm) { break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: LES Update Wallet Balance break; } @@ -1986,14 +2273,14 @@ ewmUpdateNonce (BREthereumEWM ewm) { */ static void ewmUpdateTransactions (BREthereumEWM ewm) { - if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) { - // Nothing to announce - return; - } + // Nothing to update if not connected. + if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) return; + switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { - char *address = addressGetEncodedString(accountGetPrimaryAddress(ewm->account), 0); + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { + BREthereumAddress primaryAddress = accountGetPrimaryAddress(ewm->account); + char *address = addressGetEncodedString(&primaryAddress, 0); ewm->client.funcGetTransactions (ewm->client.context, ewm, @@ -2006,8 +2293,8 @@ ewmUpdateTransactions (BREthereumEWM ewm) { break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: LES Update Wallet Balance break; } @@ -2025,14 +2312,14 @@ static void ewmUpdateLogs (BREthereumEWM ewm, BREthereumWallet wid, BREthereumContractEvent event) { - if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) { - // Nothing to announce - return; - } + // Nothing to update if not connected. + if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) return; + switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { - char *address = addressGetEncodedString(accountGetPrimaryAddress(ewm->account), 0); + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { + BREthereumAddress primaryAddress = accountGetPrimaryAddress(ewm->account); + char *address = addressGetEncodedString(&primaryAddress, 0); char *encodedAddress = eventERC20TransferEncodeAddress (event, address); const char *contract = ewmGetWalletContractAddress(ewm, wid); @@ -2051,65 +2338,142 @@ ewmUpdateLogs (BREthereumEWM ewm, break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: LES Update Logs break; } } +// ============================================================================================== // -// Periodicaly query the BRD backend to get current status (block number, nonce, balances, -// transactions and logs) The event will be NULL (as specified for a 'period dispatcher' - See -// `eventHandlerSetTimeoutDispatcher()`) +// Announce {Transactions, Logs} Complete // -static void -ewmPeriodicDispatcher (BREventHandler handler, - BREventTimeout *event) { - BREthereumEWM ewm = (BREthereumEWM) event->context; - - if (ewm->state != LIGHT_NODE_CONNECTED) return; - if (P2P_ONLY == ewm->mode || P2P_WITH_BRD_SYNC == ewm->mode) return; +extern void +ewmHandleAnnounceComplete (BREthereumEWM ewm, + BREthereumBoolean isTransaction, + BREthereumBoolean success, + int rid) { + if (ETHEREUM_BOOLEAN_IS_TRUE(isTransaction)) { + // skip out if `rid` doesn't match + if (rid != ewm->brdSync.ridTransaction) return; + ewm->brdSync.completedTransaction = 1; // completed, no matter success or failure + } + else /* isLog */ { + // skip out if `rid` doesn't match + if (rid != ewm->brdSync.ridLog) return; + ewm->brdSync.completedLog = 1; // completed, no matter success or failure + } + + // If both transaction and log are completed we'll signal complete and then, on + // success, update `begBlockNumber` + if (ewm->brdSync.completedTransaction && ewm->brdSync.completedLog) { + // If this was not an 'ongoing' sync, then signal back to 'connected' + if (ewmIsNotAnOngoingSync(ewm)) + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { EWM_STATE_SYNCING, EWM_STATE_CONNECTED }} + }); + + // On success, advance the begBlockNumber + if (ETHEREUM_BOOLEAN_IS_TRUE(success)) + ewm->brdSync.begBlockNumber = (ewm->brdSync.endBlockNumber >= EWM_BRD_SYNC_START_BLOCK_OFFSET + ? ewm->brdSync.endBlockNumber - EWM_BRD_SYNC_START_BLOCK_OFFSET + : 0); + } +} + +extern void +ewmHandleSyncAPI (BREthereumEWM ewm) { + if (ewm->state != EWM_STATE_CONNECTED) return; + if (CRYPTO_SYNC_MODE_P2P_ONLY == ewm->mode || CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == ewm->mode) return; + // Get this always and early. ewmUpdateBlockNumber(ewm); ewmUpdateNonce(ewm); // Handle a BRD Sync: - // 1) check if the prior sync has completed. - if (ewm->brdSync.completedTransaction && ewm->brdSync.completedLog) { - // 1a) if so, advance the sync range by updating `begBlockNumber` - ewm->brdSync.begBlockNumber = (ewm->brdSync.endBlockNumber >= EWM_BRD_SYNC_START_BLOCK_OFFSET - ? ewm->brdSync.endBlockNumber - EWM_BRD_SYNC_START_BLOCK_OFFSET - : 0); - } - // 2) completed or not, update the `endBlockNumber` to the current block height. + // 1) if a prior sync has not completed, skip out + if (!ewm->brdSync.completedTransaction || !ewm->brdSync.completedLog) return; + + // 2) Update the `endBlockNumber` to the current block height. ewm->brdSync.endBlockNumber = ewmGetBlockHeight(ewm); - // 3) We'll query all transactions for this ewm's account. That will give us a shot at - // getting the nonce for the account's address correct. We'll save all the transactions and - // then process them into wallet as wallets exist. - ewmUpdateTransactions(ewm); - // Note: we don't do the following `ewmUpdateTransactions` because that is `extern` - ewm->brdSync.ridTransaction = ewm->requestId; - ewm->brdSync.completedTransaction = 0; - - // 4) Similarly, we'll query all logs for this ewm's account. We'll process these into - // (token) transactions and associate with their wallet. - ewmUpdateLogs(ewm, NULL, eventERC20Transfer); - // Note: we don't do the following `ewmUpdateLogs because that is `extern` - ewm->brdSync.ridLog = ewm->requestId; - ewm->brdSync.completedLog = 0; + // 3) if the `endBlockNumber` differs from the `begBlockNumber` then perform a 'sync' + if (ewm->brdSync.begBlockNumber != ewm->brdSync.endBlockNumber) { + + // If this is not an 'ongoing' sync, then signal 'syncing' + if (ewmIsNotAnOngoingSync(ewm)) + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_CHANGED, + SUCCESS, + { .changed = { EWM_STATE_CONNECTED, EWM_STATE_SYNCING }} + }); + + // 3a) For all the registered (aka 'known') wallets, get each balance. + for (int i = 0; i < array_count(ewm->wallets); i++) + ewmUpdateWalletBalance (ewm, ewm->wallets[i]); + + // If this is not an 'ongoing' sync, then arbitrarily report progress - half way + // between transactions and logs + if (ewmIsNotAnOngoingSync(ewm)) + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_SYNC_PROGRESS, + SUCCESS, + { .syncProgress = { + NO_CRYPTO_SYNC_TIMESTAMP, // We do not have a timestamp + AS_CRYPTO_SYNC_PERCENT_COMPLETE(33.33) }} + }); + + + // 3b) We'll query all transactions for this ewm's account. That will give us a shot at + // getting the nonce for the account's address correct. We'll save all the transactions and + // then process them into wallet as wallets exist. + ewmUpdateTransactions(ewm); + + // Record an 'update transaction' as in progress + ewm->brdSync.ridTransaction = ewm->requestId; + ewm->brdSync.completedTransaction = 0; + + // If this is not an 'ongoing' sync, then arbitrarily report progress - half way + // between transactions and logs + if (ewmIsNotAnOngoingSync(ewm)) + ewmSignalEWMEvent (ewm, (BREthereumEWMEvent) { + EWM_EVENT_SYNC_PROGRESS, + SUCCESS, + { .syncProgress = { + NO_CRYPTO_SYNC_TIMESTAMP, // We do not have a timestamp + AS_CRYPTO_SYNC_PERCENT_COMPLETE(66.67) }} + }); + + // 3c) Similarly, we'll query all logs for this ewm's account. We'll process these into + // (token) transactions and associate with their wallet. + ewmUpdateLogs(ewm, NULL, eventERC20Transfer); + + // Record an 'update log' as in progress + ewm->brdSync.ridLog = ewm->requestId; + ewm->brdSync.completedLog = 0; + } // End handling a BRD Sync - - // For all the known wallets, get their balance. - for (int i = 0; i < array_count(ewm->wallets); i++) - ewmUpdateWalletBalance (ewm, ewm->wallets[i]); if (NULL != ewm->bcs) bcsClean (ewm->bcs); } +// +// Periodicaly query the BRD backend to get current status (block number, nonce, balances, +// transactions and logs) The event will be NULL (as specified for a 'period dispatcher' - See +// `eventHandlerSetTimeoutDispatcher()`) +// +static void +ewmPeriodicDispatcher (BREventHandler handler, + BREventTimeout *event) { + BREthereumEWM ewm = (BREthereumEWM) event->context; + ewmHandleSyncAPI(ewm); +} + extern void ewmTransferFillRawData (BREthereumEWM ewm, BREthereumWallet wallet, @@ -2117,11 +2481,13 @@ ewmTransferFillRawData (BREthereumEWM ewm, uint8_t **bytesPtr, size_t *bytesCountPtr) { assert (NULL != bytesCountPtr && NULL != bytesPtr); + pthread_mutex_lock (&ewm->lock); assert (walletHasTransfer(wallet, transfer)); BREthereumTransaction transaction = transferGetOriginatingTransaction (transfer); assert (NULL != transaction); assert (ETHEREUM_BOOLEAN_IS_TRUE (transactionIsSigned(transaction))); + pthread_mutex_unlock (&ewm->lock); BRRlpItem item = transactionRlpEncode(transaction, ewm->network, @@ -2143,9 +2509,11 @@ ewmTransferGetRawDataHexEncoded(BREthereumEWM ewm, BREthereumTransfer transfer, const char *prefix) { assert (walletHasTransfer(wallet, transfer)); - + + pthread_mutex_lock (&ewm->lock); BREthereumTransaction transaction = transferGetOriginatingTransaction (transfer); - + pthread_mutex_unlock (&ewm->lock); + return (NULL == transaction ? NULL : transactionGetRlpHexEncoded (transaction, ewm->network, @@ -2153,11 +2521,11 @@ ewmTransferGetRawDataHexEncoded(BREthereumEWM ewm, ? RLP_TYPE_TRANSACTION_SIGNED : RLP_TYPE_TRANSACTION_UNSIGNED), prefix)); - } +} /// MARK: - Transfer -extern BREthereumAddress +extern BREthereumAddress* ewmTransferGetTarget (BREthereumEWM ewm, BREthereumTransfer transfer) { return transferGetTargetAddress(transfer); @@ -2172,13 +2540,19 @@ ewmTransferGetSource (BREthereumEWM ewm, extern BREthereumHash ewmTransferGetIdentifier(BREthereumEWM ewm, BREthereumTransfer transfer) { - return transferGetIdentifier (transfer); + pthread_mutex_lock (&ewm->lock); + BREthereumHash identifier = transferGetIdentifier (transfer); + pthread_mutex_unlock (&ewm->lock); + return identifier; } extern BREthereumHash ewmTransferGetOriginatingTransactionHash(BREthereumEWM ewm, BREthereumTransfer transfer) { - return transferGetOriginatingTransactionHash(transfer); + pthread_mutex_lock (&ewm->lock); + BREthereumHash hash = transferGetOriginatingTransactionHash(transfer); + pthread_mutex_unlock (&ewm->lock); + return hash; } extern char * @@ -2220,28 +2594,39 @@ ewmTransferGetGasLimit(BREthereumEWM ewm, return feeBasisGetGasLimit(transferGetFeeBasis(transfer)); } +extern BREthereumFeeBasis +ewmTransferGetFeeBasis (BREthereumEWM ewm, + BREthereumTransfer transfer) { + return transferGetFeeBasis (transfer); +} + extern uint64_t ewmTransferGetNonce(BREthereumEWM ewm, BREthereumTransfer transfer) { - return transferGetNonce(transfer); + pthread_mutex_lock (&ewm->lock); + uint64_t nonce = transferGetNonce(transfer); + pthread_mutex_unlock (&ewm->lock); + return nonce; } -extern BREthereumGas -ewmTransferGetGasUsed(BREthereumEWM ewm, - BREthereumTransfer transfer) { - BREthereumGas gasUsed; - return (transferExtractStatusIncluded(transfer, NULL, NULL, NULL, NULL, &gasUsed) - ? gasUsed - : gasCreate(0)); -} +extern BREthereumBoolean +ewmTransferExtractStatusIncluded (BREthereumEWM ewm, + BREthereumTransfer transfer, + BREthereumHash *blockHash, + uint64_t *blockNumber, + uint64_t *blockTransactionIndex, + uint64_t *blockTimestamp, + BREthereumGas *gasUsed) { + pthread_mutex_lock (&ewm->lock); + int included = transferExtractStatusIncluded (transfer, + blockHash, + blockNumber, + blockTransactionIndex, + blockTimestamp, + gasUsed); + pthread_mutex_unlock (&ewm->lock); -extern uint64_t -ewmTransferGetTransactionIndex(BREthereumEWM ewm, - BREthereumTransfer transfer) { - uint64_t transactionIndex; - return (transferExtractStatusIncluded(transfer, NULL, NULL, &transactionIndex, NULL, NULL) - ? transactionIndex - : 0); + return AS_ETHEREUM_BOOLEAN (included); } extern BREthereumHash @@ -2262,6 +2647,16 @@ ewmTransferGetBlockNumber(BREthereumEWM ewm, : 0); } +extern uint64_t +ewmTransferGetTransactionIndex(BREthereumEWM ewm, + BREthereumTransfer transfer) { + uint64_t transactionIndex; + return (transferExtractStatusIncluded(transfer, NULL, NULL, &transactionIndex, NULL, NULL) + ? transactionIndex + : 0); +} + + extern uint64_t ewmTransferGetBlockTimestamp (BREthereumEWM ewm, BREthereumTransfer transfer) { @@ -2271,13 +2666,23 @@ ewmTransferGetBlockTimestamp (BREthereumEWM ewm, : TRANSACTION_STATUS_BLOCK_TIMESTAMP_UNKNOWN); } +extern BREthereumGas +ewmTransferGetGasUsed(BREthereumEWM ewm, + BREthereumTransfer transfer) { + BREthereumGas gasUsed; + return (transferExtractStatusIncluded(transfer, NULL, NULL, NULL, NULL, &gasUsed) + ? gasUsed + : gasCreate(0)); +} + extern uint64_t ewmTransferGetBlockConfirmations(BREthereumEWM ewm, BREthereumTransfer transfer) { uint64_t blockNumber = 0; - return (transferExtractStatusIncluded(transfer, NULL, &blockNumber, NULL, NULL, NULL) - ? (ewmGetBlockHeight(ewm) - blockNumber) - : 0); + if (!transferExtractStatusIncluded(transfer, NULL, &blockNumber, NULL, NULL, NULL)) + return 0; + + return (ewmGetBlockHeight(ewm) >= blockNumber) ? (ewmGetBlockHeight(ewm) - blockNumber) : 0; } extern BREthereumTransferStatus @@ -2295,31 +2700,35 @@ ewmTransferIsConfirmed(BREthereumEWM ewm, extern BREthereumBoolean ewmTransferIsSubmitted(BREthereumEWM ewm, BREthereumTransfer transfer) { - return AS_ETHEREUM_BOOLEAN(ETHEREUM_BOOLEAN_IS_TRUE(transferHasStatus(transfer, TRANSFER_STATUS_SUBMITTED)) || - ETHEREUM_BOOLEAN_IS_TRUE(transferHasStatusOrTwo(transfer, - TRANSFER_STATUS_INCLUDED, - TRANSFER_STATUS_ERRORED))); + return AS_ETHEREUM_BOOLEAN (ETHEREUM_BOOLEAN_IS_TRUE (transferHasStatus (transfer, TRANSFER_STATUS_SUBMITTED)) || + ETHEREUM_BOOLEAN_IS_TRUE (transferHasStatusOrTwo (transfer, + TRANSFER_STATUS_INCLUDED, + TRANSFER_STATUS_ERRORED))); } extern char * ewmTransferStatusGetError (BREthereumEWM ewm, BREthereumTransfer transfer) { - if (TRANSFER_STATUS_ERRORED == transferGetStatus(transfer)) { - char *reason; + char *reason = NULL; + + pthread_mutex_lock (&ewm->lock); + if (TRANSFER_STATUS_ERRORED == transferGetStatus(transfer)) transferExtractStatusError (transfer, &reason); - return reason; - } - else return NULL; + pthread_mutex_unlock (&ewm->lock); + + return reason; } extern int ewmTransferStatusGetErrorType (BREthereumEWM ewm, BREthereumTransfer transfer) { - BREthereumTransactionErrorType type; + BREthereumTransactionErrorType type = (BREthereumTransactionErrorType) -1; + + pthread_mutex_lock (&ewm->lock); + transferExtractStatusErrorType (transfer, &type); + pthread_mutex_unlock (&ewm->lock); - return (transferExtractStatusErrorType (transfer, &type) - ? type - : (int ) -1); + return type; } extern BREthereumBoolean @@ -2344,7 +2753,12 @@ ewmTransferGetFee(BREthereumEWM ewm, BREthereumTransfer transfer, int *overflow) { assert (NULL != transfer); - return transferGetFee(transfer, overflow); + + pthread_mutex_lock (&ewm->lock); + BREthereumEther fee = transferGetFee(transfer, overflow); + pthread_mutex_unlock (&ewm->lock); + + return fee; } /// MARK: - Amount @@ -2400,34 +2814,79 @@ ewmCreateGas (uint64_t value) { return gasCreate(value); } - - - - - extern void ewmTransferDelete (BREthereumEWM ewm, BREthereumTransfer transfer) { if (NULL == transfer) return; // Remove from any (and all - should be but one) wallet + pthread_mutex_lock (&ewm->lock); for (int wid = 0; wid < array_count(ewm->wallets); wid++) { BREthereumWallet wallet = ewm->wallets[wid]; if (walletHasTransfer(wallet, transfer)) { walletUnhandleTransfer(wallet, transfer); - ewmSignalTransferEvent(ewm, wallet, transfer, TRANSFER_EVENT_DELETED, SUCCESS, NULL); + ewmSignalTransferEvent(ewm, wallet, transfer, (BREthereumTransferEvent) { + TRANSFER_EVENT_DELETED, + SUCCESS + }); } } // Null the ewm's `tid` - MUST NOT array_rm() as all `tid` holders will be dead. transferRelease(transfer); + pthread_mutex_unlock (&ewm->lock); } -extern BREthereumFeeBasis -feeBasisCreate (BREthereumGas limit, - BREthereumGasPrice price) { - return (BREthereumFeeBasis) { - FEE_BASIS_GAS, - { .gas = { limit, price }} - }; +extern BREthereumToken +ewmLookupToken (BREthereumEWM ewm, + BREthereumAddress address) { + pthread_mutex_lock (&ewm->lock); + BREthereumToken token = (BREthereumToken) BRSetGet (ewm->tokens, &address); + pthread_mutex_unlock (&ewm->lock); + return token; } +extern BREthereumToken +ewmCreateToken (BREthereumEWM ewm, + const char *address, + const char *symbol, + const char *name, + const char *description, + int decimals, + BREthereumGas defaultGasLimit, + BREthereumGasPrice defaultGasPrice) { + if (NULL == address || 0 == strlen(address)) return NULL; + if (ETHEREUM_BOOLEAN_FALSE == addressValidateString(address)) return NULL; + + // This function is called in potentially two threads. One in EWM event handler (on + // `ewmHandleAnnounceToken()`) and one in `cryptoWalletManagerInstall...()` (on some App + // listener thread). Such a description, used here, is troubling in and of itself. + + BREthereumAddress addr = addressCreate(address); + + // Lock over BRSetGet(), BRSetAdd() and tokenUpdate() + pthread_mutex_lock (&ewm->lock); + BREthereumToken token = (BREthereumToken) BRSetGet (ewm->tokens, &addr); + if (NULL == token) { + token = tokenCreate (address, + symbol, + name, + description, + decimals, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (ewm->tokens, token); + } + else { + tokenUpdate (token, + symbol, + name, + description, + decimals, + defaultGasLimit, + defaultGasPrice); + } + pthread_mutex_unlock (&ewm->lock); + + fileServiceSave (ewm->fs, ewmFileServiceTypeTokens, token); + return token; +} diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.h index c283ae0da..e63e40147 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWM.h @@ -1,9 +1,9 @@ // // BREthereumEWM -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/5/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -27,29 +27,53 @@ extern BREthereumEWM ewmCreate (BREthereumNetwork network, BREthereumAccount account, BREthereumTimestamp accountTimestamp, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumClient client, - const char *storagePath); + const char *storagePath, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); extern BREthereumEWM ewmCreateWithPaperKey (BREthereumNetwork network, const char *paperKey, BREthereumTimestamp accountTimestamp, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumClient client, - const char *storagePath); + const char *storagePath, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); extern BREthereumEWM ewmCreateWithPublicKey (BREthereumNetwork network, BRKey publicKey, BREthereumTimestamp accountTimestamp, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumClient client, - const char *storagePath); + const char *storagePath, + uint64_t blockHeight, + uint64_t confirmationsUntilFinal); extern void ewmDestroy (BREthereumEWM ewm); +/// MARK: Start Stop + +/** + * Starts the EWM event queue. Must be called after ewmCreate() and ewmStop() + * + * @param ewm + */ +extern void +ewmStart (BREthereumEWM ewm); + +/** + * Stops the EWM event queue (but does not purge existing, queued events). + * + * @param ewm + */ +extern void +ewmStop (BREthereumEWM ewm); + extern BREthereumNetwork ewmGetNetwork (BREthereumEWM ewm); @@ -89,27 +113,34 @@ extern BREthereumBoolean ewmIsConnected (BREthereumEWM ewm); extern BREthereumBoolean -ewmSync (BREthereumEWM ewm); +ewmSync (BREthereumEWM ewm, + BREthereumBoolean pendExistingTransfers); -extern void -ewmLock (BREthereumEWM ewm); +extern BREthereumBoolean +ewmSyncToDepth (BREthereumEWM ewm, + BREthereumBoolean pendExistingTransfers, + BRCryptoSyncDepth depth); + +extern BRCryptoSyncMode +ewmGetMode (BREthereumEWM ewm); extern void -ewmUnlock (BREthereumEWM ewm); +ewmUpdateMode (BREthereumEWM ewm, + BRCryptoSyncMode mode); extern uint64_t ewmGetBlockHeight (BREthereumEWM ewm); extern void -ewmUpdateBlockHeight(BREthereumEWM ewm, - uint64_t blockHeight); +ewmWipe (BREthereumNetwork network, + const char *storagePath); /// MARK: - Wallets extern BREthereumWallet * ewmGetWallets (BREthereumEWM ewm); -extern unsigned int +extern size_t ewmGetWalletsCount (BREthereumEWM ewm); extern BREthereumWallet @@ -121,6 +152,15 @@ ewmGetWalletHoldingToken(BREthereumEWM ewm, /// MARK: - Wallet +extern BREthereumAddress +ewmWalletGetAddress (BREthereumEWM ewm, + BREthereumWallet wallet); + +extern BREthereumBoolean +ewmWalletHasAddress (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumAddress address); + extern BREthereumToken ewmWalletGetToken (BREthereumEWM ewm, BREthereumWallet wallet); @@ -129,7 +169,6 @@ extern BREthereumAmount ewmWalletGetBalance(BREthereumEWM ewm, BREthereumWallet wallet); - extern BREthereumGas ewmWalletGetGasEstimate(BREthereumEWM ewm, BREthereumWallet wallet, @@ -194,10 +233,28 @@ ewmWalletCreateTransferWithFeeBasis (BREthereumEWM ewm, BREthereumAmount amount, BREthereumFeeBasis feeBasis); extern BREthereumEther -ewmWalletEstimateTransferFee(BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumAmount amount, - int *overflow); +ewmWalletEstimateTransferFee (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumAmount amount, + int *overflow); + +extern BREthereumEther +ewmWalletEstimateTransferFeeForBasis (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumAmount amount, + BREthereumGasPrice price, + BREthereumGas gas, + int *overflow); + +extern void +ewmWalletEstimateTransferFeeForTransfer (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumAddress source, + BREthereumAddress *target, + BREthereumAmount amount, + BREthereumGasPrice gasPrice, + BREthereumGas gasLimit); extern void // status, error ewmWalletSignTransfer(BREthereumEWM ewm, @@ -246,7 +303,7 @@ extern void ewmTransferDelete (BREthereumEWM ewm, BREthereumTransfer transfer); -extern BREthereumAddress +extern BREthereumAddress* ewmTransferGetTarget (BREthereumEWM ewm, BREthereumTransfer transfer); @@ -277,18 +334,22 @@ extern BREthereumGas ewmTransferGetGasLimit(BREthereumEWM ewm, BREthereumTransfer transfer); +extern BREthereumFeeBasis +ewmTransferGetFeeBasis (BREthereumEWM ewm, + BREthereumTransfer transfer); + extern uint64_t ewmTransferGetNonce(BREthereumEWM ewm, BREthereumTransfer transfer); - -extern BREthereumGas -ewmTransferGetGasUsed(BREthereumEWM ewm, - BREthereumTransfer transfer); - -extern uint64_t -ewmTransferGetTransactionIndex(BREthereumEWM ewm, - BREthereumTransfer transfer); +extern BREthereumBoolean +ewmTransferExtractStatusIncluded (BREthereumEWM ewm, + BREthereumTransfer transfer, + BREthereumHash *blockHash, + uint64_t *blockNumber, + uint64_t *blockTransactionIndex, + uint64_t *blockTimestamp, + BREthereumGas *gasUsed); extern BREthereumHash ewmTransferGetBlockHash(BREthereumEWM ewm, @@ -298,10 +359,18 @@ extern uint64_t ewmTransferGetBlockNumber(BREthereumEWM ewm, BREthereumTransfer transfer); +extern uint64_t +ewmTransferGetTransactionIndex(BREthereumEWM ewm, + BREthereumTransfer transfer); + extern uint64_t ewmTransferGetBlockTimestamp (BREthereumEWM ewm, BREthereumTransfer transfer); +extern BREthereumGas +ewmTransferGetGasUsed(BREthereumEWM ewm, + BREthereumTransfer transfer); + extern uint64_t ewmTransferGetBlockConfirmations(BREthereumEWM ewm, BREthereumTransfer transfer); @@ -389,6 +458,22 @@ ethereumClientAnnounceBlockNumber (BREthereumEWM ewm, const char *strBlockNumber, int rid); +/// MARK: Token + +extern BREthereumToken +ewmLookupToken (BREthereumEWM ewm, + BREthereumAddress address); + +extern BREthereumToken +ewmCreateToken (BREthereumEWM ewm, + const char *address, + const char *symbol, + const char *name, + const char *description, + int decimals, + BREthereumGas defaultGasLimit, + BREthereumGasPrice defaultGasPrice); + /// //extern void // status, error //ewmWalletSubmitTransferCancel(BREthereumEWM ewm, @@ -408,10 +493,6 @@ ethereumClientAnnounceBlockNumber (BREthereumEWM ewm, extern uint64_t ewmGetBlockHeight(BREthereumEWM ewm); -extern void -ewmUpdateBlockHeight(BREthereumEWM ewm, - uint64_t blockHeight); - extern const char * ewmTransferGetRawDataHexEncoded(BREthereumEWM ewm, BREthereumWallet wallet, diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMClient.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMClient.c index d144dd316..37d533dfc 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMClient.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMClient.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -12,9 +12,40 @@ #include #include #include +#include #include "support/BRArray.h" #include "BREthereumEWMPrivate.h" +extern BREthereumWalletEvent +walletEventCreateError (BREthereumWalletEventType type, + BREthereumStatus status, + const char *errorDescription) { + BREthereumWalletEvent event = { type, status, {}, { '\0' } }; + if (NULL != errorDescription) + strlcpy (event.errorDescription, errorDescription, sizeof (event.errorDescription)); + return event; +} + +extern BREthereumTransferEvent +transferEventCreateError (BREthereumTransferEventType type, + BREthereumStatus status, + const char *errorDescription) { + BREthereumTransferEvent event = { type, status, { '\0' } }; + if (NULL != errorDescription) + strlcpy (event.errorDescription, errorDescription, sizeof (event.errorDescription)); + return event; +} + +extern BREthereumEWMEvent +ewmEventCreateError (BREthereumEWMEventType type, + BREthereumStatus status, + const char *errorDescription) { + BREthereumEWMEvent event = { type, status, {}, { '\0' } }; + if (NULL != errorDescription) + strlcpy (event.errorDescription, errorDescription, sizeof (event.errorDescription)); + return event; +} + // ============================================================================================== // // Wallet Balance @@ -30,22 +61,21 @@ */ extern void ewmHandleAnnounceBalance (BREthereumEWM ewm, - BREthereumWallet wallet, - UInt256 value, - int rid) { - BREthereumAmount amount = - (AMOUNT_ETHER == walletGetAmountType(wallet) - ? amountCreateEther(etherCreate(value)) - : amountCreateToken(createTokenQuantity(walletGetToken(wallet), value))); + BREthereumWallet wallet, + UInt256 value, + int rid) { + BREthereumAmount amount = (AMOUNT_ETHER == walletGetAmountType(wallet) + ? amountCreateEther(etherCreate(value)) + : amountCreateToken(createTokenQuantity(walletGetToken(wallet), value))); ewmSignalBalance(ewm, amount); } extern BREthereumStatus ewmAnnounceWalletBalance (BREthereumEWM ewm, - BREthereumWallet wallet, - const char *balance, - int rid) { + BREthereumWallet wallet, + const char *balance, + int rid) { if (NULL == wallet) { return ERROR_UNKNOWN_WALLET; } // Passed in `balance` can be base 10 or 16. Let UInt256Prase decide. @@ -57,36 +87,65 @@ ewmAnnounceWalletBalance (BREthereumEWM ewm, return SUCCESS; } +extern void +ewmHandleUpdateWalletBalances (BREthereumEWM ewm) { + int typeMismatch = 0; + + size_t walletCount = array_count (ewm->wallets); + for (size_t index = 0; index < walletCount; index++) { + BREthereumWallet wallet = ewm->wallets[index]; + + BREthereumAmount oldBalance = walletGetBalance (wallet); + walletUpdateBalance (wallet); + BREthereumAmount newBalance = walletGetBalance (wallet); + + BREthereumComparison comparison = amountCompare (oldBalance, newBalance, &typeMismatch); + assert (0 == typeMismatch); + + if (ETHEREUM_COMPARISON_EQ != comparison) + ewmSignalWalletEvent (ewm, wallet, + (BREthereumWalletEvent) { + WALLET_EVENT_BALANCE_UPDATED, + SUCCESS + }); + } +} + // ============================================================================================== // // Default Wallet Gas Price // extern void ewmUpdateGasPrice (BREthereumEWM ewm, - BREthereumWallet wallet) { + BREthereumWallet wallet) { if (NULL == wallet) { - ewmSignalWalletEvent(ewm, wallet, WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, - ERROR_UNKNOWN_WALLET, - NULL); - + ewmSignalWalletEvent(ewm, wallet, + walletEventCreateError (WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, + ERROR_UNKNOWN_WALLET, + NULL)); + } else if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) { - ewmSignalWalletEvent(ewm, wallet, WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, - ERROR_NODE_NOT_CONNECTED, - NULL); + ewmSignalWalletEvent(ewm, wallet, + walletEventCreateError (WALLET_EVENT_DEFAULT_GAS_PRICE_UPDATED, + ERROR_NODE_NOT_CONNECTED, + NULL)); + } else { switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { + pthread_mutex_lock (&ewm->lock); ewm->client.funcGetGasPrice (ewm->client.context, ewm, wallet, ++ewm->requestId); + pthread_mutex_unlock (&ewm->lock); break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: LES Update Wallet Balance break; } @@ -95,17 +154,17 @@ ewmUpdateGasPrice (BREthereumEWM ewm, extern void ewmHandleAnnounceGasPrice (BREthereumEWM ewm, - BREthereumWallet wallet, - UInt256 amount, - int rid) { + BREthereumWallet wallet, + UInt256 amount, + int rid) { ewmSignalGasPrice(ewm, wallet, gasPriceCreate(etherCreate(amount))); } extern BREthereumStatus ewmAnnounceGasPrice(BREthereumEWM ewm, - BREthereumWallet wallet, - const char *gasPrice, - int rid) { + BREthereumWallet wallet, + const char *gasPrice, + int rid) { if (NULL == wallet) { return ERROR_UNKNOWN_WALLET; } BRCoreParseStatus parseStatus; @@ -122,87 +181,127 @@ ewmAnnounceGasPrice(BREthereumEWM ewm, // extern void -ewmUpdateGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer) { +ewmGetGasEstimate (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumTransfer transfer, + BREthereumCookie cookie) { if (NULL == transfer) { - ewmSignalTransferEvent(ewm, wallet, transfer, - TRANSFER_EVENT_GAS_ESTIMATE_UPDATED, - ERROR_UNKNOWN_WALLET, - NULL); - + ewmSignalGasEstimateFailure(ewm, wallet, cookie, ERROR_UNKNOWN_TRANSACTION); + } else if (ETHEREUM_BOOLEAN_IS_FALSE(ewmIsConnected(ewm))) { - ewmSignalTransferEvent(ewm, wallet, transfer, - TRANSFER_EVENT_GAS_ESTIMATE_UPDATED, - ERROR_NODE_NOT_CONNECTED, - NULL); + ewmSignalGasEstimateFailure(ewm, wallet, cookie, ERROR_NODE_NOT_CONNECTED); + } else { switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { + pthread_mutex_lock (&ewm->lock); + // This will be ZERO if transaction amount is in TOKEN. BREthereumEther amountInEther = transferGetEffectiveAmountInEther(transfer); - BREthereumAddress fromAddress = transferGetSourceAddress(transfer); - + BREthereumFeeBasis feeBasis = transferGetFeeBasis (transfer); + BREthereumGasPrice gasPrice = feeBasisGetGasPrice (feeBasis); BREthereumTransaction transaction = transferGetOriginatingTransaction(transfer); - char *from = addressGetEncodedString(fromAddress, 1); - char *to = (char *) addressGetEncodedString(transactionGetTargetAddress(transaction), 0); - char *amount = coerceStringPrefaced(amountInEther.valueInWEI, 16, "0x"); + BREthereumAddress sourceAddress = transferGetEffectiveSourceAddress(transfer); + char *from = addressGetEncodedString (&sourceAddress, 0); + char *to = addressGetEncodedString (transferGetEffectiveTargetAddress(transfer), 0); + char *amount = coerceStringPrefaced (amountInEther.valueInWEI, 16, "0x"); + char *price = coerceStringPrefaced (gasPrice.etherPerGas.valueInWEI, 16, "0x"); + char *data = (char *) transactionGetData(transaction); ewm->client.funcEstimateGas (ewm->client.context, ewm, wallet, - transfer, + cookie, from, to, amount, - transactionGetData(transaction), + price, + data, ++ewm->requestId); + pthread_mutex_unlock (&ewm->lock); free (from); - free(to); - free(amount); + free (to); + free (amount); + free (price); break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: LES Update Wallet Balance + assert (0); break; } } } -extern void -ewmHandleAnnounceGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - UInt256 value, - int rid) { - ewmSignalGasEstimate(ewm, wallet, transfer, gasCreate(value.u64[0])); -} - extern BREthereumStatus -ewmAnnounceGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - const char *gasEstimate, - int rid) { - if (NULL == wallet) { return ERROR_UNKNOWN_WALLET; } - if (NULL == transfer) { return ERROR_UNKNOWN_TRANSACTION; } +ewmAnnounceGasEstimateSuccess (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + const char *gasEstimate, + const char *gasPrice, + int rid) { + BRCoreParseStatus estimateStatus = CORE_PARSE_OK; + BRCoreParseStatus priceStatus = CORE_PARSE_OK; + UInt256 estimate = createUInt256Parse(gasEstimate, 0, &estimateStatus); + UInt256 price = createUInt256Parse(gasPrice, 0, &priceStatus); - BRCoreParseStatus parseStatus; - UInt256 gas = createUInt256Parse(gasEstimate, 0, &parseStatus); + if (CORE_PARSE_OK != estimateStatus || 0 != estimate.u64[1] || 0 != estimate.u64[2] || 0 != estimate.u64[3] || + CORE_PARSE_OK != priceStatus || 0 != price.u64[1] || 0 != price.u64[2] || 0 != price.u64[3]) { + ewmSignalGasEstimateFailure(ewm, wallet, cookie, ERROR_NUMERIC_PARSE); - if (CORE_PARSE_OK != parseStatus || - 0 != gas.u64[1] || 0 != gas.u64[2] || 0 != gas.u64[3]) { return ERROR_NUMERIC_PARSE; } + } else { + ewmSignalGasEstimateSuccess(ewm, wallet, cookie, gasCreate(estimate.u64[0]), gasPriceCreate(etherCreate(price))); + } + return SUCCESS; +} - ewmSignalAnnounceGasEstimate(ewm, wallet, transfer, gas, rid); +extern BREthereumStatus +ewmAnnounceGasEstimateFailure (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status, + int rid) { + // TODO(fix): Expose error reasons? + ewmSignalGasEstimateFailure(ewm, wallet, cookie, status); return SUCCESS; } +extern void +ewmHandlGasEstimateSuccess (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumGas gasEstimate, + BREthereumGasPrice gasPrice) { + ewmSignalWalletEvent (ewm, + wallet, + (BREthereumWalletEvent) { + WALLET_EVENT_FEE_ESTIMATED, + SUCCESS, + {.feeEstimate = {cookie, gasEstimate, gasPrice }} + }); +} + +extern void +ewmHandlGasEstimateFailure (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status) { + assert (SUCCESS != status); + ewmSignalWalletEvent (ewm, + wallet, + (BREthereumWalletEvent) { + WALLET_EVENT_FEE_ESTIMATED, + status, + {.feeEstimate = {cookie}} + }); +} + // ============================================================================================== // // Get Block Number @@ -217,8 +316,8 @@ ewmAnnounceGasEstimate (BREthereumEWM ewm, */ extern void ewmHandleAnnounceBlockNumber (BREthereumEWM ewm, - uint64_t blockNumber, - int rid) { + uint64_t blockNumber, + int rid) { ewmUpdateBlockHeight(ewm, blockNumber); } @@ -257,12 +356,19 @@ ewmAnnounceNonce (BREthereumEWM ewm, */ extern void ewmHandleAnnounceNonce (BREthereumEWM ewm, - BREthereumAddress address, - uint64_t nonce, - int rid) { - // BREthereumEncodedAddress address = accountGetPrimaryAddress (ewmGetAccount(ewm)); - // assert (ETHEREUM_BOOLEAN_IS_TRUE (addressHasString(address, strAddress))); - accountSetAddressNonce(ewm->account, accountGetPrimaryAddress(ewm->account), nonce, ETHEREUM_BOOLEAN_FALSE); + BREthereumAddress address, + uint64_t newNonce, + int rid) { + pthread_mutex_lock (&ewm->lock); + uint64_t oldNonce = accountGetAddressNonce (ewm->account, address); + if (oldNonce != newNonce) { + // This may not change the nonce + accountSetAddressNonce (ewm->account, address, newNonce, ETHEREUM_BOOLEAN_FALSE); + // Only save the primaryWallet if the nonce has, in fact, changed. + if (oldNonce != accountGetAddressNonce (ewm->account, address)) + ewmHandleSaveWallet (ewm, ewmGetWallet(ewm), CLIENT_CHANGE_UPD); + } + pthread_mutex_unlock (&ewm->lock); } // ============================================================================================== @@ -272,12 +378,12 @@ ewmHandleAnnounceNonce (BREthereumEWM ewm, extern void -ewmHandleAnnounceTransaction(BREthereumEWM ewm, - BREthereumEWMClientAnnounceTransactionBundle *bundle, - int id) { +ewmHandleAnnounceTransaction (BREthereumEWM ewm, + BREthereumEWMClientAnnounceTransactionBundle *bundle, + int id) { switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { // // This 'announce' call is coming from the guaranteed BRD endpoint; thus we don't need to // worry about the validity of the transaction - it is surely confirmed. Is that true @@ -313,8 +419,8 @@ ewmHandleAnnounceTransaction(BREthereumEWM ewm, break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: bcsSendTransactionRequest(ewm->bcs, bundle->hash, bundle->blockNumber, @@ -349,7 +455,12 @@ ewmAnnounceTransaction(BREthereumEWM ewm, bundle->hash = hashCreate(hashString); bundle->from = addressCreate(from); - bundle->to = addressCreate(to); + if (NULL == to) { + bundle->to = NULL; + } else { + bundle->to = calloc(1, sizeof(BREthereumAddress)); + *bundle->to = addressCreate(to); + } bundle->contract = (NULL == contract || '\0' == contract[0] ? EMPTY_ADDRESS_INIT : addressCreate(contract)); @@ -391,8 +502,8 @@ ewmHandleAnnounceLog (BREthereumEWM ewm, BREthereumEWMClientAnnounceLogBundle *bundle, int id) { switch (ewm->mode) { - case BRD_ONLY: - case BRD_WITH_P2P_SEND: { + case CRYPTO_SYNC_MODE_API_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: { // This 'announce' call is coming from the guaranteed BRD endpoint; thus we don't need to // worry about the validity of the transaction - it is surely confirmed. @@ -426,12 +537,19 @@ ewmHandleAnnounceLog (BREthereumEWM ewm, bundle->blockNumber, bundle->blockTransactionIndex, bundle->blockTimestamp, - gasCreate(0)); + gasCreate(bundle->gasUsed)); logSetStatus(log, status); // If we had a `bcs` we might think about `bcsSignalLog(ewm->bcs, log);` ewmSignalLog(ewm, BCS_CALLBACK_LOG_UPDATED, log); + // The `bundle` has `gasPrice` and `gasUsed` values. The above `ewmSignalLog()` is + // going to create a `transfer` and that transfer needs a correct `feeBasis`. We will + // not use this bundle's feeBasis and put in place something that works for P2P modes + // as well. So instead will use the feeBasis derived from this log transfer's + // transaction. See ewmHandleLog, ewmHandleTransaction and their calls to + // ewmHandleLogFeeBasis. + // Do we need a transaction here? No, if another originated this Log, then we can't ever // care and if we originated this Log, then we'll get the transaction (as part of the BRD // 'getTransactions' endpoint). @@ -440,8 +558,8 @@ ewmHandleAnnounceLog (BREthereumEWM ewm, break; } - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: bcsSendLogRequest(ewm->bcs, bundle->hash, bundle->blockNumber, @@ -494,25 +612,6 @@ ewmAnnounceLogComplete (BREthereumEWM ewm, ewmSignalAnnounceComplete (ewm, ETHEREUM_BOOLEAN_FALSE, success, id); } -// ============================================================================================== -// -// Announce {Transactions, Logs} Complete -// -extern void -ewmHandleAnnounceComplete (BREthereumEWM ewm, - BREthereumBoolean isTransaction, - BREthereumBoolean success, - int rid) { - if (ETHEREUM_BOOLEAN_IS_TRUE(isTransaction)) { - if (rid == ewm->brdSync.ridTransaction) - ewm->brdSync.completedTransaction = ETHEREUM_BOOLEAN_IS_TRUE(success); - } - else { - if (rid == ewm->brdSync.ridLog) - ewm->brdSync.completedLog = ETHEREUM_BOOLEAN_IS_TRUE(success); - } -} - // ============================================================================================== // // Blocks @@ -520,11 +619,11 @@ ewmHandleAnnounceComplete (BREthereumEWM ewm, extern BREthereumStatus ewmAnnounceBlocks (BREthereumEWM ewm, - int id, - // const char *strBlockHash, - int blockNumbersCount, - uint64_t *blockNumbers) { // BRArrayOf(const char *) strBlockNumbers ?? - assert (P2P_ONLY == ewm->mode || P2P_WITH_BRD_SYNC == ewm->mode); + int id, + // const char *strBlockHash, + int blockNumbersCount, + uint64_t *blockNumbers) { + assert (CRYPTO_SYNC_MODE_P2P_ONLY == ewm->mode || CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC == ewm->mode); // into bcs... BRArrayOf(uint64_t) numbers; @@ -535,7 +634,6 @@ ewmAnnounceBlocks (BREthereumEWM ewm, return SUCCESS; } - // ============================================================================================== // // Submit Transaction @@ -557,6 +655,7 @@ ewmWalletSubmitTransfer(BREthereumEWM ewm, // assert: wallet-has-transfer // assert: signed // assert: originatingTransaction + pthread_mutex_lock (&ewm->lock); BREthereumTransaction transaction = transferGetOriginatingTransaction(transfer); BREthereumBoolean isSigned = transactionIsSigned (transaction); @@ -567,7 +666,7 @@ ewmWalletSubmitTransfer(BREthereumEWM ewm, // update the transfer's status. switch (ewm->mode) { - case BRD_ONLY: { + case CRYPTO_SYNC_MODE_API_ONLY: { char *rawTransaction = transactionGetRlpHexEncoded (transaction, ewm->network, (ETHEREUM_BOOLEAN_IS_TRUE (isSigned) @@ -586,12 +685,13 @@ ewmWalletSubmitTransfer(BREthereumEWM ewm, break; } - case BRD_WITH_P2P_SEND: - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: bcsSendTransaction(ewm->bcs, transaction); break; } + pthread_mutex_unlock (&ewm->lock); } extern void @@ -607,7 +707,7 @@ ewmHandleAnnounceSubmitTransfer (BREthereumEWM ewm, BREthereumTransactionStatus status = transactionStatusCreate(TRANSACTION_STATUS_PENDING); switch (ewm->mode) { - case BRD_ONLY: + case CRYPTO_SYNC_MODE_API_ONLY: // NODE: For BRD_ONLY the BRD endpoint is a GETH node. Hence lesTransactionErrorPreface, if (NULL != errorMessage) { BREthereumTransactionErrorType type = lookupTransactionErrorType (lesTransactionErrorPreface, errorMessage); @@ -617,9 +717,9 @@ ewmHandleAnnounceSubmitTransfer (BREthereumEWM ewm, status = transactionStatusCreateErrored (TRANSACTION_ERROR_UNKNOWN, transactionGetErrorName(TRANSACTION_ERROR_UNKNOWN)); break; - case BRD_WITH_P2P_SEND: - case P2P_WITH_BRD_SYNC: - case P2P_ONLY: + case CRYPTO_SYNC_MODE_API_WITH_P2P_SEND: + case CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC: + case CRYPTO_SYNC_MODE_P2P_ONLY: // TODO: Is this anything besides PENDING? // Is this even called outside of BRD_ONLY? If so, why did BRD_ONLY have assert(0)? break; @@ -628,18 +728,20 @@ ewmHandleAnnounceSubmitTransfer (BREthereumEWM ewm, transactionSetStatus (transaction, status); // If we had a `bcs` we might think about `bcsSignalTransaction(ewm->bcs, transaction);` - ewmSignalTransaction (ewm, BCS_CALLBACK_TRANSACTION_ADDED, transaction); + // This `ewmSignalTransation` is `OwnershipGiven` on `transaction`. As our `transaction` + // is the originating transaction, we surely must copy (CORE-508) + ewmSignalTransaction (ewm, BCS_CALLBACK_TRANSACTION_ADDED, transactionCopy (transaction)); } extern BREthereumStatus -ewmAnnounceSubmitTransfer(BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - const char *strHash, - int errorCode, - const char *errorMessage, - int id) { +ewmAnnounceSubmitTransfer (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumTransfer transfer, + const char *strHash, + int errorCode, + const char *errorMessage, + int id) { if (NULL == wallet) { return ERROR_UNKNOWN_WALLET; } if (NULL == transfer) { return ERROR_UNKNOWN_TRANSACTION; } @@ -647,7 +749,7 @@ ewmAnnounceSubmitTransfer(BREthereumEWM ewm, BREthereumHash hash = hashCreate(strHash); // We announce a submitted transfer => there is an originating transaction. if (ETHEREUM_BOOLEAN_IS_TRUE (hashEqual (hash, EMPTY_HASH_INIT)) - || ETHEREUM_BOOLEAN_IS_FALSE (hashEqual (hash, transferGetOriginatingTransactionHash (transfer)))) + || ETHEREUM_BOOLEAN_IS_FALSE (hashEqual (hash, ewmTransferGetOriginatingTransactionHash (ewm, transfer)))) return ERROR_TRANSACTION_HASH_MISMATCH; } @@ -655,54 +757,84 @@ ewmAnnounceSubmitTransfer(BREthereumEWM ewm, return SUCCESS; } - // ============================================================================================== // // Update Tokens // extern void ewmUpdateTokens (BREthereumEWM ewm) { - ewm->client.funcGetTokens - (ewm->client.context, - ewm, - ++ewm->requestId); + unsigned int rid = ++ewm->requestId; + + ewm->client.funcGetTokens (ewm->client.context, ewm, rid); + +#if 0 + if (ethereumMainnet == ewm->network) + ewm->client.funcGetTokens + (ewm->client.context, + ewm, + rid); + + else if (ethereumTestnet == ewm->network) { + ewmAnnounceToken (ewm, rid, + "0x7108ca7c4718efa810457f228305c9c71390931a", + "BRD", + "BRD Token", + "BRD Token Description", + 18, + NULL, + NULL); + ewmAnnounceToken (ewm, rid, + "0x722dd3f80bac40c951b51bdd28dd19d435762180", + "TST", + "Test Standard Token", + "TeST Standard Token (TST) for TeSTing (TST)", + 18, + NULL, + NULL); + ewmAnnounceTokenComplete (ewm, rid, ETHEREUM_BOOLEAN_TRUE); + } + + else if (ethereumRinkeby == ewm->network) + ewmAnnounceTokenComplete (ewm, rid, ETHEREUM_BOOLEAN_TRUE); + + else + assert (0); +#endif } extern void ewmHandleAnnounceToken (BREthereumEWM ewm, - BREthereumEWMClientAnnounceTokenBundle *bundle, - int id) { - tokenInstall (bundle->address, - bundle->symbol, - bundle->name, - bundle->description, - bundle->decimals, - bundle->gasLimit, - bundle->gasPrice); - - BREthereumToken token = tokenLookup(bundle->address); + BREthereumEWMClientAnnounceTokenBundle *bundle, + int id) { + BREthereumToken token = ewmCreateToken (ewm, + bundle->address, + bundle->symbol, + bundle->name, + bundle->description, + bundle->decimals, + bundle->gasLimit, + bundle->gasPrice); assert (NULL != token); ewm->client.funcTokenEvent (ewm->client.context, ewm, token, - TOKEN_EVENT_CREATED); + (BREthereumTokenEvent) {TOKEN_EVENT_CREATED, SUCCESS }); ewmClientAnnounceTokenBundleRelease(bundle); } -#include extern void ewmAnnounceToken(BREthereumEWM ewm, + int rid, const char *address, const char *symbol, const char *name, const char *description, unsigned int decimals, const char *strDefaultGasLimit, - const char *strDefaultGasPrice, - int rid) { + const char *strDefaultGasPrice) { char *strEndPointer = NULL; // Parse strDefaultGasLimit - as a decimal, hex or even octal string. If the parse fails @@ -743,16 +875,16 @@ ewmAnnounceToken(BREthereumEWM ewm, extern void ewmHandleAnnounceTokenComplete (BREthereumEWM ewm, - BREthereumBoolean success, - int rid) { + int rid, + BREthereumBoolean success) { if (ETHEREUM_BOOLEAN_IS_TRUE (success)) - ewmSync (ewm); + ewmSync (ewm, ETHEREUM_BOOLEAN_FALSE); } extern void ewmAnnounceTokenComplete (BREthereumEWM ewm, - BREthereumBoolean success, - int rid) { + int rid, + BREthereumBoolean success) { ewmSignalAnnounceTokenComplete (ewm, success, rid); } @@ -760,52 +892,49 @@ ewmAnnounceTokenComplete (BREthereumEWM ewm, // // Client // +static int +ewmNeedWalletSave (BREthereumEWM ewm, + BREthereumWallet wid, + BREthereumWalletEvent event) { + return (WALLET_EVENT_BALANCE_UPDATED == event.type); // CREATED? +} extern void ewmHandleWalletEvent(BREthereumEWM ewm, - BREthereumWallet wid, - BREthereumWalletEvent event, - BREthereumStatus status, - const char *errorDescription) { - ewm->client.funcWalletEvent - (ewm->client.context, - ewm, - wid, - event, - status, - errorDescription); + BREthereumWallet wid, + BREthereumWalletEvent event) { + if (ewmNeedWalletSave (ewm, wid, event)) + ewmHandleSaveWallet (ewm, wid, CLIENT_CHANGE_UPD); + + ewm->client.funcWalletEvent (ewm->client.context, + ewm, + wid, + event); } #if defined (NEVER_DEFINED) extern void ewmHandleBlockEvent(BREthereumEWM ewm, BREthereumBlock block, - BREthereumBlockEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumBlockEvent event) { ewm->client.funcBlockEvent (ewm->client.context, ewm, block, - event, - status, - errorDescription); + event); } #endif static int ewmNeedTransferSave (BREthereumEWM ewm, BREthereumTransferEvent event) { - return (TRANSFER_EVENT_GAS_ESTIMATE_UPDATED != event && - TRANSFER_EVENT_BLOCK_CONFIRMATIONS_UPDATED != event); + return (TRANSFER_EVENT_GAS_ESTIMATE_UPDATED != event.type); } extern void ewmHandleTransferEvent (BREthereumEWM ewm, BREthereumWallet wallet, BREthereumTransfer transfer, - BREthereumTransferEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumTransferEvent event) { // If `transfer` represents a token transfer that we've created/submitted, then we won't have // the actual `log` until the corresponding originating transaction is included. We won't @@ -831,10 +960,10 @@ ewmHandleTransferEvent (BREthereumEWM ewm, // We know that only on SIGNED do we have a transaction hash. Only on // included do we have a log hash. Thus we might see CHANGE_UPD w/o a // CHANGE_ADD - and that is NOT a problem. - BREthereumClientChangeType type = ((event == TRANSFER_EVENT_CREATED || - event == TRANSFER_EVENT_SIGNED) + BREthereumClientChangeType type = ((event.type == TRANSFER_EVENT_CREATED || + event.type == TRANSFER_EVENT_SIGNED) ? CLIENT_CHANGE_ADD - : (event == TRANSFER_EVENT_DELETED + : (event.type == TRANSFER_EVENT_DELETED ? CLIENT_CHANGE_REM : CLIENT_CHANGE_UPD)); @@ -851,263 +980,21 @@ ewmHandleTransferEvent (BREthereumEWM ewm, ewm, wallet, transfer, - event, - status, - errorDescription); + event); } extern void ewmHandlePeerEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumPeerEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumPeerEvent event) { ewm->client.funcPeerEvent (ewm->client.context, ewm, - // event->wid, - // event->tid, - event, - status, - errorDescription); + event); } extern void ewmHandleEWMEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumEWMEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumEWMEvent event) { ewm->client.funcEWMEvent (ewm->client.context, ewm, - //event->wid, - // event->tid, - event, - status, - errorDescription); + event); } - - - -#if 0 // Transaction -BREthereumTransactionId tid = -1; -BREthereumAddress primaryAddress = accountGetPrimaryAddress(ewm->account); - -assert (ETHEREUM_BOOLEAN_IS_TRUE(addressEqual(primaryAddress, bundle->from)) - || ETHEREUM_BOOLEAN_IS_TRUE(addressEqual(primaryAddress, bundle->to))); - -// primaryAddress is either the transaction's `source` or `target`. -BREthereumBoolean isSource = addressEqual(primaryAddress, bundle->from); - -BREthereumWalletId wid = ewmGetWallet(ewm); -BREthereumWallet wallet = ewmLookupWallet(ewm, wid); - -BREthereumBlock block = ewmLookupBlockByHash(ewm, bundle->blockHash); -block = blockCreateMinimal(bundle->blockHash, bundle->blockNumber, bundle->blockTimestamp); -ewmSignalBlockEvent(ewm, ewmInsertBlock(ewm, block), - BLOCK_EVENT_CREATED, - SUCCESS, NULL); - -// Look for a pre-existing transaction -BREthereumTransaction transaction = walletGetTransactionByHash(wallet, bundle->hash); - -// If we did not have a transaction for 'hash' it might be (might likely be) a newly submitted -// transaction that we are holding but that doesn't have a hash yet. This will *only* apply -// if we are the source. -if (NULL == transaction && ETHEREUM_BOOLEAN_IS_TRUE(isSource)) -transaction = walletGetTransactionByNonce(wallet, primaryAddress, bundle->nonce); - -// If we still don't have a transaction (with 'hash' or 'nonce'); then create one. -if (NULL == transaction) { - BREthereumAddress sourceAddr = (ETHEREUM_BOOLEAN_IS_TRUE(isSource) ? primaryAddress : bundle->from); - BREthereumAddress targetAddr = (ETHEREUM_BOOLEAN_IS_TRUE(isSource) ? bundle->to : primaryAddress); - - // Get the amount; this will be '0' if this is a token transfer - BREthereumAmount amount = amountCreateEther(etherCreate(bundle->amount)); - - // Easily extract the gasPrice and gasLimit. - BREthereumGasPrice gasPrice = gasPriceCreate(etherCreate(bundle->gasPrice)); - - BREthereumGas gasLimit = gasCreate(bundle->gasLimit); - - // Finally, get ourselves a transaction. - transaction = transactionCreate(sourceAddr, - targetAddr, - amount, - gasPrice, - gasLimit, - bundle->nonce); - // With a new transaction: - // - // a) add to the ewm - tid = ewmInsertTransaction(ewm, transaction); - // - // b) add to the wallet - walletHandleTransaction(wallet, transaction); - // - // c) announce the wallet update - ewmSignalTransactionEvent(ewm, wid, tid, - TRANSACTION_EVENT_CREATED, - SUCCESS, NULL); - // - // d) announce as submitted (=> there is a hash, submitted by 'us' or 'them') - walletTransactionSubmitted(wallet, transaction, bundle->hash); - -} - -if (-1 == tid) -tid = ewmLookupTransactionId(ewm, transaction); - -BREthereumGas gasUsed = gasCreate(bundle->gasUsed); -// TODO: Process 'state' properly - errors? - -// Get the current status. -BREthereumTransactionStatus status = transactionGetStatus(transaction); - -// Update the status as blocked -if (TRANSACTION_STATUS_INCLUDED != status.type) -walletTransactionIncluded(wallet, transaction, gasUsed, - blockGetHash(block), - blockGetNumber(block), - bundle->blockTransactionIndex); - -// Announce a transaction event. If already 'BLOCKED', then update CONFIRMATIONS. -ewmSignalTransactionEvent(ewm, wid, tid, - (TRANSACTION_STATUS_INCLUDED == status.type - ? TRANSACTION_EVENT_BLOCK_CONFIRMATIONS_UPDATED - : TRANSACTION_EVENT_BLOCKED), - SUCCESS, - NULL); -#endif - - -#if 0 // Log -BREthereumTransactionId tid = -1; - -pthread_mutex_lock(&ewm->lock); - -// Token of interest -BREthereumToken token = tokenLookupByAddress(bundle->contract); -if (NULL == token) { pthread_mutex_unlock(&ewm->lock); return; } // uninteresting token - -// Event of interest -BREthereumContractEvent event = contractLookupEventForTopic (contractERC20, bundle->arrayTopics[0]); -if (NULL == event || event != eventERC20Transfer) { pthread_mutex_unlock(&ewm->lock); return; }; // uninteresting event - -BREthereumBlock block = NULL; -// BREthereumBlock block = ewmLookupBlockByHash(ewm, bundle->blockHash); -// block = blockCreateMinimal(bundle->blockHash, bundle->blockNumber, bundle->blockTimestamp); -// ewmSignalBlockEvent(ewm, ewmInsertBlock(ewm, block), -// BLOCK_EVENT_CREATED, -// SUCCESS, NULL); - -// Wallet for token -BREthereumWalletId wid = (NULL == token - ? ewmGetWallet(ewm) - : ewmGetWalletHoldingToken(ewm, token)); -BREthereumWallet wallet = ewmLookupWallet(ewm, wid); - -// Existing transaction -BREthereumTransaction transaction = walletGetTransactionByHash(wallet, bundle->hash); - -BREthereumGasPrice gasPrice = gasPriceCreate(etherCreate(bundle->gasPrice)); -BREthereumGas gasUsed = gasCreate(bundle->gasUsed); - - -// Create a token transaction -if (NULL == transaction) { - - // Parse the topic data - we fake it becasue we 'know' topics indices - BREthereumAddress sourceAddr = - addressCreate(eventERC20TransferDecodeAddress(event, bundle->arrayTopics[1])); - - BREthereumAddress targetAddr = - addressCreate(eventERC20TransferDecodeAddress(event, bundle->arrayTopics[2])); - - BRCoreParseStatus status = CORE_PARSE_OK; - - BREthereumAmount amount = - amountCreateToken(createTokenQuantity(token, eventERC20TransferDecodeUInt256(event, - bundle->data, - &status))); - - transaction = transactionCreate(sourceAddr, targetAddr, amount, gasPrice, gasUsed, 0); - - // With a new transaction: - // - // a) add to the ewm - tid = ewmInsertTransaction(ewm, transaction); - // - // b) add to the wallet - walletHandleTransaction(wallet, transaction); - // - // c) announce the wallet update - ewmSignalTransactionEvent(ewm, wid, tid, TRANSACTION_EVENT_CREATED, SUCCESS, NULL); - - // - // d) announce as submitted. - walletTransactionSubmitted(wallet, transaction, bundle->hash); - -} - -if (-1 == tid) -tid = ewmLookupTransactionId(ewm, transaction); - -// TODO: Process 'state' properly - errors? - -// Get the current status. -BREthereumTransactionStatus status = transactionGetStatus(transaction); - -// Update the status as blocked -if (TRANSACTION_STATUS_INCLUDED != status.type) -walletTransactionIncluded(wallet, transaction, gasUsed, - blockGetHash(block), - blockGetNumber(block), - bundle->blockTransactionIndex); - -// Announce a transaction event. If already 'BLOCKED', then update CONFIRMATIONS. -ewmSignalTransactionEvent(ewm, wid, tid, - (TRANSACTION_STATUS_INCLUDED == status.type - ? TRANSACTION_EVENT_BLOCK_CONFIRMATIONS_UPDATED - : TRANSACTION_EVENT_BLOCKED), - SUCCESS, - NULL); - -// Hmmmm... -pthread_mutex_unlock(&ewm->lock); -#endif - - -#if 0 // token -static int -ewmDataIsEmpty (BREthereumEWM ewm, const char *data) { - return NULL == data || 0 == strcmp ("", data) || 0 == strcmp ("0x", data); -} - -// If `data` is anything besides "0x", then we have a contract function call. At that point -// it seems we need to process `data` to extract the 'function + args' and then, if the -// function is 'transfer() token' we can then and only then conclude that we have a token - -if (ewmDataIsEmpty(ewm, data)) return NULL; - -// There is contract data; see if it is a ERC20 function. -BREthereumContractFunction function = contractLookupFunctionForEncoding(contractERC20, data); - -// Not an ERC20 token -if (NULL == function) return NULL; - -// See if we have an existing token. -BREthereumToken token = tokenLookup(target); -if (NULL == token) token = tokenLookup(contract); - -// We found a token... -if (NULL != token) return token; - -// ... we didn't find a token - we should create is dynamically. -fprintf (stderr, "Ignoring transaction for unknown ERC20 token at '%s'", target); -return NULL; -#endif - - - diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMEvent.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMEvent.c index 1029d634f..e7eb587c8 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMEvent.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMEvent.c @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -68,7 +68,7 @@ typedef struct { static void ewmHandleAccountStateEventDispatcher(BREventHandler ignore, - BREthereumHandleAccountStateEvent *event) { + BREthereumHandleAccountStateEvent *event) { ewmHandleAccountState(event->ewm, event->accountState); } @@ -80,7 +80,7 @@ BREventType handleAccountStateEventType = { extern void ewmSignalAccountState (BREthereumEWM ewm, - BREthereumAccountState accountState) { + BREthereumAccountState accountState) { BREthereumHandleAccountStateEvent event = { { NULL, &handleAccountStateEventType }, ewm, accountState }; eventHandlerSignalEvent(ewm->handler, (BREvent*) &event); } @@ -97,7 +97,7 @@ typedef struct { static void ewmHandleBalanceEventDispatcher(BREventHandler ignore, - BREthereumHandleBalanceEvent *event) { + BREthereumHandleBalanceEvent *event) { ewmHandleBalance(event->ewm, event->amount); } @@ -153,14 +153,20 @@ typedef struct { BREvent base; BREthereumEWM ewm; BREthereumWallet wallet; - BREthereumTransfer transfer; + BREthereumCookie cookie; + BREthereumStatus status; BREthereumGas gasEstimate; + BREthereumGasPrice gasPrice; } BREthereumHandleGasEstimateEvent; static void ewmHandleGasEstimateEventDispatcher(BREventHandler ignore, BREthereumHandleGasEstimateEvent *event) { - ewmHandleGasEstimate(event->ewm, event->wallet, event->transfer, event->gasEstimate); + if (SUCCESS == event->status) { + ewmHandlGasEstimateSuccess (event->ewm, event->wallet, event->cookie, event->gasEstimate, event->gasPrice); + } else { + ewmHandlGasEstimateFailure (event->ewm, event->wallet, event->cookie, event->status); + } } BREventType handleGasEstimateEventType = { @@ -170,11 +176,21 @@ BREventType handleGasEstimateEventType = { }; extern void -ewmSignalGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - BREthereumGas gasEstimate) { - BREthereumHandleGasEstimateEvent event = { { NULL, &handleGasEstimateEventType }, ewm, wallet, transfer, gasEstimate }; +ewmSignalGasEstimateSuccess(BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumGas gasEstimate, + BREthereumGasPrice gasPrice) { + BREthereumHandleGasEstimateEvent event = { { NULL, &handleGasEstimateEventType }, ewm, wallet, cookie, SUCCESS, gasEstimate, gasPrice }; + eventHandlerSignalEvent(ewm->handler, (BREvent*) &event); +} + +extern void +ewmSignalGasEstimateFailure(BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status) { + BREthereumHandleGasEstimateEvent event = { { NULL, &handleGasEstimateEventType }, ewm, wallet, cookie, status }; eventHandlerSignalEvent(ewm->handler, (BREvent*) &event); } @@ -264,7 +280,7 @@ typedef struct { static void ewmHandleSaveBlocksEventDispatcher(BREventHandler ignore, - BREthereumHandleSaveBlocksEvent *event) { + BREthereumHandleSaveBlocksEvent *event) { ewmHandleSaveBlocks(event->ewm, event->blocks); } @@ -300,14 +316,24 @@ typedef struct { static void ewmHandleSaveNodesEventDispatcher(BREventHandler ignore, - BREthereumHandleSaveNodesEvent *event) { + BREthereumHandleSaveNodesEvent *event) { ewmHandleSaveNodes(event->ewm, event->nodes); } +static void +ewmHandleSaveNodesEventDestroyer (BREthereumHandleSaveNodesEvent *event) { + if (NULL != event->nodes) { + for (size_t index = 0; index < array_count (event->nodes); index++) + nodeConfigRelease (event->nodes[index]); + array_free (event->nodes); + } +} + BREventType handleSaveNodesEventType = { "EWM: Handle SaveNodes Event", sizeof (BREthereumHandleSaveNodesEvent), - (BREventDispatcher) ewmHandleSaveNodesEventDispatcher + (BREventDispatcher) ewmHandleSaveNodesEventDispatcher, + (BREventDestroyer) ewmHandleSaveNodesEventDestroyer }; extern void @@ -333,7 +359,7 @@ typedef struct { static void ewmHandleSyncEventDispatcher(BREventHandler ignore, - BREthereumHandleSyncEvent *event) { + BREthereumHandleSyncEvent *event) { ewmHandleSync(event->ewm, event->type, event->blockNumberStart, event->blockNumberCurrent, @@ -360,6 +386,35 @@ ewmSignalSync (BREthereumEWM ewm, eventHandlerSignalEvent(ewm->handler, (BREvent*) &event); } +// ============================================================================================== +// +// Handle Sync API +// +typedef struct { + BREvent base; + BREthereumEWM ewm; +} BREthereumHandleSyncAPIEvent; + +static void +ewmHandleSyncAPIEventDispatcher(BREventHandler ignore, + BREthereumHandleSyncAPIEvent *event) { + ewmHandleSyncAPI (event->ewm); +} + +BREventType handleSyncAPIEventType = { + "EWM: Handle Sync API Event", + sizeof (BREthereumHandleSyncAPIEvent), + (BREventDispatcher) ewmHandleSyncAPIEventDispatcher +}; + +extern void +ewmSignalSyncAPI (BREthereumEWM ewm, + BREthereumBoolean OOB) { + BREthereumHandleSyncEvent event = { { NULL, &handleSyncAPIEventType }, ewm }; + if (ETHEREUM_BOOLEAN_IS_TRUE(OOB)) eventHandlerSignalEventOOB (ewm->handler, (BREvent*) &event); + else eventHandlerSignalEvent (ewm->handler, (BREvent*) &event); +} + // ============================================================================================== // // Handle GetBlocks @@ -405,37 +460,31 @@ typedef struct { BREthereumEWM ewm; BREthereumWallet wid; BREthereumWalletEvent event; - BREthereumStatus status; - const char *errorDescription; } BREthereumEWMClientWalletEvent; -#define CLIENT_WALLET_EVENT_INITIALIZER(ewm, wid, vent, status, desc) \ -{ { NULL, &ewmClientWalletEventType }, (ewm), (wid), (event), (status), (desc) } +#define CLIENT_WALLET_EVENT_INITIALIZER(ewm, wid, event) \ +{ { NULL, &ewmClientWalletEventType }, (ewm), (wid), (event) } static void ewmClientWalletEventDispatcher(BREventHandler ignore, BREthereumEWMClientWalletEvent *event) { ewmHandleWalletEvent(event->ewm, - event->wid, - event->event, - event->status, - event->errorDescription); + event->wid, + event->event); } static BREventType ewmClientWalletEventType = { - "EMW: Client Wallet Event", + "EWM: Client Wallet Event", sizeof (BREthereumEWMClientWalletEvent), (BREventDispatcher) ewmClientWalletEventDispatcher }; extern void ewmSignalWalletEvent(BREthereumEWM ewm, - BREthereumWallet wid, - BREthereumWalletEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumWallet wid, + BREthereumWalletEvent event) { BREthereumEWMClientWalletEvent message = - CLIENT_WALLET_EVENT_INITIALIZER (ewm, wid, event, status, errorDescription); + CLIENT_WALLET_EVENT_INITIALIZER (ewm, wid, event); eventHandlerSignalEvent(ewm->handler, (BREvent*) &message); } @@ -448,37 +497,31 @@ typedef struct { BREthereumEWM ewm; BREthereumBlock bid; BREthereumBlockEvent event; - BREthereumStatus status; - const char *errorDescription; } BREthereumEWMClientBlockEvent; -#define CLIENT_BLOCK_EVENT_INITIALIZER(ewm, bid, event, status, desc) \ -{ { NULL, &ewmClientBlockEventType }, (ewm), (bid), (event), (status), (desc) } +#define CLIENT_BLOCK_EVENT_INITIALIZER(ewm, bid, event) \ +{ { NULL, &ewmClientBlockEventType }, (ewm), (bid), (event) } static void ewmClientBlockEventDispatcher(BREventHandler ignore, BREthereumEWMClientBlockEvent *event) { ewmHandleBlockEvent(event->ewm, - event->bid, - event->event, - event->status, - event->errorDescription); + event->bid, + event->event); } static BREventType ewmClientBlockEventType = { - "EMW: Client Block Event", + "EWM: Client Block Event", sizeof (BREthereumEWMClientBlockEvent), (BREventDispatcher) ewmClientBlockEventDispatcher }; extern void ewmSignalBlockEvent(BREthereumEWM ewm, - BREthereumBlock bid, - BREthereumBlockEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumBlock bid, + BREthereumBlockEvent event) { BREthereumEWMClientBlockEvent message = - CLIENT_BLOCK_EVENT_INITIALIZER (ewm, bid, event, status, errorDescription); + CLIENT_BLOCK_EVENT_INITIALIZER (ewm, bid, event); eventHandlerSignalEvent(ewm->handlerForMain, (BREvent*) &message); } #endif @@ -492,39 +535,33 @@ typedef struct { BREthereumWallet wid; BREthereumTransfer tid; BREthereumTransferEvent event; - BREthereumStatus status; - const char *errorDescription; } BREthereumEWMClientTransactionEvent; -#define CLIENT_TRANSACTION_EVENT_INITIALIZER(ewm, wid, tid, event, status, desc) \ -{ { NULL, &ewmClientTransactionEventType }, (ewm), (wid), (tid), (event), (status), (desc) } +#define CLIENT_TRANSACTION_EVENT_INITIALIZER(ewm, wid, tid, event) \ +{ { NULL, &ewmClientTransactionEventType }, (ewm), (wid), (tid), (event) } static void ewmClientTransactionEventDispatcher(BREventHandler ignore, BREthereumEWMClientTransactionEvent *event) { ewmHandleTransferEvent(event->ewm, - event->wid, - event->tid, - event->event, - event->status, - event->errorDescription); + event->wid, + event->tid, + event->event); } static BREventType ewmClientTransactionEventType = { - "EMW: Client Transaction Event", + "EWM: Client Transaction Event", sizeof (BREthereumEWMClientTransactionEvent), (BREventDispatcher) ewmClientTransactionEventDispatcher }; extern void ewmSignalTransferEvent(BREthereumEWM ewm, - BREthereumWallet wid, - BREthereumTransfer tid, - BREthereumTransferEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumWallet wid, + BREthereumTransfer tid, + BREthereumTransferEvent event) { BREthereumEWMClientTransactionEvent message = - CLIENT_TRANSACTION_EVENT_INITIALIZER (ewm, wid, tid, event, status, errorDescription); + CLIENT_TRANSACTION_EVENT_INITIALIZER (ewm, wid, tid, event); eventHandlerSignalEvent(ewm->handler, (BREvent*) &message); } @@ -534,37 +571,30 @@ ewmSignalTransferEvent(BREthereumEWM ewm, typedef struct { struct BREventRecord base; BREthereumEWM ewm; - // BREthereumWallet wid; - // BREthereumTransaction tid; BREthereumPeerEvent event; - BREthereumStatus status; - const char *errorDescription; + } BREthereumEWMClientPeerEvent; -#define CLIENT_PEER_EVENT_INITIALIZER(ewm, /* wid, tid,*/ event, status, desc) \ -{ { NULL, &ewmClientPeerEventType }, (ewm), /*(wid), (tid),*/ (event), (status), (desc) } +#define CLIENT_PEER_EVENT_INITIALIZER(ewm, /* wid, tid,*/ event) \ +{ { NULL, &ewmClientPeerEventType }, (ewm), /*(wid), (tid),*/ (event) } static void ewmClientPeerEventDispatcher(BREventHandler ignore, BREthereumEWMClientPeerEvent *event) { - ewmHandlePeerEvent(event->ewm, event->event, event->status, event->errorDescription); + ewmHandlePeerEvent(event->ewm, event->event); } static BREventType ewmClientPeerEventType = { - "EMW: Client Peer Event", + "EWM: Client Peer Event", sizeof (BREthereumEWMClientPeerEvent), (BREventDispatcher) ewmClientPeerEventDispatcher }; extern void ewmSignalPeerEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumPeerEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumPeerEvent event) { BREthereumEWMClientPeerEvent message = - CLIENT_PEER_EVENT_INITIALIZER (ewm, /* wid, tid,*/ event, status, errorDescription); + CLIENT_PEER_EVENT_INITIALIZER (ewm, /* wid, tid,*/ event); eventHandlerSignalEvent(ewm->handler, (BREvent*) &message); } @@ -574,37 +604,29 @@ ewmSignalPeerEvent(BREthereumEWM ewm, typedef struct { struct BREventRecord base; BREthereumEWM ewm; - // BREthereumWallet wid; - // BREthereumTransaction tid; BREthereumEWMEvent event; - BREthereumStatus status; - const char *errorDescription; } BREthereumEWMClientEWMEvent; -#define CLIENT_EWM_EVENT_INITIALIZER(ewm, /* wid, tid,*/ event, status, desc) \ -{ { NULL, &ewmClientEWMEventType }, (ewm), /*(wid), (tid),*/ (event), (status), (desc) } +#define CLIENT_EWM_EVENT_INITIALIZER(ewm, /* wid, tid,*/ event) \ +{ { NULL, &ewmClientEWMEventType }, (ewm), /*(wid), (tid),*/ (event) } static void ewmClientEWMEventDispatcher(BREventHandler ignore, BREthereumEWMClientEWMEvent *event) { - ewmHandleEWMEvent(event->ewm, event->event, event->status, event->errorDescription); + ewmHandleEWMEvent(event->ewm, event->event); } static BREventType ewmClientEWMEventType = { - "EMW: Client EWM Event", + "EWM: Client EWM Event", sizeof (BREthereumEWMClientEWMEvent), (BREventDispatcher) ewmClientEWMEventDispatcher }; extern void ewmSignalEWMEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumEWMEvent event, - BREthereumStatus status, - const char *errorDescription) { + BREthereumEWMEvent event) { BREthereumEWMClientEWMEvent message = - CLIENT_EWM_EVENT_INITIALIZER (ewm, /* wid, tid,*/ event, status, errorDescription); + CLIENT_EWM_EVENT_INITIALIZER (ewm, /* wid, tid,*/ event); eventHandlerSignalEvent(ewm->handler, (BREvent*) &message); } @@ -620,7 +642,7 @@ typedef struct { static void ewmSignalAnnounceBlockNumberDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceBlockNumberEvent *event) { + BREthereumEWMClientAnnounceBlockNumberEvent *event) { ewmHandleAnnounceBlockNumber(event->ewm, event->blockNumber, event->rid); } @@ -632,8 +654,8 @@ static BREventType ewmClientAnnounceBlockNumberEventType = { extern void ewmSignalAnnounceBlockNumber (BREthereumEWM ewm, - uint64_t blockNumber, - int rid) { + uint64_t blockNumber, + int rid) { BREthereumEWMClientAnnounceBlockNumberEvent message = { { NULL, &ewmClientAnnounceBlockNumberEventType}, ewm, blockNumber, rid}; eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); @@ -652,7 +674,7 @@ typedef struct { static void ewmSignalAnnounceNonceDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceNonceEvent *event) { + BREthereumEWMClientAnnounceNonceEvent *event) { ewmHandleAnnounceNonce(event->ewm, event->address, event->nonce, event->rid); } @@ -664,9 +686,9 @@ static BREventType ewmClientAnnounceNonceEventType = { extern void ewmSignalAnnounceNonce (BREthereumEWM ewm, - BREthereumAddress address, - uint64_t nonce, - int rid) { + BREthereumAddress address, + uint64_t nonce, + int rid) { BREthereumEWMClientAnnounceNonceEvent message = { { NULL, &ewmClientAnnounceNonceEventType}, ewm, address, nonce, rid}; eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); @@ -706,70 +728,62 @@ ewmSignalAnnounceBalance (BREthereumEWM ewm, } // -// Announce Gas Price +// Update Wallet Balances // typedef struct { struct BREventRecord base; BREthereumEWM ewm; - BREthereumWallet wallet; - UInt256 amount; - int rid; -} BREthereumEWMClientAnnounceGasPriceEvent; +} BREthereumEWMClientUpdateWalletBalancesEvent; static void -ewmSignalAnnounceGasPriceDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceGasPriceEvent *event) { - ewmHandleAnnounceGasPrice(event->ewm, event->wallet, event->amount, event->rid); +ewmHandleUpdateWalletBalancesDispatcher (BREventHandler ignore, + BREthereumEWMClientUpdateWalletBalancesEvent *event) { + ewmHandleUpdateWalletBalances (event->ewm); } -static BREventType ewmClientAnnounceGasPriceEventType = { - "EWM: Client Announce GasPrice Event", - sizeof (BREthereumEWMClientAnnounceGasPriceEvent), - (BREventDispatcher) ewmSignalAnnounceGasPriceDispatcher +static BREventType ewmClientUpdateWalletBalancesEventType = { + "EWM: Client Update Wallet Balances Event", + sizeof (BREthereumEWMClientUpdateWalletBalancesEvent), + (BREventDispatcher) ewmHandleUpdateWalletBalancesDispatcher }; extern void -ewmSignalAnnounceGasPrice (BREthereumEWM ewm, - BREthereumWallet wallet, - UInt256 amount, - int rid) { - BREthereumEWMClientAnnounceGasPriceEvent message = - { { NULL, &ewmClientAnnounceGasPriceEventType}, ewm, wallet, amount, rid}; +ewmSignalUpdateWalletBalances (BREthereumEWM ewm) { + BREthereumEWMClientUpdateWalletBalancesEvent message = + { { NULL, &ewmClientUpdateWalletBalancesEventType}, ewm }; eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); } // -// Announce Gas Estimate +// Announce Gas Price // typedef struct { struct BREventRecord base; BREthereumEWM ewm; BREthereumWallet wallet; - BREthereumTransfer transfer; - UInt256 value; + UInt256 amount; int rid; -} BREthereumEWMClientAnnounceGasEstimateEvent; +} BREthereumEWMClientAnnounceGasPriceEvent; static void -ewmSignalAnnounceGasEstimateDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceGasEstimateEvent *event) { - ewmHandleAnnounceGasEstimate(event->ewm, event->wallet, event->transfer, event->value, event->rid); +ewmSignalAnnounceGasPriceDispatcher (BREventHandler ignore, + BREthereumEWMClientAnnounceGasPriceEvent *event) { + ewmHandleAnnounceGasPrice(event->ewm, event->wallet, event->amount, event->rid); } -static BREventType ewmClientAnnounceGasEstimateEventType = { - "EWM: Client Announce GasEstimate Event", - sizeof (BREthereumEWMClientAnnounceGasEstimateEvent), - (BREventDispatcher) ewmSignalAnnounceGasEstimateDispatcher +static BREventType ewmClientAnnounceGasPriceEventType = { + "EWM: Client Announce GasPrice Event", + sizeof (BREthereumEWMClientAnnounceGasPriceEvent), + (BREventDispatcher) ewmSignalAnnounceGasPriceDispatcher }; extern void -ewmSignalAnnounceGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - UInt256 value, - int rid) { - BREthereumEWMClientAnnounceGasEstimateEvent message = - { { NULL, &ewmClientAnnounceGasEstimateEventType}, ewm, wallet, transfer, value, rid}; +ewmSignalAnnounceGasPrice (BREthereumEWM ewm, + BREthereumWallet wallet, + UInt256 amount, + int rid) { + BREthereumEWMClientAnnounceGasPriceEvent message = + { { NULL, &ewmClientAnnounceGasPriceEventType}, ewm, wallet, amount, rid}; eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); } @@ -788,7 +802,7 @@ typedef struct { static void ewmSignalAnnounceSubmitTransferDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceSubmitTransferEvent *event) { + BREthereumEWMClientAnnounceSubmitTransferEvent *event) { ewmHandleAnnounceSubmitTransfer (event->ewm, event->wallet, event->transfer, event->errorCode, event->errorMessage, event->rid); if (NULL != event->errorMessage) free (event->errorMessage); } @@ -798,7 +812,6 @@ ewmSignalAnnounceSubmitTransferDestroyer (BREthereumEWMClientAnnounceSubmitTrans if (NULL != event->errorMessage) free (event->errorMessage); } - static BREventType ewmClientAnnounceSubmitTransferEventType = { "EWM: Client Announce SubmitTransfer Event", sizeof (BREthereumEWMClientAnnounceSubmitTransferEvent), @@ -833,7 +846,7 @@ typedef struct { static void ewmSignalAnnounceTransactionDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceTransactionEvent *event) { + BREthereumEWMClientAnnounceTransactionEvent *event) { ewmHandleAnnounceTransaction(event->ewm, event->bundle, event->rid); } @@ -851,8 +864,8 @@ static BREventType ewmClientAnnounceTransactionEventType = { extern void ewmSignalAnnounceTransaction(BREthereumEWM ewm, - BREthereumEWMClientAnnounceTransactionBundle *bundle, - int rid) { + BREthereumEWMClientAnnounceTransactionBundle *bundle, + int rid) { BREthereumEWMClientAnnounceTransactionEvent message = { { NULL, &ewmClientAnnounceTransactionEventType}, ewm, bundle, rid}; eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); @@ -870,7 +883,7 @@ typedef struct { static void ewmSignalAnnounceLogDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceLogEvent *event) { + BREthereumEWMClientAnnounceLogEvent *event) { ewmHandleAnnounceLog(event->ewm, event->bundle, event->rid); } @@ -888,8 +901,8 @@ static BREventType ewmClientAnnounceLogEventType = { extern void ewmSignalAnnounceLog (BREthereumEWM ewm, - BREthereumEWMClientAnnounceLogBundle *bundle, - int rid) { + BREthereumEWMClientAnnounceLogBundle *bundle, + int rid) { BREthereumEWMClientAnnounceLogEvent message = { { NULL, &ewmClientAnnounceLogEventType}, ewm, bundle, rid}; eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); @@ -940,7 +953,7 @@ typedef struct { static void ewmSignalAnnounceTokenDispatcher (BREventHandler ignore, - BREthereumEWMClientAnnounceTokenEvent *event) { + BREthereumEWMClientAnnounceTokenEvent *event) { ewmHandleAnnounceToken(event->ewm, event->bundle, event->rid); } @@ -958,11 +971,11 @@ static BREventType ewmClientAnnounceTokenEventType = { extern void ewmSignalAnnounceToken (BREthereumEWM ewm, - BREthereumEWMClientAnnounceTokenBundle *bundle, - int rid) { + BREthereumEWMClientAnnounceTokenBundle *bundle, + int rid) { BREthereumEWMClientAnnounceTokenEvent message = { { NULL, &ewmClientAnnounceTokenEventType}, ewm, bundle, rid}; - eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); + eventHandlerSignalEventOOB (ewm->handler, (BREvent*) &message); } // @@ -978,7 +991,7 @@ typedef struct { static void ewmSignalAnnounceTokenCompleteDispatcher (BREventHandler ignore, BREthereumEWMClientAnnounceTokenCompleteEvent *event) { - ewmHandleAnnounceTokenComplete(event->ewm, event->success, event->rid); + ewmHandleAnnounceTokenComplete(event->ewm, event->rid, event->success); } static BREventType ewmClientAnnounceTokenCompleteEventType = { @@ -989,8 +1002,8 @@ static BREventType ewmClientAnnounceTokenCompleteEventType = { extern void ewmSignalAnnounceTokenComplete (BREthereumEWM ewm, - BREthereumBoolean success, - int rid) { + int rid, + BREthereumBoolean success) { BREthereumEWMClientAnnounceTokenCompleteEvent message = { { NULL, &ewmClientAnnounceTokenCompleteEventType}, ewm, success, rid }; eventHandlerSignalEvent (ewm->handler, (BREvent*) &message); @@ -1011,6 +1024,7 @@ const BREventType *ewmEventTypes[] = { &handleSaveBlocksEventType, &handleSaveNodesEventType, &handleSyncEventType, + &handleSyncAPIEventType, &handleGetBlocksEventType, &ewmClientWalletEventType, @@ -1021,8 +1035,8 @@ const BREventType *ewmEventTypes[] = { &ewmClientAnnounceBlockNumberEventType, &ewmClientAnnounceNonceEventType, &ewmClientAnnounceBalanceEventType, + &ewmClientUpdateWalletBalancesEventType, &ewmClientAnnounceGasPriceEventType, - &ewmClientAnnounceGasEstimateEventType, &ewmClientAnnounceSubmitTransferEventType, &ewmClientAnnounceTransactionEventType, &ewmClientAnnounceLogEventType, diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMPersist.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMPersist.c new file mode 100644 index 000000000..104afa5b6 --- /dev/null +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMPersist.c @@ -0,0 +1,421 @@ +// +// BREthereumEWMPersist.c +// BRCore +// +// Created by Ed Gamble on 9/24/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#include "BREthereumEWMPrivate.h" + +/// MARK: - Transaction File Service +#define fileServiceTypeTransactions "transactions" + +enum { + EWM_TRANSACTION_VERSION_1 +}; + +static UInt256 +fileServiceTypeTransactionV1Identifier (BRFileServiceContext context, + BRFileService fs, + const void *entity) { + BREthereumTransaction transaction = (BREthereumTransaction) entity; + BREthereumHash hash = transactionGetHash(transaction); + + UInt256 result; + memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); + return result; +} + +static uint8_t * +fileServiceTypeTransactionV1Writer (BRFileServiceContext context, + BRFileService fs, + const void* entity, + uint32_t *bytesCount) { + BREthereumEWM ewm = context; + BREthereumTransaction transaction = (BREthereumTransaction) entity; + + BRRlpItem item = transactionRlpEncode(transaction, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); + BRRlpData data = rlpGetData (ewm->coder, item); + rlpReleaseItem (ewm->coder, item); + + *bytesCount = (uint32_t) data.bytesCount; + return data.bytes; +} + +static void * +fileServiceTypeTransactionV1Reader (BRFileServiceContext context, + BRFileService fs, + uint8_t *bytes, + uint32_t bytesCount) { + BREthereumEWM ewm = context; + + BRRlpData data = { bytesCount, bytes }; + BRRlpItem item = rlpGetItem (ewm->coder, data); + + BREthereumTransaction transaction = transactionRlpDecode(item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); + rlpReleaseItem (ewm->coder, item); + + return transaction; +} + +/// MARK: - Log File Service + +#define fileServiceTypeLogs "logs" + +enum { + EWM_LOG_VERSION_1 +}; + +static UInt256 +fileServiceTypeLogV1Identifier (BRFileServiceContext context, + BRFileService fs, + const void *entity) { + const BREthereumLog log = (BREthereumLog) entity; + BREthereumHash hash = logGetHash( log); + + UInt256 result; + memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); + return result; +} + +static uint8_t * +fileServiceTypeLogV1Writer (BRFileServiceContext context, + BRFileService fs, + const void* entity, + uint32_t *bytesCount) { + BREthereumEWM ewm = context; + BREthereumLog log = (BREthereumLog) entity; + + BRRlpItem item = logRlpEncode (log, RLP_TYPE_ARCHIVE, ewm->coder); + BRRlpData data = rlpGetData (ewm->coder, item); + rlpReleaseItem (ewm->coder, item); + + *bytesCount = (uint32_t) data.bytesCount; + return data.bytes; +} + +static void * +fileServiceTypeLogV1Reader (BRFileServiceContext context, + BRFileService fs, + uint8_t *bytes, + uint32_t bytesCount) { + BREthereumEWM ewm = context; + + BRRlpData data = { bytesCount, bytes }; + BRRlpItem item = rlpGetItem (ewm->coder, data); + + BREthereumLog log = logRlpDecode(item, RLP_TYPE_ARCHIVE, ewm->coder); + rlpReleaseItem (ewm->coder, item); + + return log; +} + +/// MARK: - Block File Service + +#define fileServiceTypeBlocks "blocks" +enum { + EWM_BLOCK_VERSION_1 +}; + +static UInt256 +fileServiceTypeBlockV1Identifier (BRFileServiceContext context, + BRFileService fs, + const void *entity) { + const BREthereumBlock block = (BREthereumBlock) entity; + BREthereumHash hash = blockGetHash(block); + + UInt256 result; + memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); + return result; +} + +static uint8_t * +fileServiceTypeBlockV1Writer (BRFileServiceContext context, + BRFileService fs, + const void* entity, + uint32_t *bytesCount) { + BREthereumEWM ewm = context; + BREthereumBlock block = (BREthereumBlock) entity; + + BRRlpItem item = blockRlpEncode(block, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); + BRRlpData data = rlpGetData (ewm->coder, item); + rlpReleaseItem (ewm->coder, item); + + *bytesCount = (uint32_t) data.bytesCount; + return data.bytes; +} + +static void * +fileServiceTypeBlockV1Reader (BRFileServiceContext context, + BRFileService fs, + uint8_t *bytes, + uint32_t bytesCount) { + BREthereumEWM ewm = context; + + BRRlpData data = { bytesCount, bytes }; + BRRlpItem item = rlpGetItem (ewm->coder, data); + + BREthereumBlock block = blockRlpDecode (item, ewm->network, RLP_TYPE_ARCHIVE, ewm->coder); + rlpReleaseItem (ewm->coder, item); + + return block; +} + +// MARK: - Node File Service + +#define fileServiceTypeNodes "nodes" +enum { + EWM_NODE_VERSION_1 +}; + +static UInt256 +fileServiceTypeNodeV1Identifier (BRFileServiceContext context, + BRFileService fs, + const void *entity) { + const BREthereumNodeConfig node = (BREthereumNodeConfig) entity; + + BREthereumHash hash = nodeConfigGetHash(node); + + UInt256 result; + memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); + return result; +} + +static uint8_t * +fileServiceTypeNodeV1Writer (BRFileServiceContext context, + BRFileService fs, + const void* entity, + uint32_t *bytesCount) { + BREthereumEWM ewm = context; + const BREthereumNodeConfig node = (BREthereumNodeConfig) entity; + + BRRlpItem item = nodeConfigEncode (node, ewm->coder); + BRRlpData data = rlpGetData (ewm->coder, item); + rlpReleaseItem (ewm->coder, item); + + *bytesCount = (uint32_t) data.bytesCount; + return data.bytes; +} + +static void * +fileServiceTypeNodeV1Reader (BRFileServiceContext context, + BRFileService fs, + uint8_t *bytes, + uint32_t bytesCount) { + BREthereumEWM ewm = context; + + BRRlpData data = { bytesCount, bytes }; + BRRlpItem item = rlpGetItem (ewm->coder, data); + + BREthereumNodeConfig node = nodeConfigDecode (item, ewm->coder); + rlpReleaseItem (ewm->coder, item); + + return node; +} + +/// MARK: - Token File Service + +#define fileServiceTypeTokens "tokens" + +enum { + EWM_TOKEN_VERSION_1 +}; + +static UInt256 +fileServiceTypeTokenV1Identifier (BRFileServiceContext context, + BRFileService fs, + const void *entity) { + BREthereumToken token = (BREthereumToken) entity; + BREthereumHash hash = tokenGetHash(token); + + UInt256 result; + memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); + return result; +} + +static uint8_t * +fileServiceTypeTokenV1Writer (BRFileServiceContext context, + BRFileService fs, + const void* entity, + uint32_t *bytesCount) { + BREthereumEWM ewm = context; + BREthereumToken token = (BREthereumToken) entity; + + BRRlpItem item = tokenEncode(token, ewm->coder); + BRRlpData data = rlpGetData (ewm->coder, item); + rlpReleaseItem (ewm->coder, item); + + *bytesCount = (uint32_t) data.bytesCount; + return data.bytes; +} + +static void * +fileServiceTypeTokenV1Reader (BRFileServiceContext context, + BRFileService fs, + uint8_t *bytes, + uint32_t bytesCount) { + BREthereumEWM ewm = context; + + BRRlpData data = { bytesCount, bytes }; + BRRlpItem item = rlpGetItem (ewm->coder, data); + + BREthereumToken token = tokenDecode(item, ewm->coder); + rlpReleaseItem (ewm->coder, item); + + return token; +} + +/// MARK: - Wallet File Service + +#define fileServiceTypeWallets "wallets" + +enum { + EWM_WALLET_VERSION_1 +}; + +static UInt256 +fileServiceTypeWalletV1Identifier (BRFileServiceContext context, + BRFileService fs, + const void *entity) { + BREthereumWalletState state = (BREthereumWalletState) entity; + BREthereumHash hash = walletStateGetHash (state); + + UInt256 result; + memcpy (result.u8, hash.bytes, ETHEREUM_HASH_BYTES); + return result; +} + +static uint8_t * +fileServiceTypeWalletV1Writer (BRFileServiceContext context, + BRFileService fs, + const void* entity, + uint32_t *bytesCount) { + BREthereumEWM ewm = context; + BREthereumWalletState state = (BREthereumWalletState) entity; + + BRRlpItem item = walletStateEncode (state, ewm->coder); + BRRlpData data = rlpGetData (ewm->coder, item); + rlpReleaseItem (ewm->coder, item); + + *bytesCount = (uint32_t) data.bytesCount; + return data.bytes; +} + +static void * +fileServiceTypeWalletV1Reader (BRFileServiceContext context, + BRFileService fs, + uint8_t *bytes, + uint32_t bytesCount) { + BREthereumEWM ewm = context; + + BRRlpData data = { bytesCount, bytes }; + BRRlpItem item = rlpGetItem (ewm->coder, data); + + BREthereumWalletState state = walletStateDecode(item, ewm->coder); + rlpReleaseItem (ewm->coder, item); + + return state; +} + + +#define fileServiceSpecificationsCount (6) +static BRFileServiceTypeSpecification fileServiceSpecifications[] = { + { + fileServiceTypeTransactions, + EWM_TRANSACTION_VERSION_1, + 1, + { + { + EWM_TRANSACTION_VERSION_1, + fileServiceTypeTransactionV1Identifier, + fileServiceTypeTransactionV1Reader, + fileServiceTypeTransactionV1Writer + } + } + }, + + { + fileServiceTypeLogs, + EWM_LOG_VERSION_1, + 1, + { + { + EWM_LOG_VERSION_1, + fileServiceTypeLogV1Identifier, + fileServiceTypeLogV1Reader, + fileServiceTypeLogV1Writer + } + } + }, + + { + fileServiceTypeBlocks, + EWM_BLOCK_VERSION_1, + 1, + { + { + EWM_BLOCK_VERSION_1, + fileServiceTypeBlockV1Identifier, + fileServiceTypeBlockV1Reader, + fileServiceTypeBlockV1Writer + } + } + }, + + { + fileServiceTypeNodes, + EWM_NODE_VERSION_1, + 1, + { + { + EWM_NODE_VERSION_1, + fileServiceTypeNodeV1Identifier, + fileServiceTypeNodeV1Reader, + fileServiceTypeNodeV1Writer + } + } + }, + + { + fileServiceTypeTokens, + EWM_TOKEN_VERSION_1, + 1, + { + { + EWM_TOKEN_VERSION_1, + fileServiceTypeTokenV1Identifier, + fileServiceTypeTokenV1Reader, + fileServiceTypeTokenV1Writer + } + } + }, + + { + fileServiceTypeWallets, + EWM_WALLET_VERSION_1, + 1, + { + { + EWM_TOKEN_VERSION_1, + fileServiceTypeWalletV1Identifier, + fileServiceTypeWalletV1Reader, + fileServiceTypeWalletV1Writer + } + } + } +}; + +const char *ewmFileServiceTypeTransactions = fileServiceTypeTransactions; +const char *ewmFileServiceTypeLogs = fileServiceTypeLogs; +const char *ewmFileServiceTypeBlocks = fileServiceTypeBlocks; +const char *ewmFileServiceTypeNodes = fileServiceTypeNodes; +const char *ewmFileServiceTypeTokens = fileServiceTypeTokens; +const char *ewmFileServiceTypeWallets = fileServiceTypeWallets; + +size_t ewmFileServiceSpecificationsCount = fileServiceSpecificationsCount; +BRFileServiceTypeSpecification *ewmFileServiceSpecifications = fileServiceSpecifications; + diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMPrivate.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMPrivate.h index e0c6b8309..7b4114062 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMPrivate.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumEWMPrivate.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 5/7/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -11,6 +11,7 @@ #ifndef BR_Ethereum_EWM_Private_H #define BR_Ethereum_EWM_Private_H +#include #include #include "support/BRFileService.h" #include "ethereum/blockchain/BREthereumBlockChain.h" @@ -35,6 +36,8 @@ typedef enum { (CLIENT_CHANGE_ADD == (ev) ? "Add" \ : (CLIENT_CHANGE_REM == (ev) ? "Rem" : "Upd")) +#define EWM_REQUEST_ID_UNKNOWN (UINT_MAX) + // // EWM // @@ -44,20 +47,15 @@ typedef enum { #define DEFAULT_BLOCK_CAPACITY 100 #define DEFAULT_TRANSACTION_CAPACITY 1000 -typedef enum { - LIGHT_NODE_CREATED, - LIGHT_NODE_CONNECTING, - LIGHT_NODE_CONNECTED, - LIGHT_NODE_DISCONNECTING, - LIGHT_NODE_DISCONNECTED, - LIGHT_NODE_ERRORED -} BREthereumEWMState; - /// MISPLACED extern void ewmInsertWallet (BREthereumEWM ewm, BREthereumWallet wallet); +extern void +ewmUpdateBlockHeight(BREthereumEWM ewm, + uint64_t blockHeight); + // // EWM // @@ -70,7 +68,7 @@ struct BREthereumEWMRecord { /** * The Mode of this EWM */ - BREthereumMode mode; + BRCryptoSyncMode mode; /** * The network @@ -86,6 +84,7 @@ struct BREthereumEWMRecord { * The account */ BREthereumAccount account; + BREthereumTimestamp accountTimestamp; /** * The wallets 'managed/handled' by this ewm. There can be only one wallet holding ETHER; @@ -94,6 +93,11 @@ struct BREthereumEWMRecord { BREthereumWallet *wallets; BREthereumWallet walletHoldingEther; + /** + * ERC20 Tokens + */ + BRSetOf(BREthereumToken) tokens; + /** * The BCS Interface */ @@ -103,9 +107,17 @@ struct BREthereumEWMRecord { * The BlockHeight is the largest block number seen or computed. [Note: the blockHeight may * be computed from a Log event as (log block number + log confirmations). This is the block * number for the block at the head of the blockchain. + * + * This gets initialized from ewmCreate() based on the 'known' block height of the network or + * with zero if the network's block height is unknown. */ uint64_t blockHeight; + /** + * The number of blocks required to be mined before until a transfer can be considered final + */ + uint64_t confirmationsUntilFinal; + /** * An identiifer for a LES/BRD Request */ @@ -140,11 +152,11 @@ struct BREthereumEWMRecord { uint64_t begBlockNumber; uint64_t endBlockNumber; - int ridTransaction; - int ridLog; + unsigned int ridTransaction; + unsigned int ridLog; - int completedTransaction:1; - int completedLog:1; + unsigned int completedTransaction:1; + unsigned int completedLog:1; } brdSync; }; @@ -203,15 +215,30 @@ ewmSignalGasPrice (BREthereumEWM ewm, // Signal/Handle GasEstimate (BCS Callback) // extern void -ewmHandleGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - BREthereumGas gasEstimate); +ewmHandlGasEstimateSuccess (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumGas gasEstimate, + BREthereumGasPrice gasPrice); + +extern void +ewmHandlGasEstimateFailure (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status); + extern void -ewmSignalGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - BREthereumGas gasEstimate); +ewmSignalGasEstimateSuccess(BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumGas gasEstimate, + BREthereumGasPrice gasPrice); + +extern void +ewmSignalGasEstimateFailure(BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumCookie cookie, + BREthereumStatus status); // // Signal/Handle Transaction (BCS Callback) @@ -283,6 +310,11 @@ ewmHandleSaveLog (BREthereumEWM ewm, BREthereumLog log, BREthereumClientChangeType type); +extern void +ewmHandleSaveWallet (BREthereumEWM ewm, + BREthereumWallet wallet, + BREthereumClientChangeType type); + // // Signal/Handle Sync (BCS Callback) // @@ -300,6 +332,16 @@ ewmSignalSync (BREthereumEWM ewm, uint64_t blockNumberCurrent, uint64_t blockNumberStop); +// +// Signal/Handle Sync (BCS Callback) +// +extern void +ewmHandleSyncAPI (BREthereumEWM ewm); + +extern void +ewmSignalSyncAPI (BREthereumEWM ewm, + BREthereumBoolean OOB); + // // Signal/Handle Get Blocks (BCS Callback) // @@ -320,45 +362,35 @@ ewmSignalGetBlocks (BREthereumEWM ewm, extern void ewmHandleAnnounceBalance (BREthereumEWM ewm, - BREthereumWallet wallet, - UInt256 amount, - int rid); + BREthereumWallet wallet, + UInt256 amount, + int rid); extern void ewmSignalAnnounceBalance (BREthereumEWM ewm, - BREthereumWallet wallet, - UInt256 amount, - int rid); - -/// MARK: - GasPrice + BREthereumWallet wallet, + UInt256 amount, + int rid); extern void -ewmSignalAnnounceGasPrice (BREthereumEWM ewm, - BREthereumWallet wallet, - UInt256 value, - int rid); +ewmHandleUpdateWalletBalances (BREthereumEWM ewm); extern void -ewmHandleAnnounceGasPrice (BREthereumEWM ewm, - BREthereumWallet wallet, - UInt256 value, - int rid); +ewmSignalUpdateWalletBalances (BREthereumEWM ewm); -/// MARK: - Estimate Gas +/// MARK: - GasPrice extern void -ewmHandleAnnounceGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - UInt256 value, - int rid); +ewmSignalAnnounceGasPrice (BREthereumEWM ewm, + BREthereumWallet wallet, + UInt256 value, + int rid); extern void -ewmSignalAnnounceGasEstimate (BREthereumEWM ewm, - BREthereumWallet wallet, - BREthereumTransfer transfer, - UInt256 value, - int rid); +ewmHandleAnnounceGasPrice (BREthereumEWM ewm, + BREthereumWallet wallet, + UInt256 value, + int rid); /// MARK: - Submit Transaction @@ -383,7 +415,7 @@ ewmHandleAnnounceSubmitTransfer (BREthereumEWM ewm, typedef struct { BREthereumHash hash; BREthereumAddress from; - BREthereumAddress to; + BREthereumAddress *to; BREthereumAddress contract; UInt256 amount; uint64_t gasLimit; @@ -401,6 +433,7 @@ typedef struct { static inline void ewmClientAnnounceTransactionBundleRelease (BREthereumEWMClientAnnounceTransactionBundle *bundle) { + if (NULL != bundle->to) free(bundle->to); free (bundle->data); free (bundle); } @@ -495,12 +528,12 @@ ewmSignalAnnounceToken (BREthereumEWM ewm, extern void ewmHandleAnnounceTokenComplete (BREthereumEWM ewm, - BREthereumBoolean success, - int rid); + int rid, + BREthereumBoolean success); extern void ewmSignalAnnounceTokenComplete (BREthereumEWM ewm, - BREthereumBoolean success, - int rid); + int rid, + BREthereumBoolean success); /// MARK: - BlockNumber @@ -528,7 +561,6 @@ ewmSignalAnnounceNonce (BREthereumEWM ewm, uint64_t nonce, int rid); - /// // Save Sync (and other) State // @@ -539,16 +571,12 @@ ewmSignalAnnounceNonce (BREthereumEWM ewm, extern void ewmHandleWalletEvent(BREthereumEWM ewm, BREthereumWallet wid, - BREthereumWalletEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumWalletEvent event); extern void ewmSignalWalletEvent(BREthereumEWM ewm, BREthereumWallet wid, - BREthereumWalletEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumWalletEvent event); // // Block Event @@ -557,16 +585,12 @@ ewmSignalWalletEvent(BREthereumEWM ewm, extern void ewmSignalBlockEvent(BREthereumEWM ewm, BREthereumBlock bid, - BREthereumBlockEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumBlockEvent event); extern void ewmHandleBlockEvent(BREthereumEWM ewm, BREthereumBlock bid, - BREthereumBlockEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumBlockEvent event); #endif // // Transfer Event @@ -574,62 +598,54 @@ ewmHandleBlockEvent(BREthereumEWM ewm, extern void ewmSignalTransferEvent(BREthereumEWM ewm, - BREthereumWallet wid, - BREthereumTransfer tid, - BREthereumTransferEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumWallet wid, + BREthereumTransfer tid, + BREthereumTransferEvent event); extern void ewmHandleTransferEvent(BREthereumEWM ewm, - BREthereumWallet wid, - BREthereumTransfer tid, - BREthereumTransferEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumWallet wid, + BREthereumTransfer tid, + BREthereumTransferEvent event); // // Peer Event // extern void ewmSignalPeerEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumPeerEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumPeerEvent event); extern void ewmHandlePeerEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumPeerEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumPeerEvent event); // // EWM Event // extern void ewmSignalEWMEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumEWMEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumEWMEvent event); extern void ewmHandleEWMEvent(BREthereumEWM ewm, - // BREthereumWallet wid, - // BREthereumTransaction tid, - BREthereumEWMEvent event, - BREthereumStatus status, - const char *errorDescription); + BREthereumEWMEvent event); /// MARK: - Handler For Main extern const BREventType *ewmEventTypes[]; extern const unsigned int ewmEventTypesCount; +/// MARK: - File Service + +extern const char *ewmFileServiceTypeTransactions; +extern const char *ewmFileServiceTypeLogs; +extern const char *ewmFileServiceTypeBlocks; +extern const char *ewmFileServiceTypeNodes; +extern const char *ewmFileServiceTypeTokens; +extern const char *ewmFileServiceTypeWallets; + +extern size_t ewmFileServiceSpecificationsCount; +extern BRFileServiceTypeSpecification *ewmFileServiceSpecifications; + #ifdef __cplusplus } #endif diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.c index 325e5c7aa..29b7586ce 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 7/9/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -117,7 +117,7 @@ struct BREthereumTransferRecord { /* * The target */ - BREthereumAddress targetAddress; + BREthereumAddress *targetAddress; /** * The amount - which includes a 'currency' and will/must be consistent with the @@ -184,14 +184,17 @@ struct BREthereumTransferRecord { */ static BREthereumTransfer transferCreateDetailed (BREthereumAddress sourceAddress, - BREthereumAddress targetAddress, + BREthereumAddress *targetAddress, BREthereumAmount amount, BREthereumFeeBasis feeBasis, OwnershipGiven BREthereumTransaction originatingTransaction) { BREthereumTransfer transfer = calloc (1, sizeof(struct BREthereumTransferRecord)); transfer->sourceAddress = sourceAddress; - transfer->targetAddress = targetAddress; + if (NULL != targetAddress) { + transfer->targetAddress = calloc(1, sizeof(BREthereumAddress)); + *transfer->targetAddress = *targetAddress; + } transfer->amount = amount; transfer->feeBasis = feeBasis; transfer->gasEstimate = gasCreate(0); @@ -205,7 +208,7 @@ transferCreateDetailed (BREthereumAddress sourceAddress, extern BREthereumTransfer transferCreate (BREthereumAddress sourceAddress, - BREthereumAddress targetAddress, + BREthereumAddress *targetAddress, BREthereumAmount amount, BREthereumFeeBasis feeBasis, BREthereumTransferBasisType transferBasisType) { @@ -318,7 +321,7 @@ transferCreateWithLog (OwnershipGiven BREthereumLog log, // No originating transaction BREthereumTransfer transfer = transferCreateDetailed (sourceAddress, - targetAddress, + &targetAddress, amount, feeBasis, NULL); @@ -338,6 +341,7 @@ extern void transferRelease (BREthereumTransfer transfer) { transactionRelease (transfer->originatingTransaction); transferBasisRelease (&transfer->basis); + if (NULL != transfer->targetAddress) free(transfer->targetAddress); free (transfer); } @@ -346,7 +350,7 @@ transferGetSourceAddress (BREthereumTransfer transfer) { return transfer->sourceAddress; } -extern BREthereumAddress +extern BREthereumAddress* transferGetTargetAddress (BREthereumTransfer transfer) { return transfer->targetAddress; } @@ -368,6 +372,12 @@ transferGetFeeBasis (BREthereumTransfer transfer) { return transfer->feeBasis; } +extern void +transferSetFeeBasis (BREthereumTransfer transfer, + BREthereumFeeBasis feeBasis) { + transfer->feeBasis = feeBasis; +} + extern BREthereumGas transferGetGasEstimate (BREthereumTransfer transfer) { return transfer->gasEstimate; @@ -690,7 +700,7 @@ transferProvideOriginatingTransactionData (BREthereumTransfer transfer) { UInt256 value = amountGetTokenQuantity(transfer->amount).valueAsInteger; char address[ADDRESS_ENCODED_CHARS]; - addressFillEncodedString(transfer->targetAddress, 0, address); + addressFillEncodedString(NULL == transfer->targetAddress ? EMPTY_ADDRESS_INIT : *transfer->targetAddress, 0, address); // Data is a HEX ENCODED string return (char *) contractEncode (contractERC20, functionERC20Transfer, @@ -703,13 +713,13 @@ transferProvideOriginatingTransactionData (BREthereumTransfer transfer) { } } -static BREthereumAddress +static BREthereumAddress* transferProvideOriginatingTransactionTargetAddress (BREthereumTransfer transfer) { switch (amountGetType(transfer->amount)) { case AMOUNT_ETHER: return transfer->targetAddress; case AMOUNT_TOKEN: - return tokenGetAddressRaw(amountGetToken(transfer->amount)); + return tokenGetAddressPtrRaw(amountGetToken(transfer->amount)); } } @@ -754,6 +764,16 @@ transferGetEffectiveAmountInEther(BREthereumTransfer transfer) { } } +private_extern BREthereumAddress* +transferGetEffectiveTargetAddress (BREthereumTransfer transfer) { + return transferProvideOriginatingTransactionTargetAddress (transfer); +} + +private_extern BREthereumAddress +transferGetEffectiveSourceAddress (BREthereumTransfer transfer) { + return transfer->sourceAddress; +} + extern BREthereumComparison transferCompare (BREthereumTransfer t1, BREthereumTransfer t2) { diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.h index 4a3e661a8..85560c5cb 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumTransfer.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 7/9/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -25,16 +25,6 @@ extern "C" { #define TRANSACTION_NONCE_IS_NOT_ASSIGNED UINT64_MAX -static inline BREthereumGas -feeBasisGetGasLimit (BREthereumFeeBasis basis) { - return (FEE_BASIS_GAS == basis.type ? basis.u.gas.limit : gasCreate(0)); -} - -static inline BREthereumGasPrice -feeBasisGetGasPrice (BREthereumFeeBasis basis) { - return (FEE_BASIS_GAS == basis.type ? basis.u.gas.price : gasPriceCreate(etherCreateZero())); -} - typedef enum { TRANSFER_BASIS_TRANSACTION, TRANSFER_BASIS_LOG @@ -45,7 +35,7 @@ typedef enum { */ extern BREthereumTransfer transferCreate (BREthereumAddress sourceAddress, - BREthereumAddress targetAddress, + BREthereumAddress *targetAddress, BREthereumAmount amount, BREthereumFeeBasis feeBasis, BREthereumTransferBasisType transferBasisType); @@ -76,7 +66,7 @@ transferRelease (BREthereumTransfer transfer); extern BREthereumAddress transferGetSourceAddress (BREthereumTransfer transfer); -extern BREthereumAddress +extern BREthereumAddress* transferGetTargetAddress (BREthereumTransfer transfer); extern BREthereumAmount @@ -85,6 +75,10 @@ transferGetAmount (BREthereumTransfer transfer); extern BREthereumFeeBasis transferGetFeeBasis (BREthereumTransfer transfer); +extern void // Private-ish +transferSetFeeBasis (BREthereumTransfer transfer, + BREthereumFeeBasis feeBasis); + extern BREthereumGas transferGetGasEstimate (BREthereumTransfer transfer); @@ -235,6 +229,19 @@ transferStatusEqual (BREthereumTransferStatus status1, extern BREthereumTransferStatus transferStatusCreate (BREthereumTransactionStatus status); +// Returns Ether appropriate for encoding a transaction. If the transaction is for a TOKEN, +// then the returned Ether is zero (because the amount of a TOKEN transfer is encoded in the +// contract's function call, in the transaction.data field). +private_extern BREthereumEther +transferGetEffectiveAmountInEther (BREthereumTransfer transfer); + +private_extern BREthereumAddress* +transferGetEffectiveTargetAddress (BREthereumTransfer transfer); + +private_extern BREthereumAddress +transferGetEffectiveSourceAddress (BREthereumTransfer transfer); + + #ifdef __cplusplus } #endif diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.c b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.c index 8c940fd5d..4bf4e9dea 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.c @@ -1,9 +1,9 @@ // // BBREthereumWallet.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -202,13 +202,13 @@ walletsRelease (OwnershipGiven BRArrayOf(BREthereumWallet) wallets) { // extern BREthereumEther walletEstimateTransferFee (BREthereumWallet wallet, - BREthereumAmount amount, - int *overflow) { - return walletEstimateTransferFeeDetailed(wallet, - amount, - wallet->defaultGasPrice, - amountGetGasEstimate(amount), - overflow); + BREthereumAmount amount, + int *overflow) { + return walletEstimateTransferFeeDetailed (wallet, + amount, + wallet->defaultGasPrice, + amountGetGasEstimate(amount), + overflow); } /** @@ -216,13 +216,13 @@ walletEstimateTransferFee (BREthereumWallet wallet, */ extern BREthereumEther walletEstimateTransferFeeDetailed (BREthereumWallet wallet, - BREthereumAmount amount, - BREthereumGasPrice price, - BREthereumGas gas, - int *overflow) { - return etherCreate(mulUInt256_Overflow(price.etherPerGas.valueInWEI, - createUInt256(gas.amountOfGas), - overflow)); + BREthereumAmount amount, + BREthereumGasPrice price, + BREthereumGas gas, + int *overflow) { + return etherCreate (mulUInt256_Overflow (price.etherPerGas.valueInWEI, + createUInt256(gas.amountOfGas), + overflow)); } // @@ -230,7 +230,7 @@ walletEstimateTransferFeeDetailed (BREthereumWallet wallet, // extern BREthereumTransfer walletCreateTransferWithFeeBasis (BREthereumWallet wallet, - BREthereumAddress recvAddress, + BREthereumAddress *recvAddress, BREthereumAmount amount, BREthereumFeeBasis feeBasis) { BREthereumTransfer transfer = transferCreate (wallet->address, recvAddress, amount, feeBasis, @@ -243,21 +243,21 @@ walletCreateTransferWithFeeBasis (BREthereumWallet wallet, extern BREthereumTransfer walletCreateTransfer(BREthereumWallet wallet, - BREthereumAddress recvAddress, - BREthereumAmount amount) { - - return walletCreateTransferWithFeeBasis(wallet, recvAddress, amount, - (BREthereumFeeBasis) { - FEE_BASIS_GAS, - { .gas = { - wallet->defaultGasLimit, - wallet->defaultGasPrice - }}}); + BREthereumAddress *recvAddress, + BREthereumAmount amount) { + + return walletCreateTransferWithFeeBasis (wallet, recvAddress, amount, + (BREthereumFeeBasis) { + FEE_BASIS_GAS, + { .gas = { + wallet->defaultGasLimit, + wallet->defaultGasPrice + }}}); } extern BREthereumTransfer walletCreateTransferGeneric (BREthereumWallet wallet, - BREthereumAddress recvAddress, + BREthereumAddress *recvAddress, BREthereumEther amount, BREthereumGasPrice gasPrice, BREthereumGas gasLimit, @@ -403,6 +403,10 @@ walletUpdateBalance (BREthereumWallet wallet) { UInt256 balance = subUInt256_Negative(recv, sent, &negative); + // If we are going to be changing the balance here then 1) shouldn't we call walletSetBalance() + // and shouldn't we also ensure that an event is generated (like all calls to + // walletSetBalance() ensure)? + if (AMOUNT_ETHER == amountGetType(wallet->balance)) { balance = subUInt256_Negative(balance, fees, &negative); wallet->balance = amountCreateEther (etherCreate(balance)); @@ -600,6 +604,134 @@ walletTransferErrored (BREthereumWallet wallet, transactionStatusCreateErrored(reason)); } #endif // 0 + +/// MARK: - Wallet State + +struct BREthereumWalletStateRecord { + // This is a token address or, if Ether, FAKE_ETHER_ADDRESS_INIT + BREthereumAddress address; + + // Normally we would save the wallet's balance as `BREthereumAmount` but that concept includes + // the `BREthereumToken` if the amount is 'token based'. In the context of WalletState we + // do not have tokens available (tokens are held as `BRSet` in `BREthereumEWM`, not globally). + // So, we'll save the balance as `UInt256` and then when recovering the wallet use this + // `amount` with the above `address` to create a proper balance. + UInt256 amount; + + // Include the account nonce if the this wallet state is for ETHER. Hackily. + uint64_t nonce; +}; + +#define FAKE_ETHER_ADDRESS_INIT ((const BREthereumAddress) { \ +0xff, 0xff, 0xff, 0xff, \ +0xff, 0xff, 0xff, 0xff, \ +0xff, 0xff, 0xff, 0xff, \ +0xff, 0xff, 0xff, 0xff, \ +0xff, 0xff, 0xff, 0xff \ +}) + +extern BREthereumWalletState +walletStateCreate (const BREthereumWallet wallet) { + BREthereumWalletState state = malloc (sizeof (struct BREthereumWalletStateRecord)); + + state->nonce = 0; + + BREthereumAmount balance = walletGetBalance(wallet); + BREthereumToken token = walletGetToken (wallet); + + if (NULL == token) { + state->address = FAKE_ETHER_ADDRESS_INIT; + state->amount = etherGetValue (amountGetEther(balance), WEI); + } + else { + state->address = tokenGetAddressRaw(token); + state->amount = amountGetTokenQuantity(balance).valueAsInteger; + } + + return state; +} + +extern void +walletStateRelease (BREthereumWalletState state) { + free (state); +} + +extern BREthereumAddress +walletStateGetAddress (const BREthereumWalletState walletState) { + return (ETHEREUM_BOOLEAN_IS_TRUE (addressEqual (FAKE_ETHER_ADDRESS_INIT, walletState->address)) + ? EMPTY_ADDRESS_INIT + : walletState->address); +} + +extern UInt256 +walletStateGetAmount (const BREthereumWalletState walletState) { + return walletState->amount; +} + +extern uint64_t +walletStateGetNonce (const BREthereumWalletState walletState) { + return walletState->nonce; +} + +extern void +walletStateSetNonce (BREthereumWalletState walletState, + uint64_t nonce) { + walletState->nonce = nonce; +} + +extern BRRlpItem +walletStateEncode (const BREthereumWalletState state, + BRRlpCoder coder) { + return rlpEncodeList (coder, 3, + addressRlpEncode (&state->address, coder), + rlpEncodeUInt256 (coder, state->amount, 0), + rlpEncodeUInt64 (coder, state->nonce, 0)); +} + +extern BREthereumWalletState +walletStateDecode (BRRlpItem item, + BRRlpCoder coder) { + BREthereumWalletState state = malloc (sizeof (struct BREthereumWalletStateRecord)); + + size_t itemsCount = 0; + const BRRlpItem *items = rlpDecodeList (coder, item, &itemsCount); + assert (3 == itemsCount); + + BREthereumAddress *addr = addressRlpDecode (items[0], coder); + if (NULL != addr) { + state->address = *addr; + free(addr); + } else { + state->address = EMPTY_ADDRESS_INIT; + } + state->amount = rlpDecodeUInt256 (coder, items[1], 0); + state->nonce = rlpDecodeUInt64 (coder, items[2], 0); + + return state; +} + +extern BREthereumHash +walletStateGetHash (const BREthereumWalletState state) { + return addressGetHash (state->address); +} + +static inline size_t +walletStateHashValue (const void *t) +{ + return addressHashValue(((BREthereumWalletState) t)->address); +} + +static inline int +walletStateHashEqual (const void *t1, const void *t2) { + return t1 == t2 || addressHashEqual (((BREthereumWalletState) t1)->address, + ((BREthereumWalletState) t2)->address); +} + +extern BRSetOf(BREthereumWalletState) +walletStateSetCreate (size_t capacity) { + return BRSetNew (walletStateHashValue, walletStateHashEqual, capacity); +} + /* * https://medium.com/blockchain-musings/how-to-create-raw-transfers-in-ethereum-part-1-1df91abdba7c * diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.h b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.h index a60d9bb5d..fe7b96182 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.h +++ b/ThirdParty/breadwallet-core/ethereum/ewm/BREthereumWallet.h @@ -1,9 +1,9 @@ // // BBREthereumWallet.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/21/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -76,18 +76,18 @@ walletEstimateTransferFeeDetailed (BREthereumWallet wallet, */ extern BREthereumTransfer walletCreateTransfer(BREthereumWallet wallet, - BREthereumAddress recvAddress, + BREthereumAddress *recvAddress, BREthereumAmount amount); extern BREthereumTransfer walletCreateTransferWithFeeBasis (BREthereumWallet wallet, - BREthereumAddress recvAddress, + BREthereumAddress *recvAddress, BREthereumAmount amount, BREthereumFeeBasis feeBasis); extern BREthereumTransfer walletCreateTransferGeneric(BREthereumWallet wallet, - BREthereumAddress recvAddress, + BREthereumAddress *recvAddress, BREthereumEther amount, BREthereumGasPrice gasPrice, BREthereumGas gasLimit, @@ -235,12 +235,6 @@ walletGetTransferCount (BREthereumWallet wallet); // TODO: Make 'static' // -// Returns Ether appropriate for encoding a transaction. If the transaction is for a TOKEN, -// then the returned Ether is zero (because the amount of a TOKEN transfer is encoded in the -// contract's function call, in the transaction.data field). -private_extern BREthereumEther -transferGetEffectiveAmountInEther (BREthereumTransfer transfer); - private_extern void walletSetBalance (BREthereumWallet wallet, BREthereumAmount balance); @@ -278,6 +272,46 @@ private_extern int walletHasTransfer (BREthereumWallet wallet, BREthereumTransfer transaction); +/// MARK: - Persisted Wallet State; + +typedef struct BREthereumWalletStateRecord *BREthereumWalletState; + +extern BREthereumWalletState +walletStateCreate (const BREthereumWallet wallet); + +extern void +walletStateRelease (BREthereumWalletState state); + +/** + * If WalletState holds Ether, then the address will be EMPTY_ADDRESS_INIT + */ +extern BREthereumAddress +walletStateGetAddress (const BREthereumWalletState walletState); + +extern UInt256 +walletStateGetAmount (const BREthereumWalletState walletState); + +extern uint64_t +walletStateGetNonce (const BREthereumWalletState walletState); + +extern void +walletStateSetNonce (BREthereumWalletState walletState, + uint64_t nonce); + +extern BRRlpItem +walletStateEncode (const BREthereumWalletState walletState, + BRRlpCoder coder); + +extern BREthereumWalletState +walletStateDecode (BRRlpItem item, + BRRlpCoder coder); + +extern BREthereumHash +walletStateGetHash (const BREthereumWalletState walletState); + +extern BRSetOf(BREthereumWalletState) +walletStateSetCreate (size_t capacity); + #ifdef __cplusplus } #endif diff --git a/ThirdParty/breadwallet-core/ethereum/ewm/testEwm.c b/ThirdParty/breadwallet-core/ethereum/ewm/testEwm.c index 7997091b0..ff9449351 100644 --- a/ThirdParty/breadwallet-core/ethereum/ewm/testEwm.c +++ b/ThirdParty/breadwallet-core/ethereum/ewm/testEwm.c @@ -3,15 +3,17 @@ // CoreTests // // Created by Ed Gamble on 7/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #include #include #include #include #include "ethereum/event/BREventAlarm.h" -#include "BREthereumEWMPrivate.h" +#include "ethereum/ewm/BREthereumEWMPrivate.h" // // EWM Tests @@ -47,45 +49,65 @@ getTokenTSTAddress (BREthereumNetwork network) { } #endif +static BRSetOf(BREthereumToken) tokens; + +static BREthereumToken +tokenLookupTestX (const char *address) { + BREthereumAddress addr = addressCreate(address); + return BRSetGet (tokens, &addr); +} + extern void installTokensForTestOnNetwork (BREthereumNetwork network) { static int needInstall = 1; if (!needInstall) return; needInstall = 0; + tokens = tokenSetCreate(10); + BREthereumGas defaultGasLimit = gasCreate(TOKEN_BRD_DEFAULT_GAS_LIMIT); BREthereumGasPrice defaultGasPrice = gasPriceCreate(etherCreateNumber(TOKEN_BRD_DEFAULT_GAS_PRICE_IN_WEI_UINT64, WEI)); - tokenInstall (getTokenBRDAddress(network), - "BRD", - "BRD Token", - "", - 18, - defaultGasLimit, - defaultGasPrice); + + BREthereumToken token; + + token = tokenCreate (getTokenBRDAddress(network), + "BRD", + "BRD Token", + "", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); + #if defined (BITCOIN_DEBUG) - tokenInstall (getTokenTSTAddress(network), - "TST", - "Test Standard Token", - "TeST Standard Token (TST) for TeSTing (TST)", - 18, - defaultGasLimit, - defaultGasPrice); + token = tokenCreate (getTokenTSTAddress(network), + "TST", + "Test Standard Token", + "TeST Standard Token (TST) for TeSTing (TST)", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); + #endif - tokenInstall ("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", - "EOS", - "EOS Token", - "", - 18, - defaultGasLimit, - defaultGasPrice); - - tokenInstall ("0xdd974d5c2e2928dea5f71b9825b8b646686bd200", - "KNC", - "KNC token", - "", - 18, - defaultGasLimit, - defaultGasPrice); + token = tokenCreate ("0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", + "EOS", + "EOS Token", + "", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); + + token = tokenCreate ("0xdd974d5c2e2928dea5f71b9825b8b646686bd200", + "KNC", + "KNC token", + "", + 18, + defaultGasLimit, + defaultGasPrice); + BRSetAdd (tokens, token); + } // @@ -162,13 +184,14 @@ static void clientEstimateGas (BREthereumClientContext context, BREthereumEWM ewm, BREthereumWallet wid, - BREthereumTransfer tid, + BREthereumCookie cookie, const char *from, const char *to, const char *amount, + const char *price, const char *data, int rid) { - ewmAnnounceGasEstimate(ewm, wid, tid, "0x77", rid); + ewmAnnounceGasEstimateSuccess(ewm, wid, cookie, "0x77", price, rid); } static void @@ -395,66 +418,60 @@ static void clientGetTokens (BREthereumClientContext context, BREthereumEWM ewm, int rid) { - ewmAnnounceToken(ewm, + ewmAnnounceToken(ewm, 0, getTokenBRDAddress(ewm->network), "BRD", "BRD Token", "BRD Token Description", 18, NULL, - NULL, - 0); + NULL); #if defined (BITCOIN_DEBUG) - ewmAnnounceToken(ewm, + ewmAnnounceToken(ewm, 0, getTokenTSTAddress(ewm->network), "TST", "Test Standard Token", "TeST Standard Token (TST) for TeSTing (TST)", 18, NULL, - NULL, - 0); + NULL); #endif // For 0xb302B06FDB1348915599D21BD54A06832637E5E8 - ewmAnnounceToken(ewm, + ewmAnnounceToken(ewm, 0, "0x68e14bb5a45b9681327e16e528084b9d962c1a39", "CAT", "CAT Token", "", 18, NULL, - NULL, - 0); + NULL); - ewmAnnounceToken(ewm, + ewmAnnounceToken(ewm, 0, "0x1234567461d3f8db7496581774bd869c83d51c93", "bitclave", "bitclave", "", 18, NULL, - NULL, - 0); + NULL); - ewmAnnounceToken(ewm, + ewmAnnounceToken(ewm, 0, "0xb3bd49e28f8f832b8d1e246106991e546c323502", "GMT", "GMT", "", 18, NULL, - NULL, - 0); + NULL); - ewmAnnounceToken(ewm, + ewmAnnounceToken(ewm, 0, "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", "EOS", "EOS", "", 18, NULL, - NULL, - 0); + NULL); ewmAnnounceTokenComplete(ewm, ETHEREUM_BOOLEAN_TRUE, rid); @@ -464,11 +481,9 @@ static void clientEventWallet (BREthereumClientContext context, BREthereumEWM ewm, BREthereumWallet wid, - BREthereumWalletEvent event, - BREthereumStatus status, - const char *errorDescription) { - fprintf (stdout, "ETH: TST: WalletEvent: wid=%p, ev=%d\n", wid, event); - switch (event) { + BREthereumWalletEvent event) { + fprintf (stdout, "ETH: TST: WalletEvent: wid=%p, ev=%d\n", wid, event.type); + switch (event.type) { case WALLET_EVENT_BALANCE_UPDATED: signalBalance(context); break; @@ -482,7 +497,7 @@ clientEventToken (BREthereumClientContext context, BREthereumEWM ewm, BREthereumToken token, BREthereumTokenEvent event) { - fprintf (stdout, "ETH: TST: TokenEvent: wid=%p, ev=%d\n", token, event); + fprintf (stdout, "ETH: TST: TokenEvent: wid=%p, ev=%d\n", token, event.type); } #if defined (NEVER_DEFINED) @@ -490,10 +505,8 @@ static void clientEventBlock (BREthereumClientContext context, BREthereumEWM ewm, BREthereumBlockId bid, - BREthereumBlockEvent event, - BREthereumStatus status, - const char *errorDescription) { - fprintf (stdout, "ETH: TST: BlockEvent: bid=%d, ev=%d\n", bid, event); + BREthereumBlockEvent event) { + fprintf (stdout, "ETH: TST: BlockEvent: bid=%d, ev=%d\n", bid, event.type); } #endif @@ -502,32 +515,22 @@ clientEventTransfer (BREthereumClientContext context, BREthereumEWM ewm, BREthereumWallet wid, BREthereumTransfer tid, - BREthereumTransferEvent event, - BREthereumStatus status, - const char *errorDescription) { - fprintf (stdout, "ETH: TST: TransferEvent: tid=%p, ev=%d\n", tid, event); + BREthereumTransferEvent event) { + fprintf (stdout, "ETH: TST: TransferEvent: tid=%p, ev=%d\n", tid, event.type); } static void clientEventPeer (BREthereumClientContext context, BREthereumEWM ewm, - //BREthereumWallet wid, - //BREthereumTransactionId tid, - BREthereumPeerEvent event, - BREthereumStatus status, - const char *errorDescription) { - fprintf (stdout, "ETH: TST: PeerEvent: ev=%d\n", event); + BREthereumPeerEvent event) { + fprintf (stdout, "ETH: TST: PeerEvent: ev=%d\n", event.type); } static void clientEventEWM (BREthereumClientContext context, BREthereumEWM ewm, - //BREthereumWallet wid, - //BREthereumTransactionId tid, - BREthereumEWMEvent event, - BREthereumStatus status, - const char *errorDescription) { - fprintf (stdout, "ETH: TST: EWMEvent: ev=%d\n", event); + BREthereumEWMEvent event) { + fprintf (stdout, "ETH: TST: EWMEvent: ev=%d\n", event.type); } static void @@ -588,9 +591,11 @@ runEWM_CONNECT_test (const char *paperKey, assert (CORE_PARSE_OK == status); BREthereumEWM ewm = ewmCreateWithPaperKey (ethereumMainnet, paperKey, ETHEREUM_TIMESTAMP_UNKNOWN, - BRD_ONLY, + CRYPTO_SYNC_MODE_API_ONLY, client, - storagePath); + storagePath, + 0, + 6); assert (NULL != ewm); BREthereumWallet wallet = ewmGetWallet(ewm); @@ -602,8 +607,8 @@ runEWM_CONNECT_test (const char *paperKey, // Immediately dispatches callbacks for WalletManager and Wallet events. Notable, wallet // create and a wallet update balance events. - ewmConnect(ewm); - + ewmStart(ewm); + printf ("==== Waiting for Balance\n"); // First balance event, from wallet creation, will be 0. But we cannot guarantee that we won't @@ -614,6 +619,8 @@ runEWM_CONNECT_test (const char *paperKey, assert (ETHEREUM_BOOLEAN_TRUE == etherIsEQ (amountGetEther(balance), etherCreateZero()) || ETHEREUM_BOOLEAN_TRUE == etherIsEQ (amountGetEther(balance), expectedBalance)); + ewmConnect(ewm); + // the proper approach is to wait on a 'EWM' connected event. sleep (2); // let connect 'take' @@ -653,9 +660,11 @@ void prepareTransaction (const char *paperKey, client.context = (JsonRpcTestContext) calloc (1, sizeof (struct JsonRpcTestContextRecord)); BREthereumEWM ewm = ewmCreateWithPaperKey (ethereumMainnet, paperKey, ETHEREUM_TIMESTAMP_UNKNOWN, - P2P_ONLY, + CRYPTO_SYNC_MODE_P2P_ONLY, client, - storagePath); + storagePath, + 0, + 6); // A wallet amount Ether BREthereumWallet wallet = ewmGetWallet(ewm); // END - One Time Code Block @@ -713,9 +722,11 @@ testReallySend (const char *storagePath) { alarmClockCreateIfNecessary (1); BREthereumEWM ewm = ewmCreateWithPaperKey (ethereumMainnet, paperKey, ETHEREUM_TIMESTAMP_UNKNOWN, - P2P_ONLY, + CRYPTO_SYNC_MODE_P2P_ONLY, client, - storagePath); + storagePath, + 0, + 6); BREthereumAccount account = ewmGetAccount(ewm); // A wallet amount Ether @@ -784,11 +795,13 @@ runEWM_TOKEN_test (const char *paperKey, BRCoreParseStatus status; - BREthereumToken token = tokenLookup(getTokenBRDAddress(ethereumMainnet)); + BREthereumToken token = tokenLookupTestX(getTokenBRDAddress(ethereumMainnet)); BREthereumEWM ewm = ewmCreateWithPaperKey (ethereumMainnet, paperKey, ETHEREUM_TIMESTAMP_UNKNOWN, - P2P_ONLY, + CRYPTO_SYNC_MODE_P2P_ONLY, client, - storagePath); + storagePath, + 0, + 6); BREthereumWallet wid = ewmGetWalletHoldingToken(ewm, token); BREthereumAmount amount = ewmCreateTokenAmountString(ewm, token, @@ -821,16 +834,20 @@ runEWM_PUBLIC_KEY_test (BREthereumNetwork network, printf ("==== PUBLIC KEY\n"); BREthereumEWM ewm1 = ewmCreateWithPaperKey (ethereumMainnet, paperKey, ETHEREUM_TIMESTAMP_UNKNOWN, - P2P_ONLY, + CRYPTO_SYNC_MODE_P2P_ONLY, client, - storagePath); + storagePath, + 0, + 6); BRKey publicKey = ewmGetAccountPrimaryAddressPublicKey (ewm1); char *addr1 = ewmGetAccountPrimaryAddress (ewm1); BREthereumEWM ewm2 = ewmCreateWithPublicKey (ethereumMainnet, publicKey, ETHEREUM_TIMESTAMP_UNKNOWN, - P2P_ONLY, + CRYPTO_SYNC_MODE_P2P_ONLY, client, - storagePath); + storagePath, + 0, + 6); char *addr2 = ewmGetAccountPrimaryAddress (ewm2); @@ -845,7 +862,7 @@ runEWM_PUBLIC_KEY_test (BREthereumNetwork network, extern void runSyncTest (BREthereumNetwork network, BREthereumAccount account, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumTimestamp accountTimestamp, unsigned int durationInSeconds, const char *storagePath) { @@ -859,7 +876,7 @@ runSyncTest (BREthereumNetwork network, alarmClockCreateIfNecessary (1); - ewm = ewmCreate (ethereumMainnet, account, accountTimestamp, mode, client, storagePath); + ewm = ewmCreate (ethereumMainnet, account, accountTimestamp, mode, client, storagePath, 0, 6); char *address = ewmGetAccountPrimaryAddress(ewm); @@ -890,7 +907,7 @@ runEWMTests (const char *paperKey, // prepareTransaction(NODE_PAPER_KEY, NODE_RECV_ADDR, TEST_TRANS2_GAS_PRICE_VALUE, GAS_LIMIT_DEFAULT, NODE_ETHER_AMOUNT); if (NULL == paperKey) paperKey = NODE_PAPER_KEY; - runEWM_CONNECT_test(paperKey, storagePath); +// runEWM_CONNECT_test(paperKey, storagePath); runEWM_TOKEN_test (paperKey, storagePath); runEWM_PUBLIC_KEY_test (ethereumMainnet, paperKey, storagePath); } diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.c b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.c index b0b0384d2..c34769ea1 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.c +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.c @@ -1,9 +1,9 @@ // // BREthereumLES.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Lamont Samuels on 5/01/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -380,12 +380,21 @@ lesInsertNodeAsAvailable (BREthereumLES les, if (ETHEREUM_BOOLEAN_IS_FALSE(inserted)) array_add (les->availableNodes, node); } -static BREthereumNode +/// Create a node for `endpoint` and add it to `les->nodes`. If `endpoint` does not exist, +/// then do nothing. +static void lesEnsureNodeForEndpoint (BREthereumLES les, OwnershipGiven BREthereumNodeEndpoint endpoint, BREthereumNodeState state, BREthereumNodePriority priority, BREthereumBoolean *added) { + + // Skip out if given an invalid endpoint + if (NULL == endpoint) { + if (NULL != added) *added = ETHEREUM_BOOLEAN_FALSE; + return; + } + BREthereumHash hash = nodeEndpointGetHash(endpoint); pthread_mutex_lock (&les->lock); @@ -421,7 +430,6 @@ lesEnsureNodeForEndpoint (BREthereumLES les, else nodeEndpointRelease(endpoint); // we own it; release if not passed to nodeCreate() pthread_mutex_unlock (&les->lock); - return node; } /// MARK: - DNS Seeds @@ -591,12 +599,12 @@ lesCreate (BREthereumNetwork network, // preserved. nodeEndpointSetStatus (les->localEndpoint, messageP2PStatusCreate (0x00, // ignored - networkGetNetworkId(network), - les->head.number, - les->head.hash, - les->head.totalDifficulty, - les->genesisHash, - LES_SUPPORT_GETH_ANNOUNCE_TYPE)); + networkGetNetworkId(network), + les->head.number, + les->head.hash, + les->head.totalDifficulty, + les->genesisHash, + LES_SUPPORT_GETH_ANNOUNCE_TYPE)); nodeEndpointShowStatus (les->localEndpoint); // Create the PTHREAD LOCK variable @@ -710,9 +718,10 @@ lesRelease(BREthereumLES les) { extern void lesClean (BREthereumLES les) { - pthread_mutex_lock (&les->lock); - les->theTimeToCleanIsNow = 1; - pthread_mutex_unlock (&les->lock); + if (0 == pthread_mutex_trylock (&les->lock)) { + les->theTimeToCleanIsNow = 1; + pthread_mutex_unlock (&les->lock); + } } extern void @@ -1038,11 +1047,9 @@ lesHandleSelectError (BREthereumLES les, } } -static void -lesThreadBootstrapSeeds (BREthereumLES les) { - size_t bootstrappedEndpointsCount = 0; - #if !defined (LES_BOOTSTRAP_LCL_ONLY) +static void +lesSeedQueryAll (BREthereumLES les) { // Create nodes from our network seeds. const char **seeds = networkGetSeeds (les->network); size_t seedsCount = networkGetSeedsCount(les->network); @@ -1055,6 +1062,27 @@ lesThreadBootstrapSeeds (BREthereumLES les) { lesSeedQuery(&context); } +} + +static void +lesSeedQueryAllThreaded (BREthereumLES les) { + pthread_t thread; + + pthread_attr_t attr; + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize (&attr, 1024 * 1024); + pthread_create (&thread, &attr, (ThreadRoutine) lesSeedQueryAll, les); + pthread_attr_destroy(&attr); +} +#endif + +static void +lesThreadBootstrapSeeds (BREthereumLES les) { + size_t bootstrappedEndpointsCount = 0; + +#if !defined (LES_BOOTSTRAP_LCL_ONLY) + lesSeedQueryAllThreaded(les); #endif // !defined (LES_BOOTSTRAP_LCL_ONLY) // Create nodes from compiled-in nodes; this is done in case the 'seed' query fails - which @@ -1067,8 +1095,9 @@ lesThreadBootstrapSeeds (BREthereumLES les) { #if defined (LES_BOOTSTRAP_LCL_ONLY) { NODE_TYPE_PARITY, NODE_PRIORITY_LCL, networkGetEnodesLocal (les->network, 1) }, { NODE_TYPE_GETH, NODE_PRIORITY_LCL, networkGetEnodesLocal (les->network, 0) }, -#else +#elif defined (LES_BOOTSTRAP_BRD_ONLY) { NODE_TYPE_UNKNOWN, NODE_PRIORITY_BRD, networkGetEnodesBRD (les->network) }, +#else { NODE_TYPE_UNKNOWN, NODE_PRIORITY_DIS, networkGetEnodesCommunity (les->network) }, #endif { NODE_TYPE_UNKNOWN, NODE_PRIORITY_DIS, NULL } @@ -1125,15 +1154,15 @@ lesThread (BREthereumLES les) { fd_set readDescriptors, writeDesciptors; int maximumDescriptor = -1; - pthread_mutex_lock (&les->lock); - // See CORE-260: the process of finding seeds, using DNS TXT fields, can take a while. // So, we moved it out of lesCreate() here, in lesThread(). if (les->isPendingDNSSeeds) { les->isPendingDNSSeeds = 0; - lesThreadBootstrapSeeds (les); + lesThreadBootstrapSeeds(les); } + pthread_mutex_lock (&les->lock); + BRArrayOf(BREthereumNode) nodesToRemove; array_new(nodesToRemove, 10); @@ -1204,6 +1233,12 @@ lesThread (BREthereumLES les) { : (BREthereumNode) les->requests[index].nodeReference); #undef ACTIVE_NODE + // If `nodeToUse` is NULL, then there may be no active nodes. We'll leave the + // request unchanged and thus will come back to handling the request once we have + // some active nodes. + // + // TODO: Consider a timeout on a request beging handled? + if (NULL != nodeToUse && nodeHasState (nodeToUse, NODE_ROUTE_TCP, NODE_CONNECTED)) { les->requests[index].node = nodeToUse; @@ -1290,7 +1325,7 @@ lesThread (BREthereumLES les) { // We've been asked to 'clean' - which means 'reclaim memory if possible'. We'll ask // all nodes to clean up; but, only the active ones will have much to do. if (les->theTimeToCleanIsNow) { - eth_log (LES_LOG_TOPIC, "Cleaning%s", ""); + eth_log (LES_LOG_TOPIC, "Cleaning"); FOR_NODES(les, node) nodeClean(node); rlpCoderReclaim(les->coder); @@ -1408,6 +1443,10 @@ lesThread (BREthereumLES les) { array_count(les->availableNodes) > 0) { BREthereumNode node = les->availableNodes[0]; + // This blocks on Unix connect() and then loops on select() for EINPROGRESS. + // Really, really we need NODE_CONNECT_OPEN_SOCKET_IN_PROGRESS with a small + // timeout on connect(). + nodeConnect (node, NODE_ROUTE_TCP, now); switch (nodeGetState(node, NODE_ROUTE_TCP).type) { @@ -1558,7 +1597,11 @@ lesAddRequest (BREthereumLES les, if (NODE_REFERENCE_ALL != node) lesAddRequestSpecifically (les, node, context, callback, provision); else { - for (BREthereumNodeReference ns = NODE_REFERENCE_0; ns <= NODE_REFERENCE_4; ns++) + // We'll make NODE_REFERENCE_MAX - NODE_REFERENCE_MIN specific requests. Since we have at + // most LES_ACTIVE_NODE_COUNT active nodes, we might not get (MAX - MIN) actual requests + // but only as many as the number of active nodes. See ACTIVE_NODE above (which might + // discard node reference over the active nodes). + for (BREthereumNodeReference ns = NODE_REFERENCE_MIN; ns <= NODE_REFERENCE_MAX; ns++) lesAddRequestSpecifically (les, ns, context, callback, provisionCopy (&provision, ETHEREUM_BOOLEAN_FALSE)); // Handle `OwnershipGiven` diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.h index ad738c435..7a12ad52c 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLES.h @@ -1,9 +1,9 @@ // // BREthereumLES.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Lamont Samuels on 5/01/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -79,6 +79,10 @@ typedef void *BREthereumNodeReference; #define NODE_REFERENCE_3 ((BREthereumNodeReference) 3) #define NODE_REFERENCE_4 ((BREthereumNodeReference) 4) +/** Convenient macros for reference bounds */ +#define NODE_REFERENCE_MIN NODE_REFERENCE_0 +#define NODE_REFERENCE_MAX NODE_REFERENCE_4 + #if 5 < LES_ACTIVE_NODE_COUNT // 5 <= 1 + NODE_REFERENCE_4 #error Not enough NODE_REFERENCE declarations #endif diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESBase.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESBase.h index bdfc51734..df056a835 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESBase.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESBase.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -34,14 +34,14 @@ /** */ #define LES_SUPPORT_GETH -#define LES_SUPPORT_GETH_VERSION (2) +#define LES_SUPPORT_GETH_VERSION (3) #define LES_SUPPORT_GETH_ANNOUNCE_TYPE (1) /** * We can optionally only bootstrap from a BRD server. Setting this overrides the subsequent * LES_BOOTSTRAP_LCL_ONLY */ -// #define LES_BOOTSTRAP_BRD_ONLY +#define LES_BOOTSTRAP_BRD_ONLY /** * For debugging only, we can optionally only bootstrap from a LCL (local) server. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.c b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.c index a462df5bf..b686e3ba9 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.c +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.c @@ -1,9 +1,9 @@ // // BREthereumFrameCoder.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Lamont Samuels on 4/26/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.h index 121da8f16..6f71f40ae 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESFrameCoder.h @@ -1,9 +1,9 @@ // // BREthereumFrameCoder.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Lamont Samuels on 4/16/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.c b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.c index f5bc3de42..918644a97 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.c +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.c @@ -3,7 +3,7 @@ // breadwallet // // Created by Lamont Samuels on 4/24/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.h index 83f292520..3a1dd761d 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumLESRandom.h @@ -1,9 +1,9 @@ // // BREthereumRandom.h -// breadwallet-core Ethereum +// Core Ethereum // Created by Lamont Samuels on 5/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.c b/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.c index 23a10d905..5b17fba00 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.c +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/13/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.h index 9a50d07cb..507fb2d58 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumMessage.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/13/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.c b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.c index e52f87a6d..10062e24b 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.c +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/13/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -76,11 +76,11 @@ nodeSend (BREthereumNode node, #define HEPUBLIC_BYTES 32 #define NONCE_BYTES 32 -#define authBufLen (SIG_SIZE_BYTES + HEPUBLIC_BYTES + PUBLIC_SIZE_BYTES + NONCE_BYTES + 1) -#define authCipherBufLen (authBufLen + 65 + 16 + 32) +static const ssize_t authBufLen = SIG_SIZE_BYTES + HEPUBLIC_BYTES + PUBLIC_SIZE_BYTES + NONCE_BYTES + 1; +static const ssize_t authCipherBufLen = authBufLen + 65 + 16 + 32; -#define ackBufLen (PUBLIC_SIZE_BYTES + NONCE_BYTES + 1) -#define ackCipherBufLen (ackBufLen + 65 + 16 + 32) +static const ssize_t ackBufLen = PUBLIC_SIZE_BYTES + NONCE_BYTES + 1; +static const ssize_t ackCipherBufLen = ackBufLen + 65 + 16 + 32; // static int _sendAuthInitiator(BREthereumNode node); @@ -320,6 +320,10 @@ typedef struct { } BREthereumNodeProvisioner; +static long provisionerMessageWaitingResposeCount(BREthereumNodeProvisioner *provisioner) { + return provisioner->messagesCount - provisioner->messagesRemainingCount - provisioner->messagesReceivedCount; +} + static int provisionerSendMessagesPending (BREthereumNodeProvisioner *provisioner) { return provisioner->messagesRemainingCount > 0; @@ -506,6 +510,9 @@ struct BREthereumNodeRecord { /** TRUE if we've discovered the neighbors of this node */ BREthereumBoolean discovered; + /** Stop message will freeze client */ + BREthereumBoolean frozen; + /** When waiting to receive a message, timeout when time exceeds `timeout`. */ time_t timeout; @@ -696,6 +703,7 @@ nodeCreate (BREthereumNodePriority priority, node->coder.messageIdOffset = 0x00; // Changed with 'hello' message exchange. node->discovered = ETHEREUM_BOOLEAN_FALSE; + node->frozen = ETHEREUM_BOOLEAN_FALSE; node->frameCoder = frameCoderCreate(); @@ -1061,18 +1069,18 @@ nodeProcessRecvLES (BREthereumNode node, break; case LES_MESSAGE_CONTRACT_CODES: - case LES_MESSAGE_HELPER_TRIE_PROOFS:; + case LES_MESSAGE_PROOFS: + case LES_MESSAGE_HEADER_PROOFS: eth_log (LES_LOG_TOPIC, "Recv: [ LES, %15s ] Unexpected Response", messageLESGetIdentifierName (message.identifier)); break; + case LES_MESSAGE_HELPER_TRIE_PROOFS:; case LES_MESSAGE_BLOCK_HEADERS: case LES_MESSAGE_BLOCK_BODIES: case LES_MESSAGE_RECEIPTS: - case LES_MESSAGE_PROOFS: case LES_MESSAGE_PROOFS_V2: case LES_MESSAGE_TX_STATUS: - case LES_MESSAGE_HEADER_PROOFS: // Find the provisioner applicable to `message`... for (size_t index = 0; index < array_count (node->provisioners); index++) { BREthereumNodeProvisioner *provisioner = &node->provisioners[index]; @@ -1089,6 +1097,17 @@ nodeProcessRecvLES (BREthereumNode node, } } break; + case LES_MESSAGE_STOP: + for (size_t index = 0; index < array_count (node->provisioners); index++) { + BREthereumNodeProvisioner *provisioner = &node->provisioners[index]; + provisioner->messagesRemainingCount = provisioner->messagesCount - provisioner->messagesReceivedCount; + } + node->frozen = ETHEREUM_BOOLEAN_TRUE; + break; + + case LES_MESSAGE_RESUME: + node->frozen = ETHEREUM_BOOLEAN_FALSE; + break; } if (mustReleaseMessage) messageLESRelease (&message); } @@ -1358,7 +1377,7 @@ nodeProcess (BREthereumNode node, // No release for `message` - it has be OwnershipGiven in the above } - if (FD_ISSET (socket, send)) { + if (FD_ISSET (socket, send) && ETHEREUM_BOOLEAN_IS_FALSE(node->frozen)) { nodeUpdateTimeoutRecv(node, now); // override prior timeout; expect a response. // Send if we can. Really only applies to provision messages, for PIP and LES, using @@ -1370,7 +1389,9 @@ nodeProcess (BREthereumNode node, case NODE_ROUTE_TCP: // Look for the pending message in some provisioner - for (size_t index = 0; index < array_count (node->provisioners); index++) + for (size_t index = 0; index < array_count (node->provisioners); index++) { + if (provisionerMessageWaitingResposeCount(&node->provisioners[index]) > 0) + break; if (provisionerSendMessagesPending (&node->provisioners[index])) { BREthereumNodeStatus status = provisionerMessageSend(&node->provisioners[index]); switch (status) { @@ -1382,6 +1403,7 @@ nodeProcess (BREthereumNode node, // Only send one at a time - socket might be blocked break; // from for node->provisioners } + } break; } } diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.h index 90735f1ee..305543094 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNode.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/13/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.c b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.c index 9472fde19..91b0fa794 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.c +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/14/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -39,6 +39,7 @@ #define MSG_NOSIGNAL 0 // set to 0 if undefined (BSD has the SO_NOSIGPIPE sockopt, and windows has no signals at all) #endif +#define LES_LOG_SEND_RECV_ERROR /** Forward Declarations */ static int openSocket (BREthereumNodeEndpoint endpoint, int *socket, int port, int domain, int type, double timeout); @@ -143,14 +144,21 @@ nodeEndpointCreateLocal (BREthereumLESRandomContext randomContext) { extern BREthereumNodeEndpoint nodeEndpointCreateEnode (const char *enode) { size_t enodeLen = strlen (enode); - assert (enodeLen < 1024); + if (enodeLen >= 1024) + return NULL; char buffer[1024], *buf = buffer; - assert (1 == sscanf (enode, "enode://%s", buffer)); + + if (1 != sscanf (enode, "enode://%s", buffer)) + return NULL; char *id = strsep (&buf, "@:"); char *ip = strsep (&buf, "@:"); char *pt = strsep (&buf, "@:"); + + if (NULL == id || NULL == ip || NULL == pt) + return NULL; + int port = atoi (pt); BREthereumDISEndpoint disEndpoint = { @@ -271,7 +279,8 @@ nodeEndpointDefineHello (BREthereumNodeEndpoint endpoint, // The NodeID is the 64-byte (uncompressed) public key uint8_t pubKey[65]; - assert (65 == BRKeyPubKey (&endpoint->dis.key, pubKey, 65)); + size_t pubKeyLen = BRKeyPubKey (&endpoint->dis.key, pubKey, 65); + assert (65 == pubKeyLen); memcpy (endpoint->hello.nodeId.u8, &pubKey[1], 64); } @@ -410,14 +419,21 @@ nodeEndpointRecvData (BREthereumNodeEndpoint endpoint, if (socket < 0) error = ENOTCONN; + struct timeval tbegin, tnow; + + gettimeofday(&tbegin, NULL); + while (socket >= 0 && !error && totalCount < *bytesCount) { ssize_t n = recv (socket, &bytes[totalCount], *bytesCount - totalCount, 0); + gettimeofday(&tnow, NULL); if (n == 0) error = ECONNRESET; else if (n < 0 && errno != EWOULDBLOCK) error = errno; + else if (tnow.tv_sec - tbegin.tv_sec > 5) error = ETIMEDOUT; else if (n < 0 && errno == EWOULDBLOCK) continue; else { totalCount += n; if (!needBytesCount) break; + gettimeofday(&tbegin, NULL); } socket = endpoint->sockets[route]; diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.h index ddcd30b67..90a11f5fd 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumNodeEndpoint.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/14/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -63,6 +63,13 @@ nodeEndpointCreate (BREthereumDISNeighbor dis); extern BREthereumNodeEndpoint nodeEndpointCreateLocal (BREthereumLESRandomContext randomContext); +/** + * Create a Node Endpoint from `enode`. If `enode` cannot be parsed then NULL is returned. + * + * @param enode the node endpoint specification + * + * @return a new endpoint or NULL + */ extern BREthereumNodeEndpoint nodeEndpointCreateEnode (const char *enode); diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.c b/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.c index 25ee04787..bf02b1539 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.c +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 9/4/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -112,24 +112,24 @@ provisionCreateMessageLES (BREthereumProvision *provisionMulti, BRArrayOf(uint64_t) messageBlkNumbers; array_new (messageBlkNumbers, messageContentLimit); - BRArrayOf(uint64_t) messageChtNumbers; - array_new (messageChtNumbers, messageContentLimit); + BRArrayOf(uint64_t) messageSectionIdxNumbers; + array_new (messageSectionIdxNumbers, messageContentLimit); size_t numbersOffset = index * messageContentLimit; for (size_t i = 0; i < minimum (messageContentLimit, numbersCount - numbersOffset); i++) { uint64_t blkNumber = numbers[(numbersOffset + i)]; - uint64_t chtNumber = messageLESGetChtNumber(blkNumber); + uint64_t sectionIdx = messageLESGetSectionIdxNumber(blkNumber); array_add (messageBlkNumbers, blkNumber); - array_add (messageChtNumbers, chtNumber); + array_add (messageSectionIdxNumbers, sectionIdx); } return (BREthereumMessage) { MESSAGE_LES, { .les = { - LES_MESSAGE_GET_HEADER_PROOFS, - { .getHeaderProofs = { messageId, messageChtNumbers, messageBlkNumbers }}}} + LES_MESSAGE_GET_HELPER_TRIE_PROOFS, + { .getHelperTrieProofs = { messageId, messageSectionIdxNumbers, messageBlkNumbers }}}} }; } @@ -216,8 +216,8 @@ provisionCreateMessageLES (BREthereumProvision *provisionMulti, return (BREthereumMessage) { MESSAGE_LES, { .les = { - LES_MESSAGE_GET_PROOFS, - { .getProofs = { messageId, specs }}}} + LES_MESSAGE_GET_PROOFS_V2, + { .getProofsV2 = { messageId, specs }}}} }; } @@ -317,7 +317,7 @@ provisionHandleMessageLES (BREthereumProvision *provisionMulti, } case PROVISION_BLOCK_PROOFS: { - assert (LES_MESSAGE_HEADER_PROOFS == message.identifier); + assert (LES_MESSAGE_HELPER_TRIE_PROOFS == message.identifier); BREthereumProvisionProofs *provision = &provisionMulti->u.proofs; BRArrayOf(BREthereumBlockHeaderProof) provisionProofs = provision->proofs; @@ -327,7 +327,7 @@ provisionHandleMessageLES (BREthereumProvision *provisionMulti, BRArrayOf(BREthereumBlockHeader) messageHeaders; BRArrayOf(BREthereumMPTNodePath) messagePaths; - messageLESHeaderProofsConsume (&message.u.headerProofs, &messageHeaders, &messagePaths); + messageLESHelperTrieProofsConsume (&message.u.helperTrieProofs, &messageHeaders, &messagePaths); if (0 == array_count(messageHeaders)) status = PROVISION_ERROR; @@ -420,7 +420,7 @@ provisionHandleMessageLES (BREthereumProvision *provisionMulti, } case PROVISION_ACCOUNTS: { - assert (LES_MESSAGE_PROOFS == message.identifier); + assert (LES_MESSAGE_PROOFS_V2 == message.identifier); BREthereumProvisionAccounts *provision = &provisionMulti->u.accounts; BREthereumHash hash = addressGetHash(provision->address); BREthereumData key = { sizeof(BREthereumHash), hash.bytes }; @@ -431,10 +431,11 @@ provisionHandleMessageLES (BREthereumProvision *provisionMulti, BREthereumProvisionIdentifier identifier = messageLESGetRequestId (&message); BRArrayOf(BREthereumMPTNodePath) messagePaths; - messageLESProofsConsume(&message.u.proofs, &messagePaths); + array_new (messagePaths, 1); + messageLESProofsV2Consume(&message.u.proofsV2, &messagePaths); if (0 == array_count(messagePaths)) - status = PROVISION_ERROR; + status = PROVISION_SUCCESS; else { // We need a coder to RLP decode the proof's RLP data into an AccountState. We could, // and probably should, pass the coder for LES all the way down here. It is a long diff --git a/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.h b/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.h index f72cb9f50..583af196a 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.h +++ b/ThirdParty/breadwallet-core/ethereum/les/BREthereumProvision.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 9/3/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.c b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.c index a9aafb630..7fc998418 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.c +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -100,17 +100,18 @@ endpointDISEncode (const BREthereumDISEndpoint *endpoint, BRRlpCoder coder) { extern BREthereumDISEndpoint endpointDISDecode (BRRlpItem item, BRRlpCoder coder) { - BREthereumDISEndpoint endpoint; + BREthereumDISEndpoint endpoint = {}; // Zero out - we'll be hashing this and don't need random, untouched memory. memset (&endpoint, 0, sizeof (BREthereumDISEndpoint)); size_t itemsCount = 0; const BRRlpItem *items = rlpDecodeList (coder, item, &itemsCount); - assert (3 == itemsCount); + if (3 != itemsCount) { rlpCoderSetFailed (coder); return endpoint; } BRRlpData addrData = rlpDecodeBytesSharedDontRelease (coder, items[0]); - assert (4 == addrData.bytesCount || 16 == addrData.bytesCount); + if (4 != addrData.bytesCount && 16 != addrData.bytesCount) { rlpCoderSetFailed (coder); return endpoint; } + endpoint.domain = (4 == addrData.bytesCount ? AF_INET : AF_INET6); memcpy ((endpoint.domain == AF_INET ? endpoint.addr.ipv4 : endpoint.addr.ipv6), addrData.bytes, @@ -126,26 +127,27 @@ endpointDISDecode (BRRlpItem item, BRRlpCoder coder) { static BREthereumDISNeighbor neighborDISDecode (BRRlpItem item, BREthereumMessageCoder coder) { - BREthereumDISNeighbor neighbor; + BREthereumDISNeighbor neighbor = {}; // Zero out - we'll be hashing this and don't need random, untouched memory. memset (&neighbor, 0, sizeof (BREthereumDISNeighbor)); size_t itemsCount = 0; const BRRlpItem *items = rlpDecodeList (coder.rlp, item, &itemsCount); - assert (4 == itemsCount); + if (4 != itemsCount) { rlpCoderSetFailed (coder.rlp); return neighbor; } // Node ID // Endpoint - Somehow GETH explodes it. BRRlpData addrData = rlpDecodeBytesSharedDontRelease (coder.rlp, items[0]); - assert (4 == addrData.bytesCount || 16 == addrData.bytesCount); + if (4 != addrData.bytesCount && 16 != addrData.bytesCount) { rlpCoderSetFailed (coder.rlp); return neighbor; } + neighbor.node.domain = (4 == addrData.bytesCount ? AF_INET : AF_INET6); memcpy ((neighbor.node.domain == AF_INET ? neighbor.node.addr.ipv4 : neighbor.node.addr.ipv6), addrData.bytes, addrData.bytesCount); BRRlpData nodeIDData = rlpDecodeBytesSharedDontRelease (coder.rlp, items[3]); - assert (64 == nodeIDData.bytesCount); + if (64 != nodeIDData.bytesCount) { rlpCoderSetFailed (coder.rlp); return neighbor; } // Get the 65-byte 0x04-prefaced public key. uint8_t key[65] = { 0x04 }; @@ -390,11 +392,20 @@ messageDISDecode (BRRlpItem item, BREthereumDISMessagePacket *packet = (BREthereumDISMessagePacket*) packetData.bytes; size_t packetSize = sizeof (BREthereumDISMessagePacket); - // TODO: Use packet->hash + packet->signature to validate the packet. - // Get the identifier and then decode the message contents BREthereumDISMessageIdentifier identifier = packet->identifier; + // Perform the most basic validation - just of identifier + if (DIS_MESSAGE_PING != identifier && + DIS_MESSAGE_PONG != identifier && + DIS_MESSAGE_FIND_NEIGHBORS != identifier && + DIS_MESSAGE_NEIGHBORS != identifier) { + rlpCoderHasFailed (coder.rlp); + return (BREthereumDISMessage) { (BREthereumDISMessageIdentifier) NULL }; + } + + // TODO: Use packet->hash + packet->signature to validate the packet. + // Get the rlpItem from packet->data BRRlpData messageData = { packetData.bytesCount - packetSize, &packetData.bytes[packetSize] }; BRRlpItem messageItem = rlpGetItem (coder.rlp, messageData); diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.h b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.h index 03184642c..809eba3b6 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.h +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageDIS.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageETH.h b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageETH.h index 6d73e73d7..e4f5581e3 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageETH.h +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageETH.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.c b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.c index 3d8640367..0bd4dd976 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.c +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -47,7 +47,9 @@ BREthereumLESMessageSpec messageLESSpecs [NUMBER_OF_LES_MESSAGE_IDENTIFIERS] = { { "HelperTrieProofs", LES_MESSAGE_USE_RESPONSE }, { "SendTx2", LES_MESSAGE_USE_REQUEST, 64 }, { "GetTxStatus", LES_MESSAGE_USE_REQUEST, 256 }, - { "TxStatus", LES_MESSAGE_USE_RESPONSE } + { "TxStatus", LES_MESSAGE_USE_RESPONSE }, + { "Stop", LES_MESSAGE_USE_STATUS }, + { "Resume", LES_MESSAGE_USE_STATUS } }; extern const char * messageLESGetIdentifierName (BREthereumLESMessageIdentifier identifer) { @@ -187,6 +189,7 @@ messageLESAnnounceDecode (BRRlpItem item, message.headNumber = rlpDecodeUInt64 (coder.rlp, items[1], 1); message.headTotalDifficulty = rlpDecodeUInt256 (coder.rlp, items[2], 1); message.reorgDepth = rlpDecodeUInt64 (coder.rlp, items[3], 1); + message.pairs = NULL; // TODO: Decode Keys // if (5 == itemsCount) { @@ -534,7 +537,7 @@ extern void messageLESHeaderProofsConsume (BREthereumLESMessageHeaderProofs *message, BRArrayOf(BREthereumBlockHeader) *headers, BRArrayOf(BREthereumMPTNodePath) *paths) { - if (NULL != headers) { *headers = message->headers; message->headers = NULL; }; + if (NULL != headers) { *headers = message->headers; message->headers = NULL; } if (NULL != paths) { *paths = message->paths; message->paths = NULL; } } @@ -560,7 +563,6 @@ messageLESProofsV2Decode (BRRlpItem item, uint64_t reqId = rlpDecodeUInt64 (coder.rlp, items[0], 1); uint64_t bv = rlpDecodeUInt64 (coder.rlp, items[1], 1); - // TODO: See GetProofs - should be an array of PATHS return (BREthereumLESMessageProofsV2) { reqId, bv, @@ -568,10 +570,100 @@ messageLESProofsV2Decode (BRRlpItem item, }; } +extern void +messageLESProofsV2Consume (BREthereumLESMessageProofsV2 *message, + BRArrayOf(BREthereumMPTNodePath) *paths) { + if (paths != NULL) { array_add(*paths, message->path); message->path = NULL; } +} + /// MARK: LES GetHelperTrieProofs +static BRRlpItem +helperTrieProofNumbersEncodeList (BRArrayOf(uint64_t) sectionIdx, + BRArrayOf(uint64_t) blkNumbers, + BREthereumMessageCoder coder) { + size_t itemsCount = array_count(blkNumbers); + BRRlpItem items[itemsCount]; + + for (size_t index = 0; index < itemsCount; index++) { + uint8_t blkNumberBytes[8]; + UInt64SetBE(blkNumberBytes, blkNumbers[index]); + items[index] = rlpEncodeList (coder.rlp, 5, + rlpEncodeUInt64 (coder.rlp, 0, 1), + rlpEncodeUInt64 (coder.rlp, sectionIdx[index], 1), + rlpEncodeBytes(coder.rlp, blkNumberBytes, sizeof(blkNumberBytes)), + rlpEncodeUInt64 (coder.rlp, 0, 1), // level + rlpEncodeUInt64 (coder.rlp, 2, 1)); // auxReq + } + + return rlpEncodeListItems (coder.rlp, items, itemsCount); +} + +static BRRlpItem +messageLESGetHelperTrieProofsEncode (BREthereumLESMessageGetHelperTrieProofs message, BREthereumMessageCoder coder) { + // [[subType: P, sectionIdx: P, key: B, fromLevel: P, auxReq: P], ...] + return rlpEncodeList2 (coder.rlp, + rlpEncodeUInt64 (coder.rlp, message.reqId, 1), + helperTrieProofNumbersEncodeList (message.sectionIdx, message.blkNumbers, coder)); +} + /// MARK: LES HelperTrieProofs +static void helperTrieProofsDecode (BRRlpItem item, BREthereumMessageCoder coder, + BRArrayOf(BREthereumBlockHeader) *headers, + BRArrayOf(BREthereumMPTNodePath) *paths) { + size_t itemsCount = 0; + const BRRlpItem *items = rlpDecodeList (coder.rlp, item, &itemsCount); + assert (2 == itemsCount); + + array_new (*headers, 1); + array_new (*paths, 1); + + BREthereumMPTNodePath path = mptNodePathDecode (items[0], coder.rlp); + + size_t headerItemsCount = 0; + const BRRlpItem *headerItems = rlpDecodeList (coder.rlp, items[1], &headerItemsCount); + assert (1 == headerItemsCount); + + BRRlpData headerData = rlpDecodeBytes (coder.rlp, headerItems[0]); + BRRlpItem headerItem = rlpGetItem(coder.rlp, headerData); + + BREthereumBlockHeader header = blockHeaderRlpDecode (headerItem, RLP_TYPE_NETWORK, coder.rlp); + + array_add (*headers, header); + array_add (*paths, path); +} + +static BREthereumLESMessageHelperTrieProofs +messageLESHelperTrieProofsDecode (BRRlpItem item, BREthereumMessageCoder coder) { + size_t itemsCount = 0; + const BRRlpItem *items = rlpDecodeList(coder.rlp, item, &itemsCount); + assert (3 == itemsCount); + + uint64_t reqId = rlpDecodeUInt64 (coder.rlp, items[0], 1); + uint64_t bv = rlpDecodeUInt64 (coder.rlp, items[1], 1); + + BRArrayOf(BREthereumBlockHeader) headers; + BRArrayOf(BREthereumMPTNodePath) paths; + + helperTrieProofsDecode (items[2], coder, &headers, &paths); + + return (BREthereumLESMessageHelperTrieProofs) { + reqId, + bv, + headers, + paths + }; +} + +extern void +messageLESHelperTrieProofsConsume (BREthereumLESMessageHelperTrieProofs *message, + BRArrayOf(BREthereumBlockHeader) *headers, + BRArrayOf(BREthereumMPTNodePath) *paths) { + if (NULL != headers) { *headers = message->headers; message->headers = NULL; } + if (NULL != paths) { *paths = message->paths; message->paths = NULL; } +} + /// MARK: LES SendTx2 static BRRlpItem @@ -607,6 +699,20 @@ messageLESTxStatusConsume (BREthereumLESMessageTxStatus *message, if (NULL != stati) { *stati = message->stati; message->stati = NULL; } } +/// MARK: LES Stop +static BREthereumLESMessageStop messageLESStopDecode (BRRlpItem item, BREthereumMessageCoder coder) { + + return (BREthereumLESMessageStop) {}; +} + +/// MARK: LES Resume +static BREthereumLESMessageResume messageLESResumeDecode(BRRlpItem item, BREthereumMessageCoder coder) { + uint64_t bv = rlpDecodeUInt64 (coder.rlp, item, 1); + return (BREthereumLESMessageResume) { + bv + }; +} + /** * When decoding a LES TxStatus message, the 'error' field has been observed to start with these * strings. We'll map these (based on the array index) to a enumeration of status errors. @@ -692,11 +798,26 @@ messageLESDecode (BRRlpItem item, LES_MESSAGE_PROOFS_V2, { .proofsV2 = messageLESProofsV2Decode (item, coder)} }; + case LES_MESSAGE_HELPER_TRIE_PROOFS: + return (BREthereumLESMessage) { + LES_MESSAGE_HELPER_TRIE_PROOFS, + {.helperTrieProofs = messageLESHelperTrieProofsDecode(item, coder)} }; + case LES_MESSAGE_TX_STATUS: return (BREthereumLESMessage) { LES_MESSAGE_TX_STATUS, { .txStatus = messageLESTxStatusDecode (item, coder)} }; + case LES_MESSAGE_STOP: + return (BREthereumLESMessage) { + LES_MESSAGE_STOP, + { .stop = messageLESStopDecode(item, coder) } }; + + case LES_MESSAGE_RESUME: + return (BREthereumLESMessage) { + LES_MESSAGE_RESUME, + {.resume = messageLESResumeDecode(item, coder)} }; + default: BRFail(); } @@ -748,6 +869,10 @@ messageLESEncode (BREthereumLESMessage message, //rlpShowItem (coder.rlp, body, "LES GetProofsV2"); break; + case LES_MESSAGE_GET_HELPER_TRIE_PROOFS: + body = messageLESGetHelperTrieProofsEncode(message.u.getHelperTrieProofs, coder); + break; + case LES_MESSAGE_GET_TX_STATUS: body = messageLESGetTxStatusEncode (message.u.getTxStatus, coder); break; @@ -867,6 +992,12 @@ messageLESRelease (BREthereumLESMessage *message) { if (NULL != message->u.txStatus.stati) array_free (message->u.txStatus.stati); break; + + case LES_MESSAGE_STOP: + break; + + case LES_MESSAGE_RESUME: + break; } } @@ -937,6 +1068,8 @@ messageLESGetRequestIdInternal (const BREthereumLESMessage *message) { case LES_MESSAGE_SEND_TX2: return message->u.sendTx2.reqId; case LES_MESSAGE_GET_TX_STATUS: return message->u.getTxStatus.reqId; case LES_MESSAGE_TX_STATUS: return message->u.txStatus.reqId; + case LES_MESSAGE_STOP: return LES_MESSAGE_NO_REQUEST_ID; + case LES_MESSAGE_RESUME: return LES_MESSAGE_NO_REQUEST_ID; } } @@ -955,3 +1088,8 @@ messageLESGetChtNumber (uint64_t blkNumber) { assert (0 != blkNumber); return 1 + blkNumber / LES_MESSAGE_CHT_PERIOD; } + +extern uint64_t messageLESGetSectionIdxNumber (uint64_t blkNumber) { + assert (0 != blkNumber); + return 1 + blkNumber / 32768; +} diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.h b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.h index 60ea381ba..fb236ab09 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.h +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageLES.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -47,9 +47,11 @@ typedef enum { LES_MESSAGE_SEND_TX2 = 0x13, LES_MESSAGE_GET_TX_STATUS = 0x14, LES_MESSAGE_TX_STATUS = 0x15, + LES_MESSAGE_STOP = 0x16, + LES_MESSAGE_RESUME = 0x17, } BREthereumLESMessageIdentifier; -#define NUMBER_OF_LES_MESSAGE_IDENTIFIERS (LES_MESSAGE_TX_STATUS + 1) +#define NUMBER_OF_LES_MESSAGE_IDENTIFIERS (LES_MESSAGE_RESUME + 1) extern const char * messageLESGetIdentifierName (BREthereumLESMessageIdentifier id); @@ -287,7 +289,7 @@ typedef struct { typedef struct { uint64_t reqId; BRArrayOf(uint64_t) chtNumbers; - BRArrayOf(uint64_t) blkNumbers;; + BRArrayOf(uint64_t) blkNumbers; } BREthereumLESMessageGetHeaderProofs; /// MARK: LES HeaderProofs @@ -322,20 +324,38 @@ typedef struct { BREthereumMPTNodePath path; } BREthereumLESMessageProofsV2; +extern void +messageLESProofsV2Consume (BREthereumLESMessageProofsV2 *message, + BRArrayOf(BREthereumMPTNodePath) *paths); /// MARK: LES GetHelperTrieProofs + /** + * [reqID: P, [[subType: P, sectionIdx: P, key: B, fromLevel: P, auxReq: P], ...]] + */ typedef struct { uint64_t reqId; + BRArrayOf(uint64_t) sectionIdx; + BRArrayOf(uint64_t) blkNumbers; } BREthereumLESMessageGetHelperTrieProofs; /// MARK: LES HelperTrieProofs - + + /** + * [reqID: P, BV: P, [[node_1, node_2...], [auxData_0, auxData_1, ...]]] + */ typedef struct { uint64_t reqId; uint64_t bv; + BRArrayOf(BREthereumBlockHeader) headers; + BRArrayOf(BREthereumMPTNodePath) paths; } BREthereumLESMessageHelperTrieProofs; +extern void +messageLESHelperTrieProofsConsume (BREthereumLESMessageHelperTrieProofs *message, + BRArrayOf(BREthereumBlockHeader) *headers, + BRArrayOf(BREthereumMPTNodePath) *paths); + /// MARK: LES SendTx2 /** @@ -374,6 +394,19 @@ extern void messageLESTxStatusConsume (BREthereumLESMessageTxStatus *message, BRArrayOf(BREthereumTransactionStatus) *stati); +/// MARK: LES Stop + +/** + * + */ +typedef struct { +} BREthereumLESMessageStop; + +/// MARK: LES Resume +typedef struct { + uint64_t bv; +} BREthereumLESMessageResume; + // // ... // @@ -435,6 +468,8 @@ typedef struct { BREthereumLESMessageSendTx2 sendTx2; BREthereumLESMessageGetTxStatus getTxStatus; BREthereumLESMessageTxStatus txStatus; + BREthereumLESMessageStop stop; + BREthereumLESMessageResume resume; } u; } BREthereumLESMessage; @@ -483,6 +518,8 @@ messageLESGetRequestId (const BREthereumLESMessage *message); extern uint64_t messageLESGetChtNumber (uint64_t blkNumber); +extern uint64_t messageLESGetSectionIdxNumber (uint64_t blkNumber); + extern const char *lesTransactionErrorPreface[]; // extern int diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.c b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.c index 2f366a291..34610ad3e 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.c +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.h b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.h index 06ace271d..b8e1c28db 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.h +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessageP2P.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.c b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.c index 998ce9ebe..e29671908 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.c +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.h b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.h index b17485792..1cbabafc2 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.h +++ b/ThirdParty/breadwallet-core/ethereum/les/msg/BREthereumMessagePIP.h @@ -3,7 +3,7 @@ // BRCore // // Created by Ed Gamble on 9/1/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/les/testLES.c b/ThirdParty/breadwallet-core/ethereum/les/testLES.c index 843344368..f8abb0f02 100644 --- a/ThirdParty/breadwallet-core/ethereum/les/testLES.c +++ b/ThirdParty/breadwallet-core/ethereum/les/testLES.c @@ -1,9 +1,9 @@ // // test-les.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Lamont Samuels on 4/16/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,9 +28,9 @@ #include "support/BRCrypto.h" #include "ethereum/base/BREthereumHash.h" #include "ethereum/blockchain/BREthereumNetwork.h" -#include "BREthereumLESRandom.h" -#include "BREthereumLES.h" -#include "BREthereumNode.h" +#include "ethereum/les/BREthereumLESRandom.h" +#include "ethereum/les/BREthereumLES.h" +#include "ethereum/les/BREthereumNode.h" #include "ethereum/BREthereum.h" @@ -43,6 +44,23 @@ #define LES_LOCAL_ENDPOINT_UDP_PORT DEFAULT_UDPPORT #define LES_LOCAL_ENDPOINT_NAME "BRD Light Client" +/// MARK: - Helpers + +static int _pthread_cond_timedwait_relative (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *reltime) { +#if defined(__ANDROID__) + struct timeval t; + gettimeofday(&t, NULL); + + struct timespec timeout; + timeout.tv_sec = reltime->tv_sec + t.tv_sec; + timeout.tv_nsec = reltime->tv_nsec +t.tv_usec*1000; + + return pthread_cond_timedwait (cond, mutex, &timeout); +#else + return pthread_cond_timedwait_relative_np (cond, mutex, reltime); +#endif +} + /// MARK: - Node Test #pragma clang diagnostic push @@ -100,7 +118,7 @@ static void _waitForTests() { pthread_mutex_lock(&_testLock); //Wait until all the tests are complete while(_testComplete < _numOfTests){ - pthread_cond_timedwait_relative_np (&_testCond, &_testLock, &wait); + _pthread_cond_timedwait_relative (&_testCond, &_testLock, &wait); } pthread_mutex_unlock(&_testLock); } @@ -1179,7 +1197,7 @@ runLESTests (const char *paperKey) { // Run Tests on the LES messages run_GetBlockHeaders_Tests(les); - run_GetBlookProofs_Tests(les); +// run_GetBlookProofs_Tests(les); run_GetBlockBodies_Tests(les); run_GetReceipts_Tests(les); run_SendTransaction_Tests(les, paperKey); diff --git a/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.c b/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.c index 33cc3de45..2a562b833 100644 --- a/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.c +++ b/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.c @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/21/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.h b/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.h index a8e66055b..876c87f37 100644 --- a/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.h +++ b/ThirdParty/breadwallet-core/ethereum/mpt/BREthereumMPT.h @@ -3,7 +3,7 @@ // Core // // Created by Ed Gamble on 8/21/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/rlp/BRRlp.h b/ThirdParty/breadwallet-core/ethereum/rlp/BRRlp.h index ce50a7d41..ffd88cda3 100644 --- a/ThirdParty/breadwallet-core/ethereum/rlp/BRRlp.h +++ b/ThirdParty/breadwallet-core/ethereum/rlp/BRRlp.h @@ -1,9 +1,9 @@ // // BRRlp -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/25/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.c b/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.c index e265d347c..175778c83 100644 --- a/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.c +++ b/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.c @@ -1,9 +1,9 @@ // // rlp -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/25/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -16,9 +16,6 @@ #include "ethereum/util/BRUtil.h" #include "BRRlpCoder.h" -static void -rlpCoderReclaimInternal (BRRlpCoder coder) ; - static int rlpDecodeStringEmptyCheck (BRRlpCoder coder, BRRlpItem item); @@ -84,7 +81,7 @@ struct BRRlpCoderRecord { * acquire an item and you best be sure to release it and if you don't you've leaked memory. * * Well, in order to ensure that memory is not leaked, we keep a `busy` list and then on - * `rlpCoderRelease()` we assert that there a no busy items - ensuring all are releaded. + * `rlpCoderRelease()` we assert that there a no busy items - ensuring all are released. * * This busy list caused a problem (see bugfix/CORE-152). It was singly linked and thus to * remove an item we were forced to traverse the list to find the just-prior item and then @@ -125,21 +122,8 @@ rlpCoderCreate (void) { return coder; } -extern void -rlpCoderRelease (BRRlpCoder coder) { - pthread_mutex_lock(&coder->lock); - - // Every single Item must be returned! - assert (NULL == coder->busy); - rlpCoderReclaimInternal (coder); - - pthread_mutex_unlock(&coder->lock); - pthread_mutex_destroy(&coder->lock); - free (coder); -} - static void -rlpCoderReclaimInternal (BRRlpCoder coder) { +_rlpCoderReclaimInternal (BRRlpCoder coder) { BRRlpItem item = coder->free; while (item != NULL) { BRRlpItem next = item->next; // save 'next' before release... @@ -153,22 +137,26 @@ rlpCoderReclaimInternal (BRRlpCoder coder) { extern void rlpCoderReclaim (BRRlpCoder coder) { pthread_mutex_lock(&coder->lock); - rlpCoderReclaimInternal (coder); + _rlpCoderReclaimInternal (coder); pthread_mutex_unlock(&coder->lock); } -extern int -rlpCoderBusyCount (BRRlpCoder coder) { - int count = 0; - for (BRRlpItem found = coder->busy; NULL != found; found = found->next) - count++; - return count; +extern void +rlpCoderRelease (BRRlpCoder coder) { + pthread_mutex_lock(&coder->lock); + + // Every single Item must be returned! + assert (NULL == coder->busy); + _rlpCoderReclaimInternal (coder); + + pthread_mutex_unlock(&coder->lock); + pthread_mutex_destroy(&coder->lock); + free (coder); } static BRRlpItem -rlpCoderAcquireItem (BRRlpCoder coder) { +_rlpCoderAcquireItemInternal (BRRlpCoder coder) { BRRlpItem item = NULL; - pthread_mutex_lock(&coder->lock); // Get `item` from `coder->free` or `calloc` if (NULL != coder->free) { @@ -188,13 +176,19 @@ rlpCoderAcquireItem (BRRlpCoder coder) { // Update `coder` to show `item` as busy. coder->busy = item; + return item; +} + +static BRRlpItem +rlpCoderAcquireItem (BRRlpCoder coder) { + pthread_mutex_lock(&coder->lock); + BRRlpItem item = _rlpCoderAcquireItemInternal (coder); pthread_mutex_unlock(&coder->lock); return item; } static void -rlpCoderReturnItem (BRRlpCoder coder, BRRlpItem prev, BRRlpItem item, BRRlpItem next) { - pthread_mutex_lock(&coder->lock); +_rlpCoderReturnItemInternal (BRRlpCoder coder, BRRlpItem prev, BRRlpItem item, BRRlpItem next) { assert (NULL == item->next && NULL == item->prev && 0 == item->bytesCount && 0 == item->itemsCount); @@ -215,17 +209,26 @@ rlpCoderReturnItem (BRRlpCoder coder, BRRlpItem prev, BRRlpItem item, BRRlpItem // Update `coder` to show `item` as free. coder->free = item; - - pthread_mutex_unlock(&coder->lock); } static void -itemRelease (BRRlpCoder coder, BRRlpItem item) { +_rlpCoderReleaseItemInternal (BRRlpCoder coder, BRRlpItem item) { + for (size_t index = 0; index < item->itemsCount; index++) + _rlpCoderReleaseItemInternal (coder, item->items[index]); + + // Surely get these before itemReleaseMemory() blows them away. BRRlpItem prev = item->prev; BRRlpItem next = item->next; itemReleaseMemory(item); - rlpCoderReturnItem(coder, prev, item, next); + _rlpCoderReturnItemInternal (coder, prev, item, next); +} + +static void +rlpCoderReleaseItem (BRRlpCoder coder, BRRlpItem item) { + pthread_mutex_lock(&coder->lock); + _rlpCoderReleaseItemInternal (coder, item); + pthread_mutex_unlock(&coder->lock); } static int @@ -277,84 +280,6 @@ rlpCoderHasFailed (BRRlpCoder coder) { return coder->failed; } -#if 0 -static BRRlpItem -itemCreate (BRRlpCoder coder, - uint8_t *bytes, size_t bytesCount, int takeBytes) { - BRRlpItem item = calloc (1, sizeof (struct BRRlpItemRecord)); - - item->type = CODER_ITEM; - item->bytesCount = bytesCount; - if (takeBytes) - item->bytes = bytes; - else { - item->bytes = (item->bytesCount > ITEM_DEFAULT_BYTES_COUNT - ? malloc (item->bytesCount) - : item->bytesArray); - memcpy (item->bytes, bytes, item->bytesCount); - } - item->itemsCount = 0; - item->items = NULL; - - return item; -} - -static BRRlpItem -itemCreateList (BRRlpCoder coder, - uint8_t *bytes, size_t bytesCount, int takeBytes, - BRRlpItem *items, size_t itemsCount) { - BRRlpItem item = itemCreate(coder, bytes, bytesCount, takeBytes); - item->type = CODER_LIST; - item->itemsCount = itemsCount; - item->items = (item->itemsCount > ITEM_DEFAULT_ITEMS_COUNT - ? calloc (item->itemsCount, sizeof (BRRlpItem)) - : item->itemsArray); - for (int i = 0; i < itemsCount; i++) - item->items[i] = items[i]; - - return item; -} -#endif - -/** - * Return a new BRRlpContext by appending the two provided contexts. Both provided contexts - * must be for CODER_ITEM (othewise an 'assert' is raised); the appending is performed by simply - * concatenating the two context's byte arrays. - * - * If release is TRUE, then both the provided contexts are released; thereby freeing their memory. - * - */ -#if 0 -static BRRlpItem -itemCreateAppend (BRRlpCoder coder, BRRlpItem context1, BRRlpItem context2, int release) { - assert (CODER_ITEM == context1->type && CODER_ITEM == context2->type); - - BRRlpItem item = calloc (1, sizeof (struct BRRlpItemRecord)); - - item->type = CODER_ITEM; - - item->bytesCount = context1->bytesCount + context2->bytesCount; - item->bytes = (item->bytesCount > ITEM_DEFAULT_BYTES_COUNT - ? malloc (item->bytesCount) - : item->bytesArray); - - memcpy (&item->bytes[0], context1->bytes, context1->bytesCount); - memcpy (&item->bytes[context1->bytesCount], context2->bytes, context2->bytesCount); - - item->itemsCount = 0; - item->items = NULL; - - if (release) { - // assert (context2.bytes != context1.bytes || context2.bytes == NULL || context1.bytes == NULL); - // assert (context2.items != context1.items || context2.items == NULL || context1.items == NULL); - itemRelease(coder, context1); - itemRelease(coder, context2); - } - - return item; -} -#endif - // The largest number supported for encoding is a UInt256 - which is representable as 32 bytes. #define CODER_NUMBER_BYTES_LIMIT (256/8) @@ -685,6 +610,27 @@ rlpDecodeUInt256(BRRlpCoder coder, BRRlpItem item, int zeroAsEmptyString) { : coderDecodeUInt256(coder, item)); } +// +// Double +// +extern BRRlpItem +rlpEncodeDouble(BRRlpCoder coder, double value) { + char strDouble[65]; + snprintf(strDouble, 64, "%lf", value); + return rlpEncodeString (coder, strDouble); +} + +extern double +rlpDecodeDouble(BRRlpCoder coder, BRRlpItem item) { + char *strDouble = rlpDecodeString (coder, item); + + double value; + sscanf (strDouble, "%lf", &value); + free (strDouble); + + return value; +} + // // Bytes // @@ -745,7 +691,7 @@ rlpDecodeListSharedDontRelease (BRRlpCoder coder, BRRlpItem item) { // String // extern BRRlpItem -rlpEncodeString (BRRlpCoder coder, char *string) { +rlpEncodeString (BRRlpCoder coder, const char *string) { if (NULL == string) string = ""; return rlpEncodeBytes(coder, (uint8_t *) string, strlen (string)); } @@ -785,7 +731,7 @@ rlpDecodeStringEmptyCheck (BRRlpCoder coder, BRRlpItem item) { // Hex String // extern BRRlpItem -rlpEncodeHexString (BRRlpCoder coder, char *string) { +rlpEncodeHexString (BRRlpCoder coder, const char *string) { if (NULL == string || string[0] == '\0') return rlpEncodeString(coder, ""); @@ -1071,9 +1017,7 @@ rlpShowItemInternal (BRRlpCoder coder, BRRlpItem context, const char *topic, int extern void rlpReleaseItem (BRRlpCoder coder, BRRlpItem item) { assert (itemIsValid(coder, item)); - for (size_t index = 0; index < item->itemsCount; index++) - rlpReleaseItem(coder, item->items[index]); - itemRelease(coder, item); + rlpCoderReleaseItem (coder, item); } extern void diff --git a/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.h b/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.h index 5b675bf09..7dcf07fa6 100644 --- a/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.h +++ b/ThirdParty/breadwallet-core/ethereum/rlp/BRRlpCoder.h @@ -1,9 +1,9 @@ // // rlp -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/25/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -120,6 +120,15 @@ rlpEncodeUInt256(BRRlpCoder coder, UInt256 value, int zeroAsEmptyString); extern UInt256 rlpDecodeUInt256(BRRlpCoder coder, BRRlpItem item, int zeroAsEmptyString); +// +// Double +// +extern BRRlpItem +rlpEncodeDouble(BRRlpCoder coder, double value); + +extern double +rlpDecodeDouble(BRRlpCoder coder, BRRlpItem item); + // // Bytes // @@ -147,7 +156,7 @@ rlpDecodeListSharedDontRelease (BRRlpCoder coder, BRRlpItem item); // String // extern BRRlpItem -rlpEncodeString (BRRlpCoder coder, char *string); +rlpEncodeString (BRRlpCoder coder, const char *string); extern char * rlpDecodeString (BRRlpCoder coder, BRRlpItem item); @@ -159,7 +168,7 @@ rlpDecodeStringCheck (BRRlpCoder coder, BRRlpItem item); // Hex String // extern BRRlpItem -rlpEncodeHexString (BRRlpCoder coder, char *string); +rlpEncodeHexString (BRRlpCoder coder, const char *string); extern char * rlpDecodeHexString (BRRlpCoder coder, BRRlpItem item, const char *prefix); diff --git a/ThirdParty/breadwallet-core/ethereum/rlp/testRlp.c b/ThirdParty/breadwallet-core/ethereum/rlp/testRlp.c index 683dd5c4a..475d811e3 100644 --- a/ThirdParty/breadwallet-core/ethereum/rlp/testRlp.c +++ b/ThirdParty/breadwallet-core/ethereum/rlp/testRlp.c @@ -3,15 +3,17 @@ // CoreTests // // Created by Ed Gamble on 7/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #include #include #include #include #include "ethereum/util/BRUtil.h" -#include "BRRlp.h" +#include "ethereum/rlp/BRRlp.h" static void showHex (uint8_t *source, size_t sourceLen) { diff --git a/ThirdParty/breadwallet-core/ethereum/test.c b/ThirdParty/breadwallet-core/ethereum/test.c index 53adc4d67..f29402201 100644 --- a/ThirdParty/breadwallet-core/ethereum/test.c +++ b/ThirdParty/breadwallet-core/ethereum/test.c @@ -1,10 +1,10 @@ // // test -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 2/27/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -29,12 +29,15 @@ #include "ethereum/ewm/BREthereumAccount.h" #include "ethereum/ewm/BREthereumWallet.h" #include "ethereum/ewm/BREthereumTransfer.h" -#include "BREthereum.h" +#include "ethereum/BREthereum.h" -#include "test.h" +#include "ethereum/test.h" extern const char *tokenBRDAddress; +extern BREthereumToken +tokenLookupTest (const char *address); + // // Ether & Token Parse // @@ -260,7 +263,7 @@ void runTransactionTests3 (BREthereumAccount account, BREthereumNetwork network) printf (" TEST 3\n"); BRCoreParseStatus status; - BREthereumToken token = tokenLookup(tokenBRDAddress); + BREthereumToken token = tokenLookupTest (tokenBRDAddress); BREthereumWallet wallet = walletCreateHoldingToken (account, network, token); UInt256 value = createUInt256Parse ("5968770000000000000000", 10, &status); BREthereumAmount amount = amountCreateToken(createTokenQuantity (token, value)); @@ -439,7 +442,7 @@ void testTransactionCodingEther () { void testTransactionCodingToken () { printf (" Coding Transaction\n"); - BREthereumToken token = tokenLookup(tokenBRDAddress); + BREthereumToken token = tokenLookupTest(tokenBRDAddress); BREthereumAccount account = createAccount (NODE_PAPER_KEY); BREthereumWallet wallet = walletCreateHoldingToken(account, ethereumMainnet, token); diff --git a/ThirdParty/breadwallet-core/ethereum/test.h b/ThirdParty/breadwallet-core/ethereum/test.h index 84dd9dce2..d304e966e 100644 --- a/ThirdParty/breadwallet-core/ethereum/test.h +++ b/ThirdParty/breadwallet-core/ethereum/test.h @@ -47,7 +47,7 @@ runEWMTests (const char *paperKey, extern void runSyncTest (BREthereumNetwork network, BREthereumAccount account, - BREthereumMode mode, + BRCryptoSyncMode mode, BREthereumTimestamp accountTimestamp, unsigned int durationInSeconds, const char *storagePath); diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.c b/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.c index 4a9810fee..e813c2ed4 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.c +++ b/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.c @@ -1,9 +1,9 @@ // // BRKeccak.c -// breadwallet-core Ethereum +// Core Ethereum // Created by Lamont Samuels on 7/19/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // *****LICENSE DISCLAIMER***** // The original code was written by Andrey Jivsov diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.h b/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.h index f53ffb4d5..5bc3a5a86 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.h +++ b/ThirdParty/breadwallet-core/ethereum/util/BRKeccak.h @@ -1,9 +1,9 @@ // // BRKeccak.h -// breadwallet-core Ethereum +// Core Ethereum // Created by Lamont Samuels on 7/19/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // *****LICENSE DISCLAIMER***** // The original code was written by Andrey Jivsov diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRUtil.h b/ThirdParty/breadwallet-core/ethereum/util/BRUtil.h index 359188a61..438f96a2c 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRUtil.h +++ b/ThirdParty/breadwallet-core/ethereum/util/BRUtil.h @@ -1,9 +1,9 @@ // // BBRUtil.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/16/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -19,15 +19,55 @@ #else #ifdef __cplusplus -extern "C" void ElastosElaWalletLog(const char *s); +extern "C" void ElastosElaWalletLogDebug(const char *s); +extern "C" void ElastosElaWalletLogInfo(const char *s); +extern "C" void ElastosElaWalletLogWarn(const char *s); +extern "C" void ElastosElaWalletLogError(const char *s); +extern "C" void ElastosElaWalletLogCritical(const char *s); #else -void ElastosElaWalletLog(const char *s); +void ElastosElaWalletLogDebug(const char *s); +void ElastosElaWalletLogInfo(const char *s); +void ElastosElaWalletLogWarn(const char *s); +void ElastosElaWalletLogError(const char *s); +void ElastosElaWalletLogCritical(const char *s); #endif -#define eth_log(topic, formatter, ...) do { \ +#define __va_first(first, ...) first +#define __va_rest(first, ...) __VA_ARGS__ + +#define eth_log(topic, ...) do { \ + char buf[2048]; \ + snprintf(buf, sizeof(buf), "ETH: %s: " __va_first(__VA_ARGS__, NULL), (topic), __va_rest(__VA_ARGS__, NULL)); \ + buf[sizeof(buf) - 1] = '\0'; \ + ElastosElaWalletLogInfo(buf); \ +} while (0) + +#define eth_log_dbg(topic, formatter, ...) do { \ + char buf[2048]; \ + snprintf(buf, sizeof(buf), "ETH: %s: " formatter, (topic), __VA_ARGS__); \ + buf[sizeof(buf) - 1] = '\0'; \ + ElastosElaWalletLogDebug(buf); \ +} while (0) + +#define eth_log_info(topic, formatter, ...) do { \ + char buf[2048]; \ + snprintf(buf, sizeof(buf), "ETH: %s: " formatter, (topic), __VA_ARGS__); \ + buf[sizeof(buf) - 1] = '\0'; \ + ElastosElaWalletLogInfo(buf); \ +} while (0) + +#define eth_log_warn(topic, formatter, ...) do { \ + char buf[2048]; \ + snprintf(buf, sizeof(buf), "ETH: %s: " formatter, (topic), __VA_ARGS__); \ + buf[sizeof(buf) - 1] = '\0'; \ + ElastosElaWalletLogWarn(buf); \ +} while (0) + +#define eth_log_err(topic, formatter, ...) do { \ char buf[2048]; \ snprintf(buf, sizeof(buf), "ETH: %s: " formatter, (topic), __VA_ARGS__); \ - ElastosElaWalletLog(buf); \ + buf[sizeof(buf) - 1] = '\0'; \ + ElastosElaWalletLogError(buf); \ } while (0) #endif diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.c b/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.c index 37f725153..2a5cf6676 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.c +++ b/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.c @@ -1,9 +1,9 @@ // // BBRUtilHex.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/10/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.h b/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.h index 3a8c6f0fd..39fa933fb 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.h +++ b/ThirdParty/breadwallet-core/ethereum/util/BRUtilHex.h @@ -1,9 +1,9 @@ // // BBRUtilHex.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/10/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.c b/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.c index 0e7fbb75f..3b1dc8b20 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.c +++ b/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.c @@ -1,9 +1,9 @@ // // BBRUtilMath.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/10/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -22,6 +22,22 @@ createUInt256 (uint64_t value) { return result; } +extern UInt256 +createUInt256Double (double value, int decimals, int *overflow) { + assert (NULL != overflow); + + UInt256 result = UINT256_ZERO; + long double y = fabs(value) * powl (10.0, decimals); + + y = roundl(y); // (extraneous) Axe any fraction; can't participate in a UInt + + for (size_t index = 0; index < 4; index++) + result.u64[index] = UINT64_MAX * modfl(y/UINT64_MAX, &y); + *overflow = (y != 0); + + return *overflow ? UINT256_ZERO : result; +} + extern UInt256 createUInt256Power (uint8_t digits, int *overflow) { if (digits < 20) { // 10^19 fits in uint64_t @@ -274,3 +290,21 @@ coerceUInt64 (UInt256 value, int *overflow) { return *overflow ? 0 : value.u64[0]; } +extern double +coerceDouble (UInt256 value, int *overflow) { + long double result = coerceLongDouble (value, overflow); + if (!*overflow) + *overflow = !isfinite ((double) result); + return (double) result; +} + +extern long double +coerceLongDouble (UInt256 value, int *overflow) { + long double scale = powl ((long double) 2.0, (long double) 64.0); + long double result = 0; + for (ssize_t index = 3; index >= 0; index--) + result = ((long double) value.u64[index] + scale * result); + + *overflow = !isfinite(result); + return result; +} diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.h b/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.h index 83b24c589..b2eaff392 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.h +++ b/ThirdParty/breadwallet-core/ethereum/util/BRUtilMath.h @@ -1,9 +1,9 @@ // // BBRUtilMath.h -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/10/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -36,6 +36,9 @@ typedef enum { extern UInt256 createUInt256 (uint64_t value); +extern UInt256 +createUInt256Double (double value, int decimals, int *overflow); + /** * Create as `(expt 10 power)` where power < 20 is required. */ @@ -50,11 +53,12 @@ createUInt256Power2 (uint8_t power); /** * Create from a string in the provided base. The string must consist of only characters - * in the base. That is, avoid the '0x' prefix. No decimal points; this is an integer parse. + * in the base and optionally the '0x' prefix. No decimal points; this is an integer parse. There + * must not be a sign of either '-' or '+'; this is an unsigned integer parse. * * The string must be in big-endian format, always. Generally this is only an issue for hex - * (and perhaps) binary strings. That is, for decimal strings you would expect "12.3" to some - * who wind up with the number 3.21. But for 0x0102, maybe not so clear. + * (and perhaps) binary strings. That is, for decimal strings you would not expect "12.3" to some + * how wind up with the number 3.21. But for 0x0102, maybe not so clear. * * @param number - an integer value expressed in `base` in big-endian format. * @param base - must only be one of 2, 10, or 16. @@ -148,9 +152,21 @@ coerceUInt256 (UInt512 x, int *overflow); extern uint64_t coerceUInt64 (UInt256 x, int *overflow); +/** + * Coerce `x`, a UInt256, to a double. If `x` is too big then overflow is set to 1 and + * zero is returned. + */ +extern double +coerceDouble (UInt256 value, int *overflow); + +extern long double +coerceLongDouble (UInt256 value, int *overflow); + /** * Returns the string representation of `x` in `base`. No matter the base, the returned string - * will be in big-endian format. + * will be in big-endian format (as you expect). + * + * base must be one of {2, 10, 16} * * @warn YOU OWN THE RETURNED MEMORY */ diff --git a/ThirdParty/breadwallet-core/ethereum/util/BRUtilMathParse.c b/ThirdParty/breadwallet-core/ethereum/util/BRUtilMathParse.c index db2053add..4a2b6fcc4 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/BRUtilMathParse.c +++ b/ThirdParty/breadwallet-core/ethereum/util/BRUtilMathParse.c @@ -1,9 +1,9 @@ // // BBRUtilMathParse.c -// breadwallet-core Ethereum +// Core Ethereum // // Created by Ed Gamble on 3/16/2018. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // // See the LICENSE file at the project root for license information. // See the CONTRIBUTORS file at the project root for a list of contributors. @@ -13,20 +13,38 @@ #include #include #include +#include #include "support/BRAssert.h" #include "BRUtil.h" // // Parsing // +static BRCoreParseStatus +parseInIntegerInBase (const char *number, int base) { + // This isn't the right way to handle an empty `number` string. + if (NULL == number || '\0' == *number) return CORE_PARSE_STRANGE_DIGITS; + while (*number) { + switch (base) { + case 2: + if ('0' != *number && '1' != *number) return CORE_PARSE_STRANGE_DIGITS; + break; + case 10: + if (!isdigit (*number)) return CORE_PARSE_STRANGE_DIGITS; + break; + + case 16: + if (!isxdigit(*number)) return CORE_PARSE_STRANGE_DIGITS; + break; + } + number++; + } + return CORE_PARSE_OK; +} extern BRCoreParseStatus parseIsInteger(const char *number) { - // Number contains only digits and has at least one digit - if (NULL == number || '\0' == *number) return CORE_PARSE_STRANGE_DIGITS; - while (*number) - if (!isdigit (*number++)) return CORE_PARSE_STRANGE_DIGITS; - return CORE_PARSE_OK; + return parseInIntegerInBase (number, 10); } extern BRCoreParseStatus @@ -180,11 +198,14 @@ parseUInt64 (const char *string, int digits, int base) { assert (digits <= maxDigits ); // - char number[1 + maxDigits]; + char number[1 + maxDigits], *numberEnd; strncpy (number, string, maxDigits); number[maxDigits] = '\0'; - - uint64_t value = strtoull (number, NULL, base); + + errno = 0; + uint64_t value = strtoull (number, &numberEnd, base); + if (0 == errno && (*number == '\0' || numberEnd == NULL || *numberEnd != '\0')) + errno = EINVAL; return createUInt256 (value); } @@ -209,7 +230,22 @@ createUInt256Parse (const char *string, int base, BRCoreParseStatus *status) { // Strip leading '0's while ('0' == *string) string++; - UInt256 value = UINT256_ZERO; + // If the string is now empty, we've a value of '0' + if ('\0' == *string) { + *status = CORE_PARSE_OK; + return UINT256_ZERO; + } + + if ('\0' != *string && ('-' == *string || '+' == *string)) { + *status = CORE_PARSE_STRANGE_DIGITS; + return UINT256_ZERO; + } + + // Confirm that we in fact have a integer string. + *status = parseInIntegerInBase (string, base); + if (CORE_PARSE_OK != *status) + return UINT256_ZERO; + int maxDigits = parseMaximumDigitsForUInt256InBase(base); long length = strlen (string); @@ -220,14 +256,22 @@ createUInt256Parse (const char *string, int base, BRCoreParseStatus *status) { // We'll process this many digits in `string`. int stringChunks = parseMaximumDigitsForUInt64InBase(base); - + + // Fill this in. + UInt256 value = UINT256_ZERO; + // For parsing a string like "123.45", the character at index 0 is '1'. So by parsing chunks // with ascending index, we naturally treat `string` as big endian - no matter the base. // Eventually, when `parseUInt64()` calls `strtoull` we'll still be using big endian. for (long index = 0; index < length; index += stringChunks) { // On the first time through, get an initial value - if (index == 0) + if (index == 0) { value = parseUInt64(string, stringChunks, base); + if (errno != 0) { + *status = CORE_PARSE_STRANGE_DIGITS; + return UINT256_ZERO; + } + } // Otherwise, we'll scale value and add in the next chunk. else { @@ -282,6 +326,8 @@ coerceString (UInt256 x, int base) { // Reverse and 'strip zeros' UInt256 xr = UInt256Reverse(x); // TODO: LITTLE ENDIAN only int xrIndex = 0; + // We explicitly handled the '0 == x' case up-front. Thus xr.u8 *always* + // eventually has a non-zero value. while (0 == xr.u8[xrIndex]) xrIndex++; // Encode return encodeHexCreate (NULL, &xr.u8[xrIndex], sizeof (xr.u8) - xrIndex); @@ -329,8 +375,8 @@ coerceStringPrefaced (UInt256 x, int base, const char *preface) { if (NULL == preface || 0 == strcmp ("", preface)) return string; char *stringToFree = string; // save the pointer to string - // Strip off leading zeros in `string` - while ('\0' != string[0] && '0' == string[0]) string++; + // Strip off leading zeros in `string` but be sure to leave at least one digit + while ('\0' != string[0] && '0' == string[0] && '\0' != string[1]) string++; char *result = malloc (strlen(preface) + strlen (string) + 1); strcpy (result, preface); diff --git a/ThirdParty/breadwallet-core/ethereum/util/testUtil.c b/ThirdParty/breadwallet-core/ethereum/util/testUtil.c index 471887d4e..342345c08 100644 --- a/ThirdParty/breadwallet-core/ethereum/util/testUtil.c +++ b/ThirdParty/breadwallet-core/ethereum/util/testUtil.c @@ -3,13 +3,16 @@ // CoreTests // // Created by Ed Gamble on 7/23/18. -// Copyright © 2018 Breadwinner AG. All rights reserved. +// Copyright © 2018-2019 Breadwinner AG. All rights reserved. // +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. #include #include #include -#include "BRUtil.h" +#include +#include "ethereum/util/BRUtil.h" // // Math Tests @@ -157,7 +160,7 @@ runMathMulTests () { static void runMathMulDoubleTests () { BRCoreParseStatus status; - int over, neg; double rem; + int over, neg; double rem, v; UInt256 ai, ao, r; ai = createUInt256Parse("1000000000000000", 10, &status); // Input @@ -205,7 +208,11 @@ runMathMulDoubleTests () { assert (over == 0 && eqUInt256(r, ao)); assert (0 == strcmp ("2", coerceString(r, 10))); - // overflow... + double x = 25.25434525155732538797258871; + r = createUInt256Double(x, 18, &over); + assert (!over && !eqUInt256(r, UINT256_ZERO)); + v = coerceDouble(r, &over); + assert(!over && fabs(v*1e-18 - x) / x < 1e-10); } static void @@ -253,6 +260,40 @@ runMathCoerceTests () { assert (0 == strcmp (es, "0f")); // unexpected free ((char *) es); + // Value: 0, w/ and w/o a prefix + UInt256 f = { .u64 = { 0, 0, 0, 0}}; + const char *fs = coerceString (f, 10); + assert (0 == strcmp (fs, "0")); + free ((char *) fs); + + fs = coerceStringPrefaced (f, 10, NULL); + assert (0 == strcmp (fs, "0")); + free ((char *) fs); + + fs = coerceStringPrefaced (f, 10, ""); + assert (0 == strcmp (fs, "0")); + free ((char *) fs); + + fs = coerceStringPrefaced (f, 10, "hex"); + assert (0 == strcmp (fs, "hex0")); + free ((char *) fs); + + fs = coerceStringPrefaced (f, 16, "0x"); + assert (0 == strcmp (fs, "0x0")); + free ((char *) fs); + + UInt256 g = createUInt256(0x0a); + const char *gs = coerceString (g, 10); + assert (0 == strcmp (gs, "10")); + free ((char *) gs); + + gs = coerceStringPrefaced (g, 10, NULL); + assert (0 == strcmp (gs, "10")); + free ((char *) gs); + + gs = coerceStringPrefaced (g, 16, "0x"); + assert (0 == strcmp (gs, "0xa")); + free ((char *) gs); } static void @@ -314,6 +355,28 @@ runMathParseTests () { && r.u64[2] == 0 && r.u64[3] == 0); + r = createUInt256Parse("0000", 10, &status); + assert (CORE_PARSE_OK == status && eqUInt256 (r, UINT256_ZERO)); + + r = createUInt256Parse("0000", 2, &status); + assert (CORE_PARSE_OK == status && eqUInt256 (r, UINT256_ZERO)); + + r = createUInt256Parse("0x0000", 16, &status); + assert (CORE_PARSE_OK == status && eqUInt256 (r, UINT256_ZERO)); + + r = createUInt256Parse("0x", 16, &status); + assert (CORE_PARSE_OK == status && eqUInt256 (r, UINT256_ZERO)); + + r = createUInt256Parse("", 10, &status); + assert (CORE_PARSE_OK == status && eqUInt256 (r, UINT256_ZERO)); + + r = createUInt256Parse("", 2, &status); + assert (CORE_PARSE_OK == status && eqUInt256 (r, UINT256_ZERO)); + + r = createUInt256Parse("", 16, &status); + assert (CORE_PARSE_OK == status && eqUInt256 (r, UINT256_ZERO)); + + char *s; r = createUInt256Parse("425693205796080237694414176550132631862392541400559", 10, &status); s = coerceString(r, 10); @@ -375,6 +438,41 @@ runMathParseTests () { r = createUInt256ParseDecimal("2.5", 0, &status); assert (CORE_PARSE_UNDERFLOW == status); + + + // Strings for: 0xa + + r = createUInt256Parse("0xa", 16, &status); + s = coerceStringPrefaced(r, 16, "0x"); + assert (0 == strcmp ("0xa", s)); + free (s); + + r = createUInt256Parse("0x0a", 16, &status); + s = coerceStringPrefaced(r, 16, "0x"); + assert (0 == strcmp ("0xa", s)); + free (s); + + r = createUInt256Parse("0x00a", 16, &status); + s = coerceStringPrefaced(r, 16, "0x"); + assert (0 == strcmp ("0xa", s)); + free (s); + + // Strings for 0x0 + + r = createUInt256Parse("0x", 16, &status); + s = coerceStringPrefaced(r, 16, "0x"); + assert (0 == strcmp ("0x0", s)); + free (s); + + r = createUInt256Parse("0x0", 16, &status); + s = coerceStringPrefaced(r, 16, "0x"); + assert (0 == strcmp ("0x0", s)); + free (s); + + r = createUInt256Parse("0x00", 16, &status); + s = coerceStringPrefaced(r, 16, "0x"); + assert (0 == strcmp ("0x0", s)); + free (s); } extern void diff --git a/ThirdParty/breadwallet-core/include/BRCryptoAccount.h b/ThirdParty/breadwallet-core/include/BRCryptoAccount.h new file mode 100644 index 000000000..416d5ebce --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoAccount.h @@ -0,0 +1,117 @@ +// +// BRCryptoAccount.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoAccount_h +#define BRCryptoAccount_h + +#include "BRCryptoBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoAccountRecord *BRCryptoAccount; + + /** + * Generate a BIP-39 PaperKey from a BIP-39 word list + * + * @note Asserts if words is itself an invalide BIP-39 word list + * + * @param wordList The BIP-39 word list. + * + * @return A BIP-39 PaperKey + */ + extern char * + cryptoAccountGeneratePaperKey (const char *wordList[]); + + /** + * Validate a candidate BIP-39 PaperKey with a BIP-39 word list + * + * @note Asserts if words is itself an invalide BIP-39 word list + * + * @param phrase the candiate paper key + * @param words the BIP-39 word list + * + * @return true if valid; false otherwise + */ + extern BRCryptoBoolean + cryptoAccountValidatePaperKey (const char *phrase, const char *words[]); + + /** + * Validate the number of words in the word list. + * + * @param wordsCount number of words + * + * @return CRYPTO_TRUE if valid; false otherwise. + */ + extern BRCryptoBoolean + cryptoAccountValidateWordsList (size_t wordsCount); + + extern BRCryptoAccount + cryptoAccountCreate (const char *paperKey, uint64_t timestamp, const char *uids); + + /** + * Recreate an Account from a serialization + * + * @param bytes serialized bytes + * @param bytesCount serialized bytes count + * + * @return The Account, or NULL. If the serialization is invalid then the account *must be + * recreated* from the `phrase` (aka 'Paper Key'). A serialization will be invald when the + * serialization format changes which will *always occur* when a new blockchain is added. For + * example, when XRP is added the XRP public key must be serialized; the old serialization w/o + * the XRP public key will be invalid and the `phrase` is *required* in order to produce the + * XRP public key. + */ + extern BRCryptoAccount + cryptoAccountCreateFromSerialization (const uint8_t *bytes, size_t bytesCount, const char *uids); + + /* + * Serialize an account. + * + * @param bytesCount the serialized bytes count; must not be NULL + * + * @return An array of bytes holding the account serialization that is suitable for use + * by cryptoAccountCreateFromSerialization. The returned array is owned by the caller and + * must be freed. + */ + extern uint8_t * + cryptoAccountSerialize (BRCryptoAccount account, size_t *bytesCount); + + /* + * Validate that the serialization (represented with `bytes` and `bytesCount`) is consistent + * with `account`. This does not serialize account and then compare with bytes; it simply + * checks that the serialization is for account. + * + * @param account the account + * @param bytes the serialization bytes + * @param bytesCount the count of serialization bytes + */ + extern BRCryptoBoolean + cryptoAccountValidateSerialization (BRCryptoAccount account, + const uint8_t *bytes, + size_t bytesCount); + + extern uint64_t + cryptoAccountGetTimestamp (BRCryptoAccount account); + + extern char * + cryptoAccountGetFileSystemIdentifier (BRCryptoAccount account); + + extern const char * + cryptoAccountGetUids (BRCryptoAccount account); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoAccount, cryptoAccount); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoAccount_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoAddress.h b/ThirdParty/breadwallet-core/include/BRCryptoAddress.h new file mode 100644 index 000000000..fed143eea --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoAddress.h @@ -0,0 +1,70 @@ +// +// BRCryptoAddress.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoAddress_h +#define BRCryptoAddress_h + +#include "BRCryptoBase.h" +#include "BRCryptoNetwork.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /// MARK: - Address + + typedef struct BRCryptoAddressRecord *BRCryptoAddress; + + /// + /// Create an address for `string` on `network`. The string representation of addresses differ + /// depending on the network. If `string` is not valid for `network`, then `NULL` is returned. + /// + /// @param network + /// @param string + /// + /// @return An Address or NULL + /// + extern BRCryptoAddress + cryptoAddressCreateFromString (BRCryptoNetwork network, + const char *string); + + /// + /// Returns the address' string representation which is suitable for display. Note that an + /// address representing BCH will have a prefix included, typically one of 'bitcoincash' or + ///'bchtest'. And, there is not the reverse function of `cryptoAddressCreateFromString()` + /// whereby the type (BTC, BCH, ETH, ...) is derived from the string - one must know + /// beforehand in order to process the string. + /// + /// @param address the address + /// + /// @return A string representation which is newly allocated and must be freed. + /// + extern char * + cryptoAddressAsString (BRCryptoAddress address); + + /// + /// Compare two address for identity. + /// + /// @param a1 + /// @param a2 + /// + /// @return CRYPTO_TRUE if identical, CRYPTO_FALSE otherwise + /// + extern BRCryptoBoolean + cryptoAddressIsIdentical (BRCryptoAddress a1, + BRCryptoAddress a2); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoAddress, cryptoAddress); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoAddress_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoAmount.h b/ThirdParty/breadwallet-core/include/BRCryptoAmount.h new file mode 100644 index 000000000..d483cc445 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoAmount.h @@ -0,0 +1,189 @@ +// +// BRCryptoAmount.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoAmount_h +#define BRCryptoAmount_h + +#include "BRCryptoBase.h" +#include "BRCryptoCurrency.h" +#include "BRCryptoUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + CRYPTO_COMPARE_LT, + CRYPTO_COMPARE_EQ, + CRYPTO_COMPARE_GT + } BRCryptoComparison; + + typedef struct BRCryptoAmountRecord *BRCryptoAmount; + + /** + * Create an amount from `value` in `unit`. If the amount is "2 Bitcoin" then, for example, + * the amount can be created from either of {2.0, BTC} or {2e8, SAT}. + * + * @note The internal representation is always as a UInt256 in the currency's baseUnit (aka + * the 'integer unit'). This allows easy arithmetic operations w/o conversions. Conversions + * to the base unit happens on creation, and conversion from the base unit happens on display. + * + * @param value + * @param unit + * + * @return An amount + */ + extern BRCryptoAmount + cryptoAmountCreateDouble (double value, + BRCryptoUnit unit); + + /** + * Create an amount form `value` in `unit`. See discusion on `cryptoAmountCreateDouble()` + * + * @param value + * @param unit + * + *@return An Amount + */ + extern BRCryptoAmount + cryptoAmountCreateInteger (int64_t value, + BRCryptoUnit unit); + + /** + * Create an amount from `value` and `unit`. See discusion on `cryptoAmountCreateDouble()` + * + * @note there are some constraints on `const char *value` that need to be described. + * + * @param value + * @param isNegative + * @param unit + * + * @return An amount + */ + extern BRCryptoAmount + cryptoAmountCreateString (const char *value, + BRCryptoBoolean isNegative, + BRCryptoUnit unit); + + /** + * Returns the amount's unit. + * + * @param amount The amount + * + * @return The amount's unit, typically use for default display w/ an incremented reference + * count (aka 'taken') + */ + extern BRCryptoUnit + cryptoAmountGetUnit (BRCryptoAmount amount); + + /** + * Returns the amount's currency + * + * @param amount The amount + * + * @return The currency w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoCurrency + cryptoAmountGetCurrency (BRCryptoAmount amount); + + extern BRCryptoBoolean + cryptoAmountHasCurrency (BRCryptoAmount amount, + BRCryptoCurrency currency); + + extern BRCryptoBoolean + cryptoAmountIsNegative (BRCryptoAmount amount); + + + /** + * Check of two amount's are compatible; they are compatible if they have the same currency and + * thus can be added. + * + * @param a1 + * @param a2 + * + * @return + */ + extern BRCryptoBoolean + cryptoAmountIsCompatible (BRCryptoAmount a1, + BRCryptoAmount a2); + + extern BRCryptoBoolean + cryptoAmountIsZero (BRCryptoAmount amount); + + extern BRCryptoComparison + cryptoAmountCompare (BRCryptoAmount a1, + BRCryptoAmount a2); + + extern BRCryptoAmount + cryptoAmountAdd (BRCryptoAmount a1, + BRCryptoAmount a2); + + extern BRCryptoAmount + cryptoAmountSub (BRCryptoAmount a1, + BRCryptoAmount a2); + + extern BRCryptoAmount + cryptoAmountNegate (BRCryptoAmount amount); + + /** + * Convert `amount` into `unit` + * + * @param amount + * @param unit + * + * @note: if `unit` is incompatible, then NULL is returned. + * + * @return An amount + */ + extern BRCryptoAmount + cryptoAmountConvertToUnit (BRCryptoAmount amount, + BRCryptoUnit unit); + + /** + * Return `amount` as a double in `unit`. For example, if amount is "2 BTC" and unit is + * SAT then the returned value will be 2e8; if amount is "2e6 SAT" and unit is "BTC" then the + * return value will be 0.02. + * + * @param amount + * @param unit + * @param overflow + * + * @return A double + */ + extern double + cryptoAmountGetDouble (BRCryptoAmount amount, + BRCryptoUnit unit, + BRCryptoBoolean *overflow); + + /** + * Return `amount` as a UInt64 if the representation of `amount` in the base unit is less than + * or equal to UINT64_MAX; otherwise set overflow. + * + * @param amount + * @param overflow + * + * @return + */ + extern uint64_t + cryptoAmountGetIntegerRaw (BRCryptoAmount amount, + BRCryptoBoolean *overflow); + + extern char * + cryptoAmountGetStringPrefaced (BRCryptoAmount amount, + int base, + const char *preface); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoAmount, cryptoAmount); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoAmount_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoBase.h b/ThirdParty/breadwallet-core/include/BRCryptoBase.h new file mode 100644 index 000000000..6c197354d --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoBase.h @@ -0,0 +1,142 @@ +// +// BRCryptoBase.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoBase_h +#define BRCryptoBase_h + +#include +#include +#include +#include + +// temporary + +#if !defined (OwnershipGiven) +#define OwnershipGiven +#endif + +#if !defined (OwnershipKept) +#define OwnershipKept +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoWalletRecord *BRCryptoWallet; + + typedef struct BRCryptoWalletManagerRecord *BRCryptoWalletManager; + + // Cookies are used as markers to match up an asynchronous operation + // request with its corresponding event. + typedef void *BRCryptoCookie; + + typedef enum { + CRYPTO_FALSE = 0, + CRYPTO_TRUE = 1 + } BRCryptoBoolean; + +#define AS_CRYPTO_BOOLEAN(zeroIfFalse) ((zeroIfFalse) ? CRYPTO_TRUE : CRYPTO_FALSE) + + // Only for use in Swift/Java + typedef size_t BRCryptoCount; + + // Only for use in Swift/Java + static inline void + cryptoMemoryFree (void *memory) { + free (memory); + } + +#if !defined(BLOCK_HEIGHT_UNBOUND) +// See BRBase.h +#define BLOCK_HEIGHT_UNBOUND (UINT64_MAX) +#endif + + /// MARK: - Data32 / Data16 + + typedef struct { + uint8_t data[256/8]; + } BRCryptoData32; + + static inline void cryptoData32Clear (BRCryptoData32 *data32) { + memset (data32, 0, sizeof (BRCryptoData32)); + } + + typedef struct { + uint8_t data[128/8]; + } BRCryptoData16; + + static inline void cryptoData16Clear (BRCryptoData16 *data16) { + memset (data16, 0, sizeof (BRCryptoData16)); + } + + /// MARK: - Reference Counting + + typedef struct { + _Atomic(unsigned int) count; + void (*free) (void *); + } BRCryptoRef; + +#if defined (CRYPTO_REF_DEBUG) +#include +static int cryptoRefDebug = 1; +#define cryptoRefShow printf +#else +static int cryptoRefDebug = 0; +#define cryptoRefShow +#endif + +#define DECLARE_CRYPTO_GIVE_TAKE(type, preface) \ + extern type preface##Take (type obj); \ + extern type preface##TakeWeak (type obj); \ + extern void preface##Give (type obj) + +#define IMPLEMENT_CRYPTO_GIVE_TAKE(type, preface) \ + static void preface##Release (type obj); \ + extern type \ + preface##Take (type obj) { \ + unsigned int _c = atomic_fetch_add (&obj->ref.count, 1); \ + /* catch take after release */ \ + assert (0 != _c); \ + return obj; \ + } \ + extern type \ + preface##TakeWeak (type obj) { \ + unsigned int _c = atomic_load(&obj->ref.count); \ + /* keep trying to take unless object is released */ \ + while (_c != 0 && \ + !atomic_compare_exchange_weak (&obj->ref.count, &_c, _c + 1)) {} \ + if (cryptoRefDebug && 0 == _c) { cryptoRefShow ("CRY: Missed: %s\n", #type); }\ + return (_c != 0) ? obj : NULL; \ + } \ + extern void \ + preface##Give (type obj) { \ + unsigned int _c = atomic_fetch_sub (&obj->ref.count, 1); \ + /* catch give after release */ \ + assert (0 != _c); \ + if (1 == _c) { \ + if (cryptoRefDebug) { cryptoRefShow ("CRY: Release: %s\n", #type); } \ + obj->ref.free (obj); \ + } \ + } + +#define CRYPTO_AS_FREE(release) ((void (*) (void *)) release) + +#define CRYPTO_REF_ASSIGN(release) (BRCryptoRef) { 1, CRYPTO_AS_FREE (release) } + +#if !defined (private_extern) +# define private_extern extern +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoBase_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoCipher.h b/ThirdParty/breadwallet-core/include/BRCryptoCipher.h new file mode 100644 index 000000000..ce02ea9ec --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoCipher.h @@ -0,0 +1,80 @@ +// +// BRCryptoCipher.h +// BRCore +// +// Created by Michael Carrara on 9/23/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoCipher_h +#define BRCryptoCipher_h + +#include "BRCryptoBase.h" +#include "BRCryptoKey.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + CRYPTO_CIPHER_AESECB, + CRYPTO_CIPHER_CHACHA20_POLY1305, + CRYPTO_CIPHER_PIGEON + } BRCryptoCipherType; + + typedef struct BRCryptoCipherRecord *BRCryptoCipher; + + extern BRCryptoCipher + cryptoCipherCreateForAESECB(const uint8_t *key, + size_t keyLen); + + extern BRCryptoCipher + cryptoCipherCreateForChacha20Poly1305(BRCryptoKey key, + const uint8_t *nonce, size_t nonceLen, + const uint8_t *authenticatedData, size_t authenticatedDataLen); + + extern BRCryptoCipher + cryptoCipherCreateForPigeon(BRCryptoKey privKey, + BRCryptoKey pubKey, + const uint8_t *nonce, size_t nonceLen); + + extern size_t + cryptoCipherEncryptLength (BRCryptoCipher cipher, + const uint8_t *plaintext, + size_t plaintextLen); + + extern BRCryptoBoolean + cryptoCipherEncrypt (BRCryptoCipher cipher, + uint8_t *ciphertext, + size_t ciphertextLen, + const uint8_t *plaintext, + size_t plaintextLen); + + extern size_t + cryptoCipherDecryptLength (BRCryptoCipher cipher, + const uint8_t *ciphertext, + size_t ciphertextLen); + + extern BRCryptoBoolean + cryptoCipherDecrypt (BRCryptoCipher cipher, + uint8_t *plaintext, + size_t plaintextLen, + const uint8_t *ciphertext, + size_t ciphertextLen); + + extern BRCryptoBoolean + cryptoCipherMigrateBRCoreKeyCiphertext (BRCryptoCipher cipher, + uint8_t *migratedCiphertext, + size_t migratedCiphertextLen, + const uint8_t *originalCiphertext, + size_t originalCiphertextLen); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoCipher, cryptoCipher); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoCipher_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoCoder.h b/ThirdParty/breadwallet-core/include/BRCryptoCoder.h new file mode 100644 index 000000000..4dc19e35d --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoCoder.h @@ -0,0 +1,59 @@ +// +// BRCryptoCoder.h +// BRCore +// +// Created by Michael Carrara on 9/23/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoCoder_h +#define BRCryptoCoder_h + +#include "BRCryptoBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + CRYPTO_CODER_HEX, + CRYPTO_CODER_BASE58, + CRYPTO_CODER_BASE58CHECK + } BRCryptoCoderType; + + typedef struct BRCryptoCoderRecord *BRCryptoCoder; + + extern BRCryptoCoder + cryptoCoderCreate(BRCryptoCoderType type); + + extern size_t + cryptoCoderEncodeLength (BRCryptoCoder coder, + const uint8_t *src, + size_t srcLen); + + extern BRCryptoBoolean + cryptoCoderEncode (BRCryptoCoder coder, + char *dst, + size_t dstLen, + const uint8_t *src, + size_t srcLen); + + extern size_t + cryptoCoderDecodeLength (BRCryptoCoder coder, + const char *src); + + extern BRCryptoBoolean + cryptoCoderDecode (BRCryptoCoder coder, + uint8_t *dst, + size_t dstLen, + const char *src); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoCoder, cryptoCoder); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoCoder_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoCurrency.h b/ThirdParty/breadwallet-core/include/BRCryptoCurrency.h new file mode 100644 index 000000000..b7d6403da --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoCurrency.h @@ -0,0 +1,79 @@ +// +// BRCryptoCurrency.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoCurrency_h +#define BRCryptoCurrency_h + +#include "BRCryptoBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoCurrencyRecord *BRCryptoCurrency; + + /** + * Create a currency + * + * @param uids the 'unique identifier string'. This will be globally unique + * @param name the name, such as "The Breadwallet Token" + * @param code the code, such as "BRD" + * @param type the type, such a 'erc20' + * @param issuer the issuer or NULL. For currency derived from an ERC20 token, the issue must + * be a 'hex string' (starts with '0x') representing the Smart Contract Address. + * + * @return a currency + */ + extern BRCryptoCurrency + cryptoCurrencyCreate (const char *uids, + const char *name, + const char *code, + const char *type, + const char *issuer); + + + + extern const char * + cryptoCurrencyGetUids (BRCryptoCurrency currency); + + extern const char * + cryptoCurrencyGetName (BRCryptoCurrency currency); + + extern const char * + cryptoCurrencyGetCode (BRCryptoCurrency currency); + + extern const char * + cryptoCurrencyGetType (BRCryptoCurrency currency); + + /** + * Return the currency issuer or NULL if there is none. For an ERC20-based currency, the + * issuer will be the Smart Contract Address. + * + * @param currency the currency + * + *@return the issuer as a string or NULL + */ + extern const char * + cryptoCurrencyGetIssuer (BRCryptoCurrency currency); + + extern BRCryptoBoolean + cryptoCurrencyIsIdentical (BRCryptoCurrency c1, + BRCryptoCurrency c2); + + // initial supply + // total supply + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoCurrency, cryptoCurrency); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoCurrency_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoFeeBasis.h b/ThirdParty/breadwallet-core/include/BRCryptoFeeBasis.h new file mode 100644 index 000000000..136cae2d7 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoFeeBasis.h @@ -0,0 +1,46 @@ +// +// BRCryptoFeeBasis.h +// Core +// +// Created by Ed Gamble on 5/15/19. +// Copyright © 2019 Breadwallet AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoFeeBasis_h +#define BRCryptoFeeBasis_h + +#include "BRCryptoBase.h" +#include "BRCryptoCurrency.h" +#include "BRCryptoAmount.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoFeeBasisRecord *BRCryptoFeeBasis; + + extern BRCryptoAmount + cryptoFeeBasisGetPricePerCostFactor (BRCryptoFeeBasis feeBasis); + + extern BRCryptoUnit + cryptoFeeBasisGetPricePerCostFactorUnit (BRCryptoFeeBasis feeBasis); + + extern double + cryptoFeeBasisGetCostFactor (BRCryptoFeeBasis feeBasis); + + extern BRCryptoAmount + cryptoFeeBasisGetFee (BRCryptoFeeBasis feeBasis); + + extern BRCryptoBoolean + cryptoFeeBasisIsIdentical (BRCryptoFeeBasis feeBasis1, + BRCryptoFeeBasis feeBasis2); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoFeeBasis, cryptoFeeBasis); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoFeeBasis_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoHash.h b/ThirdParty/breadwallet-core/include/BRCryptoHash.h new file mode 100644 index 000000000..5cfe46f91 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoHash.h @@ -0,0 +1,37 @@ +// +// BRCryptoHash.h +// Core +// +// Created by Ed Gamble on 5/15/19. +// Copyright © 2019 Breadwallet AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoHash_h +#define BRCryptoHash_h + +#include "BRCryptoBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoHashRecord *BRCryptoHash; + + extern BRCryptoBoolean + cryptoHashEqual (BRCryptoHash h1, BRCryptoHash h2); + + extern char * + cryptoHashString (BRCryptoHash hash); + + extern int + cryptoHashGetHashValue (BRCryptoHash hash); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoHash, cryptoHash); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoHash_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoHasher.h b/ThirdParty/breadwallet-core/include/BRCryptoHasher.h new file mode 100644 index 000000000..a7215e37b --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoHasher.h @@ -0,0 +1,55 @@ +// +// BRCryptoHasher.h +// BRCore +// +// Created by Michael Carrara on 9/23/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoHasher_h +#define BRCryptoHasher_h + +#include "BRCryptoBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + CRYPTO_HASHER_SHA1, + CRYPTO_HASHER_SHA224, + CRYPTO_HASHER_SHA256, + CRYPTO_HASHER_SHA256_2, + CRYPTO_HASHER_SHA384, + CRYPTO_HASHER_SHA512, + CRYPTO_HASHER_SHA3, + CRYPTO_HASHER_RMD160, + CRYPTO_HASHER_HASH160, + CRYPTO_HASHER_KECCAK256, + CRYPTO_HASHER_MD5 + } BRCryptoHasherType; + + typedef struct BRCryptoHasherRecord *BRCryptoHasher; + + extern BRCryptoHasher + cryptoHasherCreate(BRCryptoHasherType type); + + extern size_t + cryptoHasherLength (BRCryptoHasher hasher); + + extern BRCryptoBoolean + cryptoHasherHash (BRCryptoHasher hasher, + uint8_t *dst, + size_t dstLen, + const uint8_t *src, + size_t srcLen); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoHasher, cryptoHasher); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoHasher_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoKey.h b/ThirdParty/breadwallet-core/include/BRCryptoKey.h new file mode 100644 index 000000000..f0d425ea7 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoKey.h @@ -0,0 +1,103 @@ +// +// BRCryptoKey.h +// BRCore +// +// Created by Ed Gamble on 7/30/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoKey_h +#define BRCryptoKey_h + +#include "BRCryptoBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /// MARK: - Crypto Secret + + typedef struct { + uint8_t data[256/8]; + } BRCryptoSecret; + + static inline void cryptoSecretClear (BRCryptoSecret *secret) { + memset (secret->data, 0, sizeof (secret->data)); + } + + /// MARK: Crypto Key + + typedef struct BRCryptoKeyRecord *BRCryptoKey; + + extern BRCryptoBoolean + cryptoKeyIsProtectedPrivate (const char *privateKey); + + extern BRCryptoKey + cryptoKeyCreateFromSecret (BRCryptoSecret secret); + + extern BRCryptoKey + cryptoKeyCreateFromPhraseWithWords (const char *phrase, const char *words[]); + + extern BRCryptoKey + cryptoKeyCreateFromStringProtectedPrivate (const char *privateKey, const char * passphrase); + + extern BRCryptoKey + cryptoKeyCreateFromStringPrivate (const char *string); + + extern BRCryptoKey + cryptoKeyCreateFromStringPublic (const char *string); + +// extern BRCryptoKey +// cryptoKeyCreateFromSerializationPublic (uint8_t *data, size_t dataCount); +// +// extern BRCryptoKey +// cryptoKeyCreateFromSerializationPrivate (uint8_t *data, size_t dataCount); +// + extern BRCryptoKey + cryptoKeyCreateForPigeon (BRCryptoKey key, uint8_t *nonce, size_t nonceCount); + + extern BRCryptoKey + cryptoKeyCreateForBIP32ApiAuth (const char *phrase, const char *words[]); + + extern BRCryptoKey + cryptoKeyCreateForBIP32BitID (const char *phrase, int index, const char *uri, const char *words[]); + + extern size_t + cryptoKeySerializePublic (BRCryptoKey key, /* ... */ uint8_t *data, size_t dataCount); + + extern size_t + cryptoKeySerializePrivate(BRCryptoKey key, /* ... */ uint8_t *data, size_t dataCount); + + extern int + cryptoKeyHasSecret (BRCryptoKey key); + + extern char * + cryptoKeyEncodePrivate (BRCryptoKey key); + + extern char * + cryptoKeyEncodePublic (BRCryptoKey key); + + extern BRCryptoSecret + cryptoKeyGetSecret (BRCryptoKey key); + + extern int + cryptoKeySecretMatch (BRCryptoKey key1, BRCryptoKey key2); + + extern int + cryptoKeyPublicMatch (BRCryptoKey key1, BRCryptoKey key2); + +// extern size_t +// cryptoKeySign (BRCryptoKey key, void *sig, size_t sigLen, UInt256 md); + + extern void + cryptoKeyProvidePublicKey (BRCryptoKey key, int useCompressed, int compressed); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoKey, cryptoKey); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoKey_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoNetwork.h b/ThirdParty/breadwallet-core/include/BRCryptoNetwork.h new file mode 100644 index 000000000..ce404b2c5 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoNetwork.h @@ -0,0 +1,330 @@ +// +// BRCryptoNetwork.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoNetwork_h +#define BRCryptoNetwork_h + +#include "BRCryptoAmount.h" +#include "BRCryptoSync.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /// + /// Crypto Network Type + /// + /// Try as we might, there are certain circumstances where the type of the network needs to + /// be known. Without this enumeration, one uses hack-arounds like: + /// "btc" == network.currency.code + /// So, provide these and expect them to grow. + /// + /// Enumerations here need to be consistent with the networks defined in; + /// crypto/BRCryptoConfig.h + /// + typedef enum { + CRYPTO_NETWORK_TYPE_BTC, + CRYPTO_NETWORK_TYPE_BCH, + CRYPTO_NETWORK_TYPE_ETH, + CRYPTO_NETWORK_TYPE_XRP, + // CRYPTO_NETWORK_TYPE_HBAR, + // CRYPTO_NETWORK_TYPE_XLM, + } BRCryptoNetworkCanonicalType; + +# define NUMBER_OF_NETWORK_TYPES (1 + CRYPTO_NETWORK_TYPE_XRP) + + extern const char * + cryptoNetworkCanonicalTypeString (BRCryptoNetworkCanonicalType type); + + /// MARK: - (Network) Address Scheme + + typedef enum { + CRYPTO_ADDRESS_SCHEME_BTC_LEGACY, + CRYPTO_ADDRESS_SCHEME_BTC_SEGWIT, + CRYPTO_ADDRESS_SCHEME_ETH_DEFAULT, + CRYPTO_ADDRESS_SCHEME_GEN_DEFAULT + } BRCryptoAddressScheme; + +#define NUMBER_OF_ADDRESS_SCHEMES (1 + CRYPTO_ADDRESS_SCHEME_GEN_DEFAULT) + + + // Same as: BRBlockHeight + typedef uint64_t BRCryptoBlockChainHeight; + + typedef struct BRCryptoNetworkFeeRecord *BRCryptoNetworkFee; + + extern BRCryptoNetworkFee + cryptoNetworkFeeCreate (uint64_t confirmationTimeInMilliseconds, + BRCryptoAmount pricePerCostFactor, + BRCryptoUnit pricePerCostFactorUnit); + + + /** + * The estimated time to confirm a transfer for this network fee + * + * @param networkFee the network fee + * + * @return time in milliseconds + */ + extern uint64_t + cryptoNetworkFeeGetConfirmationTimeInMilliseconds (BRCryptoNetworkFee networkFee); + + extern BRCryptoAmount + cryptoNetworkFeeGetPricePerCostFactor (BRCryptoNetworkFee networkFee); + + extern BRCryptoUnit + cryptoNetworkFeeGetPricePerCostFactorUnit (BRCryptoNetworkFee networkFee); + + extern BRCryptoBoolean + cryptoNetworkFeeEqual (BRCryptoNetworkFee nf1, BRCryptoNetworkFee nf2); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoNetworkFee, cryptoNetworkFee); + + /** + * A Crypto Network represents a Blockchain. The blockchains are determined from the + * BlockChainDB '/blockchains' query; however, networks for testnets are also defined. + * Thus, the available networks are {btc,bch,eth,...} X {mainnet,testnet} + * + * A Crypto Network has a currency which represents the asset used to pay for network fees. + * For Bitcoin the currency is 'bitcoin'; for Ethereum the currency is 'Ethereum'. + * + * A Crypto Network may support more than one currency. For Ethereum additional currencies + * include the ERC20 Smart Contracts of interest - for example, BRD. + * + * Every Crypto Network's currency has a defined base Unit, default Unit and an arbitrary + * set of other units. For Ethereum there are: WEI, ETHER, [WEI, GWEI, ..., ETHER, ...] + * respectively. + */ + typedef struct BRCryptoNetworkRecord *BRCryptoNetwork; + + typedef void *BRCryptoNetworkListener; + + extern BRCryptoNetworkCanonicalType + cryptoNetworkGetCanonicalType (BRCryptoNetwork network); + + extern const char * + cryptoNetworkGetUids (BRCryptoNetwork network); + + extern const char * + cryptoNetworkGetName (BRCryptoNetwork network); + + extern BRCryptoBoolean + cryptoNetworkIsMainnet (BRCryptoNetwork network); + + /** + * Returns the network's currency. This is typically (always?) the currency used to pay + * for network fees. + + @param network The network + @return the network's currency w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoCurrency + cryptoNetworkGetCurrency (BRCryptoNetwork network); + + extern void + cryptoNetworkSetCurrency (BRCryptoNetwork network, + BRCryptoCurrency currency); + + extern void + cryptoNetworkAddCurrency (BRCryptoNetwork network, + BRCryptoCurrency currency, + BRCryptoUnit baseUnit, + BRCryptoUnit defaultUnit); + + + extern const char * + cryptoNetworkGetCurrencyCode (BRCryptoNetwork network); + + /** + * Returns the currency's default unit or NULL + * + * @param network the network + * @param currency the currency desired for the default unit + * + * @return the currency's default unit or NULL w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoUnit + cryptoNetworkGetUnitAsDefault (BRCryptoNetwork network, + BRCryptoCurrency currency); + + /** + * Returns the currency's base unit or NULL + * + * @param network the network + * @param currency the currency desired for the base unit + * + * @return the currency's base unit or NULL w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoUnit + cryptoNetworkGetUnitAsBase (BRCryptoNetwork network, + BRCryptoCurrency currency); + + extern void + cryptoNetworkAddCurrencyUnit (BRCryptoNetwork network, + BRCryptoCurrency currency, + BRCryptoUnit unit); + + + extern BRCryptoBlockChainHeight + cryptoNetworkGetHeight (BRCryptoNetwork network); + + extern void + cryptoNetworkSetHeight (BRCryptoNetwork network, + BRCryptoBlockChainHeight height); + + extern uint32_t + cryptoNetworkGetConfirmationsUntilFinal (BRCryptoNetwork network); + + /** + * Returns the number of network currencies. This is the index exclusive limit to be used + * in `cryptoNetworkGetCurrencyAt()`. + * + * @param network the network + * + * @return number of network currencies. + */ + extern size_t + cryptoNetworkGetCurrencyCount (BRCryptoNetwork network); + + /** + * Returns the network's currency at `index`. The index must satisfy [0, count) otherwise + * an assertion is signaled. + * + * @param network the network + * @param index the desired currency index + * + * @return The currency w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoCurrency + cryptoNetworkGetCurrencyAt (BRCryptoNetwork network, + size_t index); + + /** + * Return 'TRUE' is `network` has `currency`. + * + * @param network the network + *@param currency the currency + * + *@return CRYPTO_TRUE if `network` has `currency`. + */ + extern BRCryptoBoolean + cryptoNetworkHasCurrency (BRCryptoNetwork network, + BRCryptoCurrency currency); + + /** + * Returns the network's currency with `symbol` or NULL. + * + * @param network the network + * @param index the desired currency's symbol + * + * @return The currency w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoCurrency + cryptoNetworkGetCurrencyForCode (BRCryptoNetwork network, + const char *code); + + extern BRCryptoCurrency + cryptoNetworkGetCurrencyForUids (BRCryptoNetwork network, + const char *uids); + + extern BRCryptoCurrency + cryptoNetworkGetCurrencyForIssuer (BRCryptoNetwork network, + const char *issuer); + + /** + * Returns the number of units for network's `currency`. This is the index exclusive limit to + * be used in `cryptoNetworkGetUnitAt()`. + * + * @param network the network + * @param currency the currency + * + * @return the number of units for `currency` + */ + extern size_t + cryptoNetworkGetUnitCount (BRCryptoNetwork network, + BRCryptoCurrency currency); + + /** + * Returns the currency's unit at `index`. The index must satisfy [0, count) otherwise an + * assertion is signaled. + * + * @param network the network + * @param currency the currency + * @param index the desired unit's index + * + * @return the currency unit w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoUnit + cryptoNetworkGetUnitAt (BRCryptoNetwork network, + BRCryptoCurrency currency, + size_t index); + + extern size_t + cryptoNetworkGetNetworkFeeCount (BRCryptoNetwork network); + + extern BRCryptoNetworkFee + cryptoNetworkGetNetworkFeeAt (BRCryptoNetwork network, + size_t index); + + extern BRCryptoNetworkFee * + cryptoNetworkGetNetworkFees (BRCryptoNetwork network, + size_t *count); + + extern void + cryptoNetworkSetNetworkFees (BRCryptoNetwork network, + const BRCryptoNetworkFee *fees, + size_t count); + + extern void + cryptoNetworkAddNetworkFee (BRCryptoNetwork network, + BRCryptoNetworkFee fee); + + // MARK: - Address Scheme + + extern BRCryptoAddressScheme + cryptoNetworkGetDefaultAddressScheme (BRCryptoNetwork network); + + extern const BRCryptoAddressScheme * + cryptoNetworkGetSupportedAddressSchemes (BRCryptoNetwork network, + BRCryptoCount *count); + + extern BRCryptoBoolean + cryptoNetworkSupportsAddressScheme (BRCryptoNetwork network, + BRCryptoAddressScheme scheme); + + // MARK: - Sync Mode + + extern BRCryptoSyncMode + cryptoNetworkGetDefaultSyncMode (BRCryptoNetwork network); + + extern const BRCryptoSyncMode * + cryptoNetworkGetSupportedSyncModes (BRCryptoNetwork network, + BRCryptoCount *count); + + extern BRCryptoBoolean + cryptoNetworkSupportsSyncMode (BRCryptoNetwork network, + BRCryptoSyncMode scheme); + + extern BRCryptoBoolean + cryptoNetworkRequiresMigration (BRCryptoNetwork network); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoNetwork, cryptoNetwork); + + extern BRCryptoNetwork * + cryptoNetworkInstallBuiltins (size_t *networksCount); + + extern BRCryptoNetwork + cryptoNetworkFindBuiltin (const char *uids); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoNetwork_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoPayment.h b/ThirdParty/breadwallet-core/include/BRCryptoPayment.h new file mode 100644 index 000000000..0fb79c476 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoPayment.h @@ -0,0 +1,178 @@ +// +// BRCryptoPayment.h +// BRCore +// +// Created by Michael Carrara on 8/27/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoPayment_h +#define BRCryptoPayment_h + +#include "BRCryptoBase.h" +#include "BRCryptoAddress.h" +#include "BRCryptoAmount.h" +#include "BRCryptoCurrency.h" +#include "BRCryptoNetwork.h" +#include "BRCryptoTransfer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /// Mark: Forward Declarations + + typedef struct BRCryptoPaymentProtocolRequestBitPayBuilderRecord *BRCryptoPaymentProtocolRequestBitPayBuilder; + + typedef struct BRCryptoPaymentProtocolRequestRecord *BRCryptoPaymentProtocolRequest; + + typedef struct BRCryptoPaymentProtocolPaymentRecord *BRCryptoPaymentProtocolPayment; + + typedef struct BRCryptoPaymentProtocolPaymentACKRecord *BRCryptoPaymentProtocolPaymentACK; + + typedef enum { + CRYPTO_PAYMENT_PROTOCOL_TYPE_BITPAY, + CRYPTO_PAYMENT_PROTOCOL_TYPE_BIP70, + } BRCryptoPaymentProtocolType; + + typedef enum { + CRYPTO_PAYMENT_PROTOCOL_ERROR_NONE, + CRYPTO_PAYMENT_PROTOCOL_ERROR_CERT_MISSING, + CRYPTO_PAYMENT_PROTOCOL_ERROR_CERT_NOT_TRUSTED, + CRYPTO_PAYMENT_PROTOCOL_ERROR_SIGNATURE_TYPE_NOT_SUPPORTED, + CRYPTO_PAYMENT_PROTOCOL_ERROR_SIGNATURE_VERIFICATION_FAILED, + CRYPTO_PAYMENT_PROTOCOL_ERROR_EXPIRED + } BRCryptoPaymentProtocolError; + + typedef void * BRCryptoPayProtReqContext; + + typedef char * (*BRCryptoPayProtReqBitPayAndBip70CommonNameExtractor) (BRCryptoPaymentProtocolRequest request, + BRCryptoPayProtReqContext context, + const char *pkiType, + uint8_t *certBytes[], + size_t certLengths[], + size_t certCount); + + typedef BRCryptoPaymentProtocolError (*BRCryptoPayProtReqBitPayAndBip70Validator) (BRCryptoPaymentProtocolRequest request, + BRCryptoPayProtReqContext context, + const char *pkiType, + uint64_t expires, + uint8_t *certBytes[], + size_t certLengths[], + size_t certCount, + const uint8_t *digest, + size_t digestLength, + const uint8_t *signature, + size_t signatureLength); + + + typedef struct { + BRCryptoPayProtReqContext context; + BRCryptoPayProtReqBitPayAndBip70Validator validator; + BRCryptoPayProtReqBitPayAndBip70CommonNameExtractor nameExtractor; + } BRCryptoPayProtReqBitPayAndBip70Callbacks; + + typedef BRCryptoPayProtReqBitPayAndBip70Callbacks BRCryptoPayProtReqBitPayCallbacks; + typedef BRCryptoPayProtReqBitPayAndBip70Callbacks BRCryptoPayProtReqBip70Callbacks; + + /// Mark: BitPay Payment Protocol Request Builder + + extern BRCryptoPaymentProtocolRequestBitPayBuilder + cryptoPaymentProtocolRequestBitPayBuilderCreate (BRCryptoNetwork cryptoNetwork, + BRCryptoCurrency cryptoCurrency, + BRCryptoPayProtReqBitPayCallbacks callbacks, + const char *network, + uint64_t time, + uint64_t expires, + double feePerByte, + const char *memo, + const char *paymentURL, + const uint8_t *merchantData, + size_t merchDataLen); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoPaymentProtocolRequestBitPayBuilder, cryptoPaymentProtocolRequestBitPayBuilder); + + extern void + cryptoPaymentProtocolRequestBitPayBuilderAddOutput(BRCryptoPaymentProtocolRequestBitPayBuilder builder, + const char *address, + uint64_t amount); + + extern BRCryptoPaymentProtocolRequest + cryptoPaymentProtocolRequestBitPayBuilderBuild(BRCryptoPaymentProtocolRequestBitPayBuilder builder); + + /// Mark: Payment Protocol Request + + extern BRCryptoBoolean + cryptoPaymentProtocolRequestValidateSupported (BRCryptoPaymentProtocolType type, + BRCryptoNetwork network, + BRCryptoCurrency currency, + BRCryptoWallet wallet); + + extern BRCryptoPaymentProtocolRequest + cryptoPaymentProtocolRequestCreateForBip70 (BRCryptoNetwork cryptoNetwork, + BRCryptoCurrency cryptoCurrency, + BRCryptoPayProtReqBip70Callbacks callbacks, + uint8_t *serialization, + size_t serializationLen); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoPaymentProtocolRequest, cryptoPaymentProtocolRequest); + + extern BRCryptoPaymentProtocolType + cryptoPaymentProtocolRequestGetType (BRCryptoPaymentProtocolRequest protoReq); + + extern BRCryptoBoolean + cryptoPaymentProtocolRequestIsSecure (BRCryptoPaymentProtocolRequest protoReq); + + extern const char * + cryptoPaymentProtocolRequestGetMemo (BRCryptoPaymentProtocolRequest protoReq); + + extern const char * + cryptoPaymentProtocolRequestGetPaymentURL (BRCryptoPaymentProtocolRequest protoReq); + + extern BRCryptoAmount + cryptoPaymentProtocolRequestGetTotalAmount (BRCryptoPaymentProtocolRequest protoReq); + + extern BRCryptoNetworkFee + cryptoPaymentProtocolRequestGetRequiredNetworkFee (BRCryptoPaymentProtocolRequest protoReq); + + extern BRCryptoAddress + cryptoPaymentProtocolRequestGetPrimaryTargetAddress (BRCryptoPaymentProtocolRequest protoReq); + + // If the return value is not NULL, it must be deallocated using free() + extern char * + cryptoPaymentProtocolRequestGetCommonName (BRCryptoPaymentProtocolRequest protoReq); + + extern BRCryptoPaymentProtocolError + cryptoPaymentProtocolRequestIsValid(BRCryptoPaymentProtocolRequest protoReq); + + /// Mark: Payment Protocol Payment + + extern BRCryptoPaymentProtocolPayment + cryptoPaymentProtocolPaymentCreate (BRCryptoPaymentProtocolRequest protoReq, + BRCryptoTransfer transfer, + BRCryptoAddress refundAddress); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoPaymentProtocolPayment, cryptoPaymentProtocolPayment); + + extern uint8_t * + cryptoPaymentProtocolPaymentEncode(BRCryptoPaymentProtocolPayment protoPay, + size_t *encodedLen); + + /// Mark: Payment Protocol ACK + + extern BRCryptoPaymentProtocolPaymentACK + cryptoPaymentProtocolPaymentACKCreateForBip70 (uint8_t *serialization, + size_t serializationLen); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoPaymentProtocolPaymentACK, cryptoPaymentProtocolPaymentACK); + + extern const char * + cryptoPaymentProtocolPaymentACKGetMemo (BRCryptoPaymentProtocolPaymentACK protoAck); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoPayment_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoPeer.h b/ThirdParty/breadwallet-core/include/BRCryptoPeer.h new file mode 100644 index 000000000..cfc8886d3 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoPeer.h @@ -0,0 +1,60 @@ +// +// BRCryptoPeer.h +// BRCore +// +// Created by Ed Gamble on 10/17/2019. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoPeer_h +#define BRCryptoPeer_h + +#include "BRCryptoBase.h" +#include "BRCryptoNetwork.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoPeerRecord *BRCryptoPeer; + + private_extern BRCryptoPeer + cryptoPeerCreate (BRCryptoNetwork network, + const char *address, + uint16_t port, + const char *publicKey); + + private_extern BRCryptoPeer + cryptoPeerCreateFromSerialization (BRCryptoNetwork network, + uint8_t *bytes, size_t bytesCount); + + extern BRCryptoData16 + cryptoPeerGetAddrAsInt (BRCryptoPeer peer); + + extern BRCryptoNetwork + cryptoPeerGetNetwork (BRCryptoPeer peer); + + extern const char * + cryptoPeerGetAddress (BRCryptoPeer peer); + + extern uint16_t + cryptoPeerGetPort (BRCryptoPeer peer); + + extern const char * + cryptoPeerGetPublicKey (BRCryptoPeer peer); + + extern uint8_t * + cryptoPeerSerialize (BRCryptoPeer peer, size_t *bytesCount); + + extern BRCryptoBoolean + cryptoPeerIsIdentical (BRCryptoPeer p1, BRCryptoPeer p2); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoPeer, cryptoPeer); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoPeer_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoSigner.h b/ThirdParty/breadwallet-core/include/BRCryptoSigner.h new file mode 100644 index 000000000..0242697c8 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoSigner.h @@ -0,0 +1,59 @@ +// +// BRCryptoSigner.h +// BRCore +// +// Created by Michael Carrara on 9/23/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoSigner_h +#define BRCryptoSigner_h + +#include "BRCryptoBase.h" +#include "BRCryptoKey.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + CRYPTO_SIGNER_BASIC_DER, + CRYPTO_SIGNER_BASIC_JOSE, + CRYPTO_SIGNER_COMPACT + } BRCryptoSignerType; + + typedef struct BRCryptoSignerRecord *BRCryptoSigner; + + extern BRCryptoSigner + cryptoSignerCreate(BRCryptoSignerType type); + + extern size_t + cryptoSignerSignLength (BRCryptoSigner signer, + BRCryptoKey key, + const uint8_t *src, + size_t srcLen); + + extern BRCryptoBoolean + cryptoSignerSign (BRCryptoSigner signer, + BRCryptoKey key, + uint8_t *dst, + size_t dstLen, + const uint8_t *src, + size_t srcLen); + + extern BRCryptoKey + cryptoSignerRecover (BRCryptoSigner signer, + const uint8_t *digest, + size_t digestLen, + const uint8_t *signature, + size_t signatureLen); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoSigner, cryptoSigner); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoSigner_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoStatus.h b/ThirdParty/breadwallet-core/include/BRCryptoStatus.h new file mode 100644 index 000000000..63db240f4 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoStatus.h @@ -0,0 +1,55 @@ +// +// BRCryptoStatus.c +// BRCore +// +// Created by Michael Carrara on 7/31/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoStatus_h +#define BRCryptoStatus_h + +#include "BRCryptoBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + CRYPTO_SUCCESS = 0, + // Generic catch-all failure. This should only be used as if creating a + // specific error code does not make sense (you really should create + // a specifc error code...). + CRYPTO_ERROR_FAILED, + + // Reference access + CRYPTO_ERROR_UNKNOWN_NODE = 10000, + CRYPTO_ERROR_UNKNOWN_TRANSFER, + CRYPTO_ERROR_UNKNOWN_ACCOUNT, + CRYPTO_ERROR_UNKNOWN_WALLET, + CRYPTO_ERROR_UNKNOWN_BLOCK, + CRYPTO_ERROR_UNKNOWN_LISTENER, + + // Node + CRYPTO_ERROR_NODE_NOT_CONNECTED = 20000, + + // Transfer + CRYPTO_ERROR_TRANSFER_HASH_MISMATCH = 30000, + CRYPTO_ERROR_TRANSFER_SUBMISSION, + + // Numeric + CRYPTO_ERROR_NUMERIC_PARSE = 40000, + + // Acount + // Wallet + // Block + // Listener + } BRCryptoStatus; + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoStatus_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoSync.h b/ThirdParty/breadwallet-core/include/BRCryptoSync.h new file mode 100644 index 000000000..b899127ec --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoSync.h @@ -0,0 +1,131 @@ +// +// BRCryptoSync.h +// BRCore +// +// Created by Ed Gamble on 11/27/19. +// Copyright © 2019 Breadwinner AG. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoSync_h +#define BRCryptoSync_h + +#ifdef __cplusplus +extern "C" { +#endif + + /// MARK: Sync Stopped Reason + + typedef enum { + CRYPTO_SYNC_STOPPED_REASON_COMPLETE, + CRYPTO_SYNC_STOPPED_REASON_REQUESTED, + CRYPTO_SYNC_STOPPED_REASON_UNKNOWN, + CRYPTO_SYNC_STOPPED_REASON_POSIX + } BRCryptoSyncStoppedReasonType; + + typedef struct { + BRCryptoSyncStoppedReasonType type; + union { + struct { + int errnum; + } posix; + } u; + } BRCryptoSyncStoppedReason; + + extern BRCryptoSyncStoppedReason + cryptoSyncStoppedReasonComplete(void); + + extern BRCryptoSyncStoppedReason + cryptoSyncStoppedReasonRequested(void); + + extern BRCryptoSyncStoppedReason + cryptoSyncStoppedReasonUnknown(void); + + extern BRCryptoSyncStoppedReason + cryptoSyncStoppedReasonPosix(int errnum); + + /** + * Return a descriptive message as to why the sync stopped. + * + *@return the detailed reason as a string or NULL + */ + extern char * + cryptoSyncStoppedReasonGetMessage(BRCryptoSyncStoppedReason *reason); + + /// MARK: Sync Mode + + /// + /// The modes supported for syncing of a wallet's transactions. These are the supported modes + /// but they are not necessarily available for any individual WalletKit execution. + /// Specifically, the API_ONLY mode may be supported but if the backend services are not + /// accessible, such as if a device is in 'airplane mode', then API_ONLY will not be available. + /// + typedef enum { + /** + * Use the BRD backend for all Core blockchain state. The BRD backend includes a 'submit + * transaction' interface. + */ + CRYPTO_SYNC_MODE_API_ONLY, + + /** + * Use the BRD backend for everything other than 'submit transaction' + */ + CRYPTO_SYNC_MODE_API_WITH_P2P_SEND, + + /** + * Use the BRD backend for an initial sync and then, once complete, use P2P. If a sync + * has not occurred in a while, use the BRD backend again before using P2P (so as to catch-up + * quickly) + */ + CRYPTO_SYNC_MODE_P2P_WITH_API_SYNC, + + /** + * Use acomplete block chain sync, even starting at block zero (but usually from a block + * derived from the accounts `earliestStartTime` (or the BIP-39 introduction block). + */ + CRYPTO_SYNC_MODE_P2P_ONLY + } BRCryptoSyncMode; + +#define NUMBER_OF_SYNC_MODES (1 + CRYPTO_SYNC_MODE_P2P_ONLY) + + extern const char * + cryptoSyncModeString (BRCryptoSyncMode m); + + /// MARK: Sync Depth + + typedef enum { + /** + * Sync from the block height of the last confirmed send transaction. + */ + CRYPTO_SYNC_DEPTH_FROM_LAST_CONFIRMED_SEND, + + /** + * Sync from the block height of the last trusted block; this is dependent on the + * blockchain and mode as to how it determines trust. + */ + CRYPTO_SYNC_DEPTH_FROM_LAST_TRUSTED_BLOCK, + + /** + * Sync from the block height of the point in time when the account was created. + */ + CRYPTO_SYNC_DEPTH_FROM_CREATION + } BRCryptoSyncDepth; + + /// The Percent Complete (0...100.0) derived from the last block processed relative to the + /// full block range in a sync. + typedef float BRCryptoSyncPercentComplete; + +#define AS_CRYPTO_SYNC_PERCENT_COMPLETE(number) ((BRCryptoSyncPercentComplete) (number)) + + /// The Timestamp (in the Unix epoch) of the last block processed in a sync. + typedef uint32_t BRCryptoSyncTimestamp; + +#define AS_CRYPTO_SYNC_TIMESTAMP(unixSeconds) ((BRCryptoSyncTimestamp) (unixSeconds)) +#define NO_CRYPTO_SYNC_TIMESTAMP (AS_CRYPTO_SYNC_TIMESTAMP (0)) + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoSync_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoTransfer.h b/ThirdParty/breadwallet-core/include/BRCryptoTransfer.h new file mode 100644 index 000000000..2ee958bc1 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoTransfer.h @@ -0,0 +1,336 @@ +// +// BRCryptoTransfer.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoTransfer_h +#define BRCryptoTransfer_h + +#include "BRCryptoHash.h" +#include "BRCryptoAddress.h" +#include "BRCryptoAmount.h" +#include "BRCryptoFeeBasis.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoTransferRecord *BRCryptoTransfer; + + /// MARK: Transfer Submission Result + + typedef enum { + CRYPTO_TRANSFER_SUBMIT_ERROR_UNKNOWN, + CRYPTO_TRANSFER_SUBMIT_ERROR_POSIX, + } BRCryptoTransferSubmitErrorType; + + typedef struct { + BRCryptoTransferSubmitErrorType type; + union { + struct { + int errnum; + } posix; + } u; + } BRCryptoTransferSubmitError; + + extern BRCryptoTransferSubmitError + cryptoTransferSubmitErrorUnknown(void); + + extern BRCryptoTransferSubmitError + cryptoTransferSubmitErrorPosix(int errnum); + + /** + * Return a descriptive message as to why the error occurred. + * + *@return the detailed reason as a string or NULL + */ + extern char * + cryptoTransferSubmitErrorGetMessage(BRCryptoTransferSubmitError *e); + + /// MARK: - Transfer State + + typedef enum { + CRYPTO_TRANSFER_STATE_CREATED, + CRYPTO_TRANSFER_STATE_SIGNED, + CRYPTO_TRANSFER_STATE_SUBMITTED, + CRYPTO_TRANSFER_STATE_INCLUDED, + CRYPTO_TRANSFER_STATE_ERRORED, + CRYPTO_TRANSFER_STATE_DELETED, + } BRCryptoTransferStateType; + + extern const char * + cryptoTransferStateTypeString (BRCryptoTransferStateType type); + +#define CRYPTO_TRANSFER_INCLUDED_ERROR_SIZE 16 + typedef struct { + BRCryptoTransferStateType type; + union { + struct { + uint64_t blockNumber; + uint64_t transactionIndex; + // This is not assuredly the including block's timestamp; it is the transaction's + // timestamp which varies depending on how the transaction was discovered. + uint64_t timestamp; + BRCryptoFeeBasis feeBasis; + + // transfer that have failed can be included too + BRCryptoBoolean success; + char error[CRYPTO_TRANSFER_INCLUDED_ERROR_SIZE + 1]; + } included; + + struct { + BRCryptoTransferSubmitError error; + } errored; + } u; + } BRCryptoTransferState; + + extern BRCryptoTransferState + cryptoTransferStateInit (BRCryptoTransferStateType type); + + extern BRCryptoTransferState + cryptoTransferStateIncludedInit (uint64_t blockNumber, + uint64_t transactionIndex, + uint64_t timestamp, + BRCryptoFeeBasis feeBasis, + BRCryptoBoolean success, + const char *error); + + extern BRCryptoTransferState + cryptoTransferStateErroredInit (BRCryptoTransferSubmitError error); + + extern BRCryptoTransferState + cryptoTransferStateCopy (BRCryptoTransferState *state); + + extern void + cryptoTransferStateRelease (BRCryptoTransferState *state); + + /// MARK: - Transfer Event + + typedef enum { + CRYPTO_TRANSFER_EVENT_CREATED, + CRYPTO_TRANSFER_EVENT_CHANGED, + CRYPTO_TRANSFER_EVENT_DELETED, + } BRCryptoTransferEventType; + + extern const char * + cryptoTransferEventTypeString (BRCryptoTransferEventType t); + + typedef struct { + BRCryptoTransferEventType type; + union { + struct { + BRCryptoTransferState old; + BRCryptoTransferState new; + } state; + } u; + } BRCryptoTransferEvent; + + /// MARK: - Transfer Direction + + typedef enum { + CRYPTO_TRANSFER_SENT, + CRYPTO_TRANSFER_RECEIVED, + CRYPTO_TRANSFER_RECOVERED + } BRCryptoTransferDirection; + + /// MARK: - Transfer Attribute + + typedef struct BRCryptoTransferAttributeRecord *BRCryptoTransferAttribute; + + extern const char * + cryptoTransferAttributeGetKey (BRCryptoTransferAttribute attribute); + + extern const char * // nullable + cryptoTransferAttributeGetValue (BRCryptoTransferAttribute attribute); + + extern void + cryptoTransferAttributeSetValue (BRCryptoTransferAttribute attribute, const char *value); + + extern BRCryptoBoolean + cryptoTransferAttributeIsRequired (BRCryptoTransferAttribute attribute); + + extern BRCryptoTransferAttribute + cryptoTransferAttributeCopy (BRCryptoTransferAttribute attribute); + + private_extern BRCryptoTransferAttribute + cryptoTransferAttributeCreate (const char *key, + const char *val, // nullable + BRCryptoBoolean isRequired); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoTransferAttribute, cryptoTransferAttribute); + + typedef enum { + CRYPTO_TRANSFER_ATTRIBUTE_VALIDATION_ERROR_REQUIRED_BUT_NOT_PROVIDED, + CRYPTO_TRANSFER_ATTRIBUTE_VALIDATION_ERROR_MISMATCHED_TYPE, + CRYPTO_TRANSFER_ATTRIBUTE_VALIDATION_ERROR_RELATIONSHIP_INCONSISTENCY + } BRCryptoTransferAttributeValidationError; + + /// MARK: - Transfer + + /** + * Returns the transfer's source address + * + * @param transfer the transfer + * + * @return the source address or NULL + */ + extern BRCryptoAddress + cryptoTransferGetSourceAddress (BRCryptoTransfer transfer); + + /** + * Returns the transfer's target address + * + * @param transfer the transfer + * + * @return the source address or NULL + */ + extern BRCryptoAddress + cryptoTransferGetTargetAddress (BRCryptoTransfer transfer); + + /** + * Returns the transfer's amount + * + * @param transfer the transfer + * + * @return the amount + */ + extern BRCryptoAmount + cryptoTransferGetAmount (BRCryptoTransfer transfer); + + /** + * Returns the transfer's amount after considering the direction + * + * If we received the transfer, the amount will be positive; if we sent the transfer, the + * amount will be negative; if the transfer is 'self directed', the amount will be zero. + * + * @param transfer the transfer + * + * @return the amount + */ + extern BRCryptoAmount + cryptoTransferGetAmountDirected (BRCryptoTransfer transfer); + + /** + * Returns the transfers amount after considering the direction and fee + * + * @param transfer the transfer + * + * @return the signed, net amoount + */ + extern BRCryptoAmount + cryptoTransferGetAmountDirectedNet (BRCryptoTransfer transfer); + + /** + * Returns the transfer's fee. Note that the `fee` and the `amount` may be in different + * currencies. + * + * @param transfer the transfer + * + * @return the fee + */ +// extern BRCryptoAmount +// cryptoTransferGetFee (BRCryptoTransfer transfer); + +// extern BRCryptoBoolean +// cryptoTransferExtractConfirmation (BRCryptoTransfer transfer, +// uint64_t *blockNumber, +// uint64_t *transactionIndex, +// uint64_t *timestamp, +// BRCryptoAmount *fee); + + extern BRCryptoTransferStateType + cryptoTransferGetStateType (BRCryptoTransfer transfer); + + extern BRCryptoTransferState + cryptoTransferGetState (BRCryptoTransfer transfer); + + extern BRCryptoBoolean + cryptoTransferIsSent (BRCryptoTransfer transfer); + + extern BRCryptoTransferDirection + cryptoTransferGetDirection (BRCryptoTransfer transfer); + + /** + * Returns the transfer's hash. This is the unique identifier for this transfer on the + * associated network's blockchain. + * + * @note: Uniqueness is TBD for Ethereum TOKEN transfers + * + * @param transfer the transfer + * + * @return the transfer's hash + */ + extern BRCryptoHash + cryptoTransferGetHash (BRCryptoTransfer transfer); + + extern BRCryptoUnit + cryptoTransferGetUnitForAmount (BRCryptoTransfer transfer); + + extern BRCryptoUnit + cryptoTransferGetUnitForFee (BRCryptoTransfer transfer); + + /** + * Returns the transfer's feeBasis. + * + * @param transfer the transfer + * + * @return the transfer's feeBasis + */ + extern BRCryptoFeeBasis + cryptoTransferGetEstimatedFeeBasis (BRCryptoTransfer transfer); + + extern BRCryptoFeeBasis + cryptoTransferGetConfirmedFeeBasis (BRCryptoTransfer transfer); + + extern size_t + cryptoTransferGetAttributeCount (BRCryptoTransfer transfer); + + extern BRCryptoTransferAttribute + cryptoTransferGetAttributeAt (BRCryptoTransfer transfer, + size_t index); + + extern BRCryptoBoolean + cryptoTransferEqual (BRCryptoTransfer transfer1, BRCryptoTransfer transfer2); + + /** + * Compares two transfers. + * + * The transfers are ordered according to the following algorithm: + * - IF neither transfer is in the INCLUDED state, they are ordered by pointer identity + * - ELSE IF one transfer is in the INCLUDED state, it is "lesser than" one that is NOT + * - ELSE both are in the INCLUDED state, order by timestamp, block number and transaction + * index (in that order), with those values being compared by magnitude + * + * In practice, this means that: + * - Transfer A (INCLUDED at time 0, block 0, index 0) is lesser than + * - Transfer B (INCLUDED at time 0, block 0, index 1) is lesser than + * - Transfer C (INCLUDED at time 0, block 1, index 0) is lesser than + * - Transfer D (INCLUDED at time 1, block 0, index 0) is lesser than + * - Transfer E (CREATED with pointer 0x10000000) is lesser than + * - Transfer F (SIGNED with pointer 0x20000000) is lesser than + * - Transfer G (CREATED with pointer 0x30000000) is lesser than + * - Transfer H (DELETED with pointer 0x40000000) + */ + extern BRCryptoComparison + cryptoTransferCompare (BRCryptoTransfer transfer1, BRCryptoTransfer transfer2); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoTransfer, cryptoTransfer); + + extern void + cryptoTransferExtractBlobAsBTC (BRCryptoTransfer transfer, + uint8_t **bytes, + size_t *bytesCount, + uint32_t *blockHeight, + uint32_t *timestamp); + + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoTransfer_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoUnit.h b/ThirdParty/breadwallet-core/include/BRCryptoUnit.h new file mode 100644 index 000000000..b2b9f6b42 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoUnit.h @@ -0,0 +1,88 @@ +// +// BRCryptoUnit.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoUnit_h +#define BRCryptoUnit_h + +#include "BRCryptoBase.h" +#include "BRCryptoCurrency.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct BRCryptoUnitRecord *BRCryptoUnit; + + private_extern BRCryptoUnit + cryptoUnitCreateAsBase (BRCryptoCurrency currency, + const char *code, + const char *name, + const char *symbol); + + private_extern BRCryptoUnit + cryptoUnitCreate (BRCryptoCurrency currency, + const char *code, + const char *name, + const char *symbol, + BRCryptoUnit baseUnit, + uint8_t powerOffset); + + + extern const char * + cryptoUnitGetUids(BRCryptoUnit unit); + + extern const char * + cryptoUnitGetName (BRCryptoUnit unit); + + extern const char * + cryptoUnitGetSymbol (BRCryptoUnit unit); + + /** + * Returns the unit's currency + * + * @param unit the Unit + * + * @return The currency w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoCurrency + cryptoUnitGetCurrency (BRCryptoUnit unit); + + extern BRCryptoBoolean + cryptoUnitHasCurrency (BRCryptoUnit unit, + BRCryptoCurrency currency); + + /** + * Returns the unit's base unit. If unit is itself the base unit then unit is returned + * + * @param unit The unit + * + * @return the base unit w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoUnit + cryptoUnitGetBaseUnit (BRCryptoUnit unit); + + extern uint8_t + cryptoUnitGetBaseDecimalOffset (BRCryptoUnit unit); + + extern BRCryptoBoolean + cryptoUnitIsCompatible (BRCryptoUnit u1, + BRCryptoUnit u2); + + extern BRCryptoBoolean + cryptoUnitIsIdentical (BRCryptoUnit u1, + BRCryptoUnit u2); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoUnit, cryptoUnit); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoUnit_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoWallet.h b/ThirdParty/breadwallet-core/include/BRCryptoWallet.h new file mode 100644 index 000000000..a6ed58e19 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoWallet.h @@ -0,0 +1,300 @@ +// +// BRCryptoWallet.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoWallet_h +#define BRCryptoWallet_h + +#include "BRCryptoStatus.h" +#include "BRCryptoKey.h" +#include "BRCryptoNetwork.h" // NetworkFee +#include "BRCryptoPayment.h" +#include "BRCryptoFeeBasis.h" +#include "BRCryptoTransfer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /// MARK: Forward Declarations + + typedef struct BRCryptoWalletSweeperRecord *BRCryptoWalletSweeper; + + /// MARK: Wallet Event + + typedef enum { + CRYPTO_WALLET_STATE_CREATED, + CRYPTO_WALLET_STATE_DELETED + } BRCryptoWalletState; + + typedef enum { + CRYPTO_WALLET_EVENT_CREATED, + CRYPTO_WALLET_EVENT_CHANGED, + CRYPTO_WALLET_EVENT_DELETED, + + CRYPTO_WALLET_EVENT_TRANSFER_ADDED, + CRYPTO_WALLET_EVENT_TRANSFER_CHANGED, + CRYPTO_WALLET_EVENT_TRANSFER_SUBMITTED, + CRYPTO_WALLET_EVENT_TRANSFER_DELETED, + + CRYPTO_WALLET_EVENT_BALANCE_UPDATED, + CRYPTO_WALLET_EVENT_FEE_BASIS_UPDATED, + + CRYPTO_WALLET_EVENT_FEE_BASIS_ESTIMATED, + } BRCryptoWalletEventType; + + extern const char * + cryptoWalletEventTypeString (BRCryptoWalletEventType t); + + typedef struct { + BRCryptoWalletEventType type; + union { + struct { + BRCryptoWalletState oldState; + BRCryptoWalletState newState; + } state; + + struct { + /// Handler must 'give' + BRCryptoTransfer value; + } transfer; + + struct { + /// Handler must 'give' + BRCryptoAmount amount; + } balanceUpdated; + + struct { + /// Handler must 'give' + BRCryptoFeeBasis basis; + } feeBasisUpdated; + + struct { + /// Handler must 'give' basis + BRCryptoStatus status; + BRCryptoCookie cookie; + BRCryptoFeeBasis basis; + } feeBasisEstimated; + } u; + } BRCryptoWalletEvent; + + extern BRCryptoWalletState + cryptoWalletGetState (BRCryptoWallet wallet); + + /** + * Returns the wallet's currency + * + * @param wallet the wallet + * + * @return The currency w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoCurrency + cryptoWalletGetCurrency (BRCryptoWallet wallet); + + /** + * Returns the wallet's (default) unit. Used for *display* of the wallet's balance. + * + * @param wallet The wallet + * + * @return the unit w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoUnit + cryptoWalletGetUnit (BRCryptoWallet wallet); + + extern BRCryptoCurrency + cryptoWalletGetCurrencyForFee (BRCryptoWallet wallet); + + /** + * Returns the wallet's fee unit. + * + * @param wallet The wallet + * + * @return the fee unit w/ an incremented reference count (aka 'taken') + */ + extern BRCryptoUnit + cryptoWalletGetUnitForFee (BRCryptoWallet wallet); + + /** + * Returns the wallets balance + * + * @param wallet the wallet + * + * @return the balance + */ + extern BRCryptoAmount + cryptoWalletGetBalance (BRCryptoWallet wallet); + + extern BRCryptoAmount /* nullable */ + cryptoWalletGetBalanceMinimum (BRCryptoWallet wallet); + + extern BRCryptoAmount /* nullable */ + cryptoWalletGetBalanceMaximum (BRCryptoWallet wallet); + + extern BRCryptoBoolean + cryptoWalletHasTransfer (BRCryptoWallet wallet, + BRCryptoTransfer transfer); + + /** + * Returns a newly allocated array of the wallet's transfers. + * + * The caller is responsible for deallocating the returned array using + * free(). + * + * @param wallet the wallet + * @param count the number of transfers returned + * + * @return An array of transfers w/ an incremented reference count (aka 'taken') + * or NULL if there are no transfers in the wallet. + */ + extern BRCryptoTransfer * + cryptoWalletGetTransfers (BRCryptoWallet wallet, + size_t *count); + + /** + * Returns a 'new' adddress from `wallet` according to the provided `addressScheme`. For BTC + * this is a segwit or a bech32 address. Note that the returned address is not associated with + * `wallet` and thus one runs the risk of using a BRCryptoAddress w/ the wrong BRCryptoWallet + */ + extern BRCryptoAddress + cryptoWalletGetAddress (BRCryptoWallet wallet, + BRCryptoAddressScheme addressScheme); + + /** + * Check if `wallet` has `address`. Checks that `address` has been used already by `wallet` + * or if `address` is the *next* address from `wallet` + */ + extern BRCryptoBoolean + cryptoWalletHasAddress (BRCryptoWallet wallet, + BRCryptoAddress address); + + extern BRCryptoFeeBasis + cryptoWalletGetDefaultFeeBasis (BRCryptoWallet wallet); + + extern void + cryptoWalletSetDefaultFeeBasis (BRCryptoWallet wallet, + BRCryptoFeeBasis feeBasis); + + extern size_t + cryptoWalletGetTransferAttributeCount (BRCryptoWallet wallet, + BRCryptoAddress target); + + extern BRCryptoTransferAttribute + cryptoWalletGetTransferAttributeAt (BRCryptoWallet wallet, + BRCryptoAddress target, + size_t index); + + extern BRCryptoTransferAttributeValidationError + cryptoWalletValidateTransferAttribute (BRCryptoWallet wallet, + OwnershipKept BRCryptoTransferAttribute attribute, + BRCryptoBoolean *validates); + + extern BRCryptoTransferAttributeValidationError + cryptoWalletValidateTransferAttributes (BRCryptoWallet wallet, + size_t attributesCount, + OwnershipKept BRCryptoTransferAttribute *attribute, + BRCryptoBoolean *validates); + + /** + * Create a transfer. + * + * @param wallet The wallet providing the amount + * @param target The target address; this must be consistent with the provied wallet's address + * @param amount the amount to transfer + * @param estimatedFeeBasis the fees one is willing to + * + * @return the transfer or NULL + */ + extern BRCryptoTransfer + cryptoWalletCreateTransfer (BRCryptoWallet wallet, + BRCryptoAddress target, + BRCryptoAmount amount, + BRCryptoFeeBasis estimatedFeeBasis, + size_t attributesCount, + OwnershipKept BRCryptoTransferAttribute *attributes); + + extern BRCryptoTransfer + cryptoWalletCreateTransferForWalletSweep (BRCryptoWallet wallet, + BRCryptoWalletSweeper sweeper, + BRCryptoFeeBasis estimatedFeeBasis); + + extern BRCryptoTransfer + cryptoWalletCreateTransferForPaymentProtocolRequest (BRCryptoWallet wallet, + BRCryptoPaymentProtocolRequest request, + BRCryptoFeeBasis estimatedFeeBasis); + + extern void + cryptoWalletAddTransfer (BRCryptoWallet wallet, BRCryptoTransfer transfer); + + extern void + cryptoWalletRemTransfer (BRCryptoWallet wallet, BRCryptoTransfer transfer); + + extern BRCryptoFeeBasis + cryptoWalletCreateFeeBasis (BRCryptoWallet wallet, + BRCryptoAmount pricePerCostFactor, + double costFactor); + + extern BRCryptoBoolean + cryptoWalletEqual (BRCryptoWallet w1, BRCryptoWallet w2); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoWallet, cryptoWallet); + + /// MARK: Wallet Sweeper + + typedef enum { + CRYPTO_WALLET_SWEEPER_SUCCESS, + CRYPTO_WALLET_SWEEPER_UNSUPPORTED_CURRENCY, + CRYPTO_WALLET_SWEEPER_INVALID_KEY, + CRYPTO_WALLET_SWEEPER_INVALID_ARGUMENTS, + CRYPTO_WALLET_SWEEPER_INVALID_TRANSACTION, + CRYPTO_WALLET_SWEEPER_INVALID_SOURCE_WALLET, + CRYPTO_WALLET_SWEEPER_NO_TRANSFERS_FOUND, + CRYPTO_WALLET_SWEEPER_INSUFFICIENT_FUNDS, + CRYPTO_WALLET_SWEEPER_UNABLE_TO_SWEEP, + + // calling a sweeper function for the wrong type + CRYPTO_WALLET_SWEEPER_ILLEGAL_OPERATION, + } BRCryptoWalletSweeperStatus; + + extern BRCryptoWalletSweeperStatus + cryptoWalletSweeperValidateSupported (BRCryptoNetwork network, + BRCryptoCurrency currency, + BRCryptoKey key, + BRCryptoWallet wallet); + + extern BRCryptoWalletSweeper + cryptoWalletSweeperCreateAsBtc (BRCryptoNetwork network, + BRCryptoCurrency currency, + BRCryptoKey key, + BRCryptoAddressScheme scheme); + + extern void + cryptoWalletSweeperRelease (BRCryptoWalletSweeper sweeper); + + extern BRCryptoWalletSweeperStatus + cryptoWalletSweeperHandleTransactionAsBTC (BRCryptoWalletSweeper sweeper, + OwnershipKept uint8_t *transaction, + size_t transactionLen); + + extern BRCryptoKey + cryptoWalletSweeperGetKey (BRCryptoWalletSweeper sweeper); + + extern char * + cryptoWalletSweeperGetAddress (BRCryptoWalletSweeper sweeper); + + extern BRCryptoAmount + cryptoWalletSweeperGetBalance (BRCryptoWalletSweeper sweeper); + + extern BRCryptoWalletSweeperStatus + cryptoWalletSweeperValidate (BRCryptoWalletSweeper sweeper); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoWallet_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoWalletManager.h b/ThirdParty/breadwallet-core/include/BRCryptoWalletManager.h new file mode 100644 index 000000000..b3afc23cc --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoWalletManager.h @@ -0,0 +1,408 @@ +// +// BRCryptoWalletManager.h +// BRCore +// +// Created by Ed Gamble on 3/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoWalletManager_h +#define BRCryptoWalletManager_h + +#include "BRCryptoBase.h" +#include "BRCryptoKey.h" +#include "BRCryptoNetwork.h" +#include "BRCryptoPeer.h" +#include "BRCryptoAccount.h" +#include "BRCryptoTransfer.h" +#include "BRCryptoWallet.h" +#include "BRCryptoSync.h" +#include "BRCryptoWalletManagerClient.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /// MARK: - Wallet Manager Disconnect Reason + + typedef enum { + CRYPTO_WALLET_MANAGER_DISCONNECT_REASON_REQUESTED, + CRYPTO_WALLET_MANAGER_DISCONNECT_REASON_UNKNOWN, + CRYPTO_WALLET_MANAGER_DISCONNECT_REASON_POSIX + } BRCryptoWalletManagerDisconnectReasonType; + + typedef struct { + BRCryptoWalletManagerDisconnectReasonType type; + union { + struct { + int errnum; + } posix; + } u; + } BRCryptoWalletManagerDisconnectReason; + + extern BRCryptoWalletManagerDisconnectReason + cryptoWalletManagerDisconnectReasonRequested (void); + + extern BRCryptoWalletManagerDisconnectReason + cryptoWalletManagerDisconnectReasonUnknown (void); + + extern BRCryptoWalletManagerDisconnectReason + cryptoWalletManagerDisconnectReasonPosix (int errnum); + + /** + * Return a descriptive message as to why the disconnect occurred. + * + *@return the detailed reason as a string or NULL + */ + extern char * + cryptoWalletManagerDisconnectReasonGetMessage (BRCryptoWalletManagerDisconnectReason *reason); + + /// MARK: Wallet Manager Event + + typedef enum { + CRYPTO_WALLET_MANAGER_STATE_CREATED, + CRYPTO_WALLET_MANAGER_STATE_DISCONNECTED, + CRYPTO_WALLET_MANAGER_STATE_CONNECTED, + CRYPTO_WALLET_MANAGER_STATE_SYNCING, + CRYPTO_WALLET_MANAGER_STATE_DELETED + } BRCryptoWalletManagerStateType; + + typedef struct { + BRCryptoWalletManagerStateType type; + union { + struct { + BRCryptoWalletManagerDisconnectReason reason; + } disconnected; + } u; + } BRCryptoWalletManagerState; + + extern const BRCryptoWalletManagerState CRYPTO_WALLET_MANAGER_STATE_CREATED_INIT; + + typedef enum { + CRYPTO_WALLET_MANAGER_EVENT_CREATED, + CRYPTO_WALLET_MANAGER_EVENT_CHANGED, + CRYPTO_WALLET_MANAGER_EVENT_DELETED, + + CRYPTO_WALLET_MANAGER_EVENT_WALLET_ADDED, + CRYPTO_WALLET_MANAGER_EVENT_WALLET_CHANGED, + CRYPTO_WALLET_MANAGER_EVENT_WALLET_DELETED, + + // wallet: added, ... + CRYPTO_WALLET_MANAGER_EVENT_SYNC_STARTED, + CRYPTO_WALLET_MANAGER_EVENT_SYNC_CONTINUES, + CRYPTO_WALLET_MANAGER_EVENT_SYNC_STOPPED, + CRYPTO_WALLET_MANAGER_EVENT_SYNC_RECOMMENDED, + + CRYPTO_WALLET_MANAGER_EVENT_BLOCK_HEIGHT_UPDATED, + } BRCryptoWalletManagerEventType; + + extern const char * + cryptoWalletManagerEventTypeString (BRCryptoWalletManagerEventType t); + + typedef struct { + BRCryptoWalletManagerEventType type; + union { + struct { + BRCryptoWalletManagerState oldValue; + BRCryptoWalletManagerState newValue; + } state; + + struct { + /// Handler must 'give' + BRCryptoWallet value; + } wallet; + + struct { + BRCryptoSyncTimestamp timestamp; + BRCryptoSyncPercentComplete percentComplete; + } syncContinues; + + struct { + BRCryptoSyncStoppedReason reason; + } syncStopped; + + struct { + BRCryptoSyncDepth depth; + } syncRecommended; + + struct { + uint64_t value; + } blockHeight; + } u; + } BRCryptoWalletManagerEvent; + + /// MARK: Listener + + typedef void *BRCryptoCWMListenerContext; + + /// Handler must 'give': manager, event.wallet.value + typedef void (*BRCryptoCWMListenerWalletManagerEvent) (BRCryptoCWMListenerContext context, + BRCryptoWalletManager manager, + BRCryptoWalletManagerEvent event); + + /// Handler must 'give': manager, wallet, event.* + typedef void (*BRCryptoCWMListenerWalletEvent) (BRCryptoCWMListenerContext context, + BRCryptoWalletManager manager, + BRCryptoWallet wallet, + BRCryptoWalletEvent event); + + /// Handler must 'give': manager, wallet, transfer + typedef void (*BRCryptoCWMListenerTransferEvent) (BRCryptoCWMListenerContext context, + BRCryptoWalletManager manager, + BRCryptoWallet wallet, + BRCryptoTransfer transfer, + BRCryptoTransferEvent event); + + typedef struct { + BRCryptoCWMListenerContext context; + BRCryptoCWMListenerWalletManagerEvent walletManagerEventCallback; + BRCryptoCWMListenerWalletEvent walletEventCallback; + BRCryptoCWMListenerTransferEvent transferEventCallback; + } BRCryptoCWMListener; + + /// MARK: Wallet Manager + + /// Can return NULL + extern BRCryptoWalletManager + cryptoWalletManagerCreate (BRCryptoCWMListener listener, + BRCryptoCWMClient client, + BRCryptoAccount account, + BRCryptoNetwork network, + BRCryptoSyncMode mode, + BRCryptoAddressScheme scheme, + const char *path); + + extern BRCryptoNetwork + cryptoWalletManagerGetNetwork (BRCryptoWalletManager cwm); + + extern BRCryptoAccount + cryptoWalletManagerGetAccount (BRCryptoWalletManager cwm); + + extern BRCryptoSyncMode + cryptoWalletManagerGetMode (BRCryptoWalletManager cwm); + + extern void + cryptoWalletManagerSetMode (BRCryptoWalletManager cwm, BRCryptoSyncMode mode); + + extern BRCryptoWalletManagerState + cryptoWalletManagerGetState (BRCryptoWalletManager cwm); + + extern BRCryptoAddressScheme + cryptoWalletManagerGetAddressScheme (BRCryptoWalletManager cwm); + + extern void + cryptoWalletManagerSetAddressScheme (BRCryptoWalletManager cwm, + BRCryptoAddressScheme scheme); + + extern const char * + cryptoWalletManagerGetPath (BRCryptoWalletManager cwm); + + extern void + cryptoWalletManagerSetNetworkReachable (BRCryptoWalletManager cwm, + BRCryptoBoolean isNetworkReachable); + + extern BRCryptoBoolean + cryptoWalletManagerHasWallet (BRCryptoWalletManager cwm, + BRCryptoWallet wallet); + + extern BRCryptoWallet + cryptoWalletManagerGetWallet (BRCryptoWalletManager cwm); + + extern void + cryptoWalletManagerAddWallet (BRCryptoWalletManager cwm, + BRCryptoWallet wallet); + + extern void + cryptoWalletManagerRemWallet (BRCryptoWalletManager cwm, + BRCryptoWallet wallet); + + /** + * Returns a newly allocated array of the managers's wallets. + * + * The caller is responsible for deallocating the returned array using + * free(). + * + * @param cwm the wallet manager + * @param count the number of wallets returned + * + * @return An array of wallets w/ an incremented reference count (aka 'taken') + * or NULL if there are no wallters in the manager. + */ + extern BRCryptoWallet * + cryptoWalletManagerGetWallets (BRCryptoWalletManager cwm, + size_t *count); + + extern BRCryptoWallet + cryptoWalletManagerGetWalletForCurrency (BRCryptoWalletManager cwm, + BRCryptoCurrency currency); + + extern BRCryptoWallet + cryptoWalletManagerRegisterWallet (BRCryptoWalletManager cwm, + BRCryptoCurrency currency); + + extern void + cryptoWalletManagerStop (BRCryptoWalletManager cwm); + + extern void + cryptoWalletManagerConnect (BRCryptoWalletManager cwm, + BRCryptoPeer peer); + + extern void + cryptoWalletManagerDisconnect (BRCryptoWalletManager cwm); + + extern void + cryptoWalletManagerSync (BRCryptoWalletManager cwm); + + extern void + cryptoWalletManagerSyncToDepth (BRCryptoWalletManager cwm, + BRCryptoSyncDepth depth); + + // TODO: Workaround to create a TransferEvent.created for GEN (required CWM) + extern BRCryptoTransfer + cryptoWalletManagerCreateTransfer (BRCryptoWalletManager cwm, + BRCryptoWallet wallet, + BRCryptoAddress target, + BRCryptoAmount amount, + BRCryptoFeeBasis estimatedFeeBasis, + size_t attributesCount, + OwnershipKept BRCryptoTransferAttribute *attributes); + + extern BRCryptoBoolean + cryptoWalletManagerSign (BRCryptoWalletManager cwm, + BRCryptoWallet wallet, + BRCryptoTransfer transfer, + const char *paperKey); + + extern void + cryptoWalletManagerSubmit (BRCryptoWalletManager cwm, + BRCryptoWallet wid, + BRCryptoTransfer tid, + const char *paperKey); + + extern void + cryptoWalletManagerSubmitForKey (BRCryptoWalletManager cwm, + BRCryptoWallet wallet, + BRCryptoTransfer transfer, + BRCryptoKey key); + + extern void + cryptoWalletManagerSubmitSigned (BRCryptoWalletManager cwm, + BRCryptoWallet wallet, + BRCryptoTransfer transfer); + + /** + * Estimate the wallet's maximum or minimun transfer amount. + */ + extern BRCryptoAmount + cryptoWalletManagerEstimateLimit (BRCryptoWalletManager manager, + BRCryptoWallet wallet, + BRCryptoBoolean asMaximum, + BRCryptoAddress target, + BRCryptoNetworkFee fee, + BRCryptoBoolean *needEstimate, + BRCryptoBoolean *isZeroIfInsuffientFunds); + + /** + * Estimate the fee to transfer `amount` from `wallet` using the `feeBasis`. Return an amount + * represented in the wallet's fee currency. + * + * @param manager the manager + * @param wallet the wallet + * @param amount the amount to transfer + * @param feeBasis the fee basis for the transfer + * + * @return the fee + */ + + extern void + cryptoWalletManagerEstimateFeeBasis (BRCryptoWalletManager manager, + BRCryptoWallet wallet, + BRCryptoCookie cookie, + BRCryptoAddress target, + BRCryptoAmount amount, + BRCryptoNetworkFee fee); + + extern void + cryptoWalletManagerEstimateFeeBasisForWalletSweep (BRCryptoWalletManager manager, + BRCryptoWallet wallet, + BRCryptoCookie cookie, + BRCryptoWalletSweeper sweeper, + BRCryptoNetworkFee fee); + + extern void + cryptoWalletManagerEstimateFeeBasisForPaymentProtocolRequest (BRCryptoWalletManager manager, + BRCryptoWallet wallet, + BRCryptoCookie cookie, + BRCryptoPaymentProtocolRequest request, + BRCryptoNetworkFee fee); + + extern void + cryptoWalletManagerWipe (BRCryptoNetwork network, + const char *path); + + DECLARE_CRYPTO_GIVE_TAKE (BRCryptoWalletManager, cryptoWalletManager); + + /// MARK: Wallet Migrator + + typedef struct BRCryptoWalletMigratorRecord *BRCryptoWalletMigrator; + + typedef enum { + CRYPTO_WALLET_MIGRATOR_SUCCESS, + CRYPTO_WALLET_MIGRATOR_ERROR_TRANSACTION, + CRYPTO_WALLET_MIGRATOR_ERROR_BLOCK, + CRYPTO_WALLET_MIGRATOR_ERROR_PEER + } BRCryptoWalletMigratorStatusType; + + typedef struct { + BRCryptoWalletMigratorStatusType type; + // union {} u; + } BRCryptoWalletMigratorStatus; + + extern BRCryptoWalletMigrator // NULL on error + cryptoWalletMigratorCreate (BRCryptoNetwork network, + const char *storagePath); + + extern void + cryptoWalletMigratorRelease (BRCryptoWalletMigrator migrator); + + extern BRCryptoWalletMigratorStatus + cryptoWalletMigratorHandleTransactionAsBTC (BRCryptoWalletMigrator migrator, + const uint8_t *bytes, + size_t bytesCount, + uint32_t blockHeight, + uint32_t timestamp); + + extern BRCryptoWalletMigratorStatus + cryptoWalletMigratorHandleBlockAsBTC (BRCryptoWalletMigrator migrator, + BRCryptoData32 hash, + uint32_t height, + uint32_t nonce, + uint32_t target, + uint32_t txCount, + uint32_t version, + uint32_t timestamp, + uint8_t *flags, size_t flagsLen, + BRCryptoData32 *hashes, size_t hashesCount, + BRCryptoData32 merkleRoot, + BRCryptoData32 prevBlock); + + extern BRCryptoWalletMigratorStatus + cryptoWalletMigratorHandleBlockBytesAsBTC (BRCryptoWalletMigrator migrator, + const uint8_t *bytes, + size_t bytesCount, + uint32_t height); + + extern BRCryptoWalletMigratorStatus + cryptoWalletMigratorHandlePeerAsBTC (BRCryptoWalletMigrator migrator, + uint32_t address, + uint16_t port, + uint64_t services, + uint32_t timestamp); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoWalletManager_h */ diff --git a/ThirdParty/breadwallet-core/include/BRCryptoWalletManagerClient.h b/ThirdParty/breadwallet-core/include/BRCryptoWalletManagerClient.h new file mode 100644 index 000000000..dffcbeb78 --- /dev/null +++ b/ThirdParty/breadwallet-core/include/BRCryptoWalletManagerClient.h @@ -0,0 +1,387 @@ +// +// BRCryptoWalletManagerClient.h +// BRCrypto +// +// Created by Michael Carrara on 6/19/19. +// Copyright © 2019 breadwallet. All rights reserved. +// +// See the LICENSE file at the project root for license information. +// See the CONTRIBUTORS file at the project root for a list of contributors. + +#ifndef BRCryptoWalletManagerClient_h +#define BRCryptoWalletManagerClient_h + +#include "BRCryptoBase.h" +#include "BRCryptoNetwork.h" +#include "BRCryptoAccount.h" +#include "BRCryptoStatus.h" +#include "BRCryptoTransfer.h" +#include "BRCryptoWallet.h" + +#ifdef __cplusplus +extern "C" { +#endif + + typedef void *BRCryptoCWMClientContext; + + typedef struct BRCryptoCWMClientCallbackStateRecord *BRCryptoCWMClientCallbackState; + + typedef void + (*BRCryptoCWMEthGetEtherBalanceCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *address); + + typedef void + (*BRCryptoCWMEthGetTokenBalanceCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *address, + OwnershipKept const char *tokenAddress); + + typedef void + (*BRCryptoCWMEthGetGasPriceCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network); + + typedef void + (*BRCryptoCWMEthEstimateGasCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *from, + OwnershipKept const char *to, + OwnershipKept const char *amount, + OwnershipKept const char *price, + OwnershipKept const char *data); + + typedef void + (*BRCryptoCWMEthSubmitTransactionCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *transaction); + + typedef void + (*BRCryptoCWMEthGetTransactionsCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *address, + uint64_t begBlockNumber, + uint64_t endBlockNumber); + + typedef void + (*BRCryptoCWMEthGetLogsCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *contract, + OwnershipKept const char *address, + OwnershipKept const char *event, + uint64_t begBlockNumber, + uint64_t endBlockNumber); + + typedef void + (*BRCryptoCWMEthGetBlocksCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *address, // disappears immediately + unsigned int interests, + uint64_t blockNumberStart, + uint64_t blockNumberStop); + + typedef void + (*BRCryptoCWMEthGetTokensCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + typedef void + (*BRCryptoCWMEthGetBlockNumberCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network); + + typedef void + (*BRCryptoCWMEthGetNonceCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *network, + OwnershipKept const char *address); + + typedef struct { + BRCryptoCWMEthGetEtherBalanceCallback funcGetEtherBalance; + BRCryptoCWMEthGetTokenBalanceCallback funcGetTokenBalance; + BRCryptoCWMEthGetGasPriceCallback funcGetGasPrice; + BRCryptoCWMEthEstimateGasCallback funcEstimateGas; + BRCryptoCWMEthSubmitTransactionCallback funcSubmitTransaction; + BRCryptoCWMEthGetTransactionsCallback funcGetTransactions; // announce one-by-one + BRCryptoCWMEthGetLogsCallback funcGetLogs; // announce one-by-one + BRCryptoCWMEthGetBlocksCallback funcGetBlocks; + BRCryptoCWMEthGetTokensCallback funcGetTokens; // announce one-by-one + BRCryptoCWMEthGetBlockNumberCallback funcGetBlockNumber; + BRCryptoCWMEthGetNonceCallback funcGetNonce; + } BRCryptoCWMClientETH; + + typedef void + (*BRCryptoCWMBtcGetBlockNumberCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + typedef void + (*BRCryptoCWMBtcGetTransactionsCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char **addresses, + size_t addressCount, + uint64_t begBlockNumber, + uint64_t endBlockNumber); + + typedef void + (*BRCryptoCWMBtcSubmitTransactionCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept uint8_t *transaction, + size_t transactionLength, + OwnershipKept const char *hashAsHex); + + typedef struct { + BRCryptoCWMBtcGetBlockNumberCallback funcGetBlockNumber; + BRCryptoCWMBtcGetTransactionsCallback funcGetTransactions; + BRCryptoCWMBtcSubmitTransactionCallback funcSubmitTransaction; + } BRCryptoCWMClientBTC; + + /// MARK: GEN Callbacks + + typedef void + (*BRCryptoCWMGenGetBlockNumberCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + typedef void + (*BRCryptoCWMGenGetTransactionsCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *address, + uint64_t begBlockNumber, + uint64_t endBlockNumber); + + typedef void + (*BRCryptoCWMGenGetTransfersCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *address, + uint64_t begBlockNumber, + uint64_t endBlockNumber); + + typedef void + (*BRCryptoCWMGenSubmitTransactionCallback) (BRCryptoCWMClientContext context, + OwnershipGiven BRCryptoWalletManager manager, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept uint8_t *transaction, + size_t transactionLength, + OwnershipKept const char *hashAsHex); + + typedef struct { + BRCryptoCWMGenGetBlockNumberCallback funcGetBlockNumber; + BRCryptoCWMGenGetTransactionsCallback funcGetTransactions; + BRCryptoCWMGenGetTransfersCallback funcGetTransfers; + BRCryptoCWMGenSubmitTransactionCallback funcSubmitTransaction; + } BRCryptoCWMClientGEN; + + typedef struct { + BRCryptoCWMClientContext context; + BRCryptoCWMClientBTC btc; + BRCryptoCWMClientETH eth; + BRCryptoCWMClientGEN gen; + } BRCryptoCWMClient; + + extern void + cwmAnnounceGetBlockNumberSuccessAsInteger (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + uint64_t blockNumber); + + extern void + cwmAnnounceGetBlockNumberSuccessAsString (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *blockNumber); + + extern void + cwmAnnounceGetBlockNumberFailure (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + extern void + cwmAnnounceGetTransactionsItemBTC (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + BRCryptoTransferStateType status, + OwnershipKept uint8_t *transaction, + size_t transactionLength, + uint64_t timestamp, + uint64_t blockHeight); + + extern void + cwmAnnounceGetTransactionsItemETH (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *hash, + OwnershipKept const char *from, + OwnershipKept const char *to, + OwnershipKept const char *contract, + OwnershipKept const char *amount, // value + OwnershipKept const char *gasLimit, + OwnershipKept const char *gasPrice, + OwnershipKept const char *data, + OwnershipKept const char *nonce, + OwnershipKept const char *gasUsed, + OwnershipKept const char *blockNumber, + OwnershipKept const char *blockHash, + OwnershipKept const char *blockConfirmations, + OwnershipKept const char *blockTransactionIndex, + OwnershipKept const char *blockTimestamp, + // cumulative gas used, + // confirmations + // txreceipt_status + OwnershipKept const char *isError); + + extern void + cwmAnnounceGetTransactionsItemGEN (BRCryptoWalletManager cwm, + BRCryptoCWMClientCallbackState callbackState, + BRCryptoTransferStateType status, + uint8_t *transaction, + size_t transactionLength, + uint64_t timestamp, + uint64_t blockHeight); + + extern void + cwmAnnounceGetTransferItemGEN (BRCryptoWalletManager cwm, + BRCryptoCWMClientCallbackState callbackState, + BRCryptoTransferStateType status, + OwnershipKept const char *hash, + OwnershipKept const char *uids, + OwnershipKept const char *from, + OwnershipKept const char *to, + OwnershipKept const char *amount, + OwnershipKept const char *currency, + OwnershipKept const char *fee, + uint64_t timestamp, + uint64_t blockHeight, + size_t attributesCount, + OwnershipKept const char **attributeKeys, + OwnershipKept const char **attributeVals); + + extern void + cwmAnnounceGetTransactionsComplete (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + BRCryptoBoolean success); + + extern void + cwmAnnounceGetTransfersComplete (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + BRCryptoBoolean success); + + extern void + cwmAnnounceSubmitTransferSuccess (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + extern void + cwmAnnounceSubmitTransferSuccessForHash (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + const char *hash); + + extern void + cwmAnnounceSubmitTransferFailure (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + extern void + cwmAnnounceGetBalanceSuccess (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + const char *balance); + + extern void + cwmAnnounceGetBalanceFailure (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + extern void + cwmAnnounceGetBlocksSuccess (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + int blockNumbersCount, + uint64_t *blockNumbers); + + extern void + cwmAnnounceGetBlocksFailure (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + extern void + cwmAnnounceGetGasPriceSuccess (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + const char *gasPrice); + + extern void + cwmAnnounceGetGasPriceFailure (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + + extern void + cwmAnnounceGetGasEstimateSuccess (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + const char *gasEstimate, + const char *gasPrice); + + extern void + cwmAnnounceGetGasEstimateFailure (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + BRCryptoStatus status); + + extern void + cwmAnnounceGetLogsItem(OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *strHash, + OwnershipKept const char *strContract, + int topicCount, + OwnershipKept const char **arrayTopics, + OwnershipKept const char *strData, + OwnershipKept const char *strGasPrice, + OwnershipKept const char *strGasUsed, + OwnershipKept const char *strLogIndex, + OwnershipKept const char *strBlockNumber, + OwnershipKept const char *strBlockTransactionIndex, + OwnershipKept const char *strBlockTimestamp); + + extern void + cwmAnnounceGetLogsComplete(OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + BRCryptoBoolean success); + + extern void + cwmAnnounceGetTokensItem (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *address, + OwnershipKept const char *symbol, + OwnershipKept const char *name, + OwnershipKept const char *description, + unsigned int decimals, + OwnershipKept const char *strDefaultGasLimit, + OwnershipKept const char *strDefaultGasPrice); + + extern void + cwmAnnounceGetTokensComplete (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + BRCryptoBoolean success); + + extern void + cwmAnnounceGetNonceSuccess (OwnershipKept BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState, + OwnershipKept const char *address, + OwnershipKept const char *nonce); + + extern void + cwmAnnounceGetNonceFailure (BRCryptoWalletManager cwm, + OwnershipGiven BRCryptoCWMClientCallbackState callbackState); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCryptoWalletManagerClient_h */ diff --git a/ThirdParty/breadwallet-core/support/BRBase.h b/ThirdParty/breadwallet-core/support/BRBase.h new file mode 100644 index 000000000..078a5b069 --- /dev/null +++ b/ThirdParty/breadwallet-core/support/BRBase.h @@ -0,0 +1,61 @@ +// +// BRBase.h +// BRCore +// +// Created by Michael Carrara on 7/1/19. +// Copyright © 2019 breadwallet LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef BRBase_h +#define BRBase_h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OwnershipGiven +#define OwnershipKept + +/** + * A Block Height in a block chain. Note: uint32_t is perhaps enough; conservatively use + * uint64_t. + */ +typedef uint64_t BRBlockHeight; + +// Inclusive block chain height bounds +#define BLOCK_HEIGHT_MINIMUM (0) +#define BLOCK_HEIGHT_MAXIMUM (UINT64_MAX - 5) // Leave room for special values + +/** + * Check if `height` is between the MINIMUM and MAXIMUM values. + */ +#define BLOCK_HEIGHT_IS_IN_RANGE(height) \ + (BLOCK_HEIGHT_MINIMUM <= (height) && (height) <= BLOCK_HEIGHT_MAXIMUM) + +// Special values +#define BLOCK_HEIGHT_UNBOUND (UINT64_MAX) + +#ifdef __cplusplus +} +#endif + +#endif /* BRBase_h */ diff --git a/ThirdParty/breadwallet-core/support/BRFileService.c b/ThirdParty/breadwallet-core/support/BRFileService.c index 653531140..ced21b70a 100644 --- a/ThirdParty/breadwallet-core/support/BRFileService.c +++ b/ThirdParty/breadwallet-core/support/BRFileService.c @@ -30,10 +30,84 @@ #include #include #include +#include +#include "sqlite3.h" +typedef int sqlite3_status_code; #define FILE_SERVICE_INITIAL_TYPE_COUNT (5) #define FILE_SERVICE_INITIAL_HANDLER_COUNT (2) +#define FILE_SERVICE_SDB_FILENAME "entities.db" + +#define FILE_SERVICE_SDB_ENTITY_TABLE \ +"CREATE TABLE IF NOT EXISTS Entity( \n\ + Type CHAR(64) NOT NULL, \n\ + Hash CHAR(64) NOT NULL, \n\ + Data TEXT NOT NULL, \n\ + PRIMARY KEY (Type, Hash));" + +typedef char FileServiceSQL[1024]; + +#define FILE_SERVICE_SDB_INSERT_ENTITY \ +"INSERT OR REPLACE INTO Entity (Type, Hash, Data) VALUES (?, ?, ?);" + +#define FILE_SERVICE_SDB_QUERY_ENTITY \ +"SELECT Data FROM Entity WHERE Type = ? AND Hash = ?;" + +#define FILE_SERVICE_SDB_QUERY_ALL_ENTITY \ +"SELECT Hash, Data FROM Entity WHERE Type = ?;" + +#define FILE_SERVICE_SDB_UPDATE_ENTITY \ +"UPDATE Entity SET Data = ? WHERE Type = ? AND Hash = ?;" + +#define FILE_SERVICE_SDB_DELETE_ENTITY \ +"DELETE FROM Entity WHERE Type = ? AND Hash = ?;" + +#define FILE_SERVICE_SDB_DELETE_ALL_TYPE_ENTITY \ +"DELETE FROM Entity WHERE Type = ?;" + +#define FILE_SERVICE_SDB_DELETE_ALL_ENTITY \ +"DELETE FROM Entity;" + +#if defined(DEBUG) +static int needSQLiteCompileOptions = 1; +#endif +// HEX Encode/Decode - Cribbed from ethereum/util/BRUtilHex.c + +// Convert a char into uint8_t (decode) +#define decodeChar(c) ((uint8_t) _hexu(c)) + +// Convert a uint8_t into a char (encode) +#define encodeChar(u) ((char) _hexc(u)) + +static void +decodeHex (uint8_t *target, size_t targetLen, const char *source, size_t sourceLen) { + // + assert (0 == sourceLen % 2); + assert (2 * targetLen == sourceLen); + + for (int i = 0; i < targetLen; i++) { + target[i] = (uint8_t) ((decodeChar(source[2*i]) << 4) | decodeChar(source[(2*i)+1])); + } +} + +static void +encodeHex (char *target, size_t targetLen, const uint8_t *source, size_t sourceLen) { + assert (targetLen == 2 * sourceLen + 1); + + for (int i = 0; i < sourceLen; i++) { + target[2*i + 0] = encodeChar (source[i] >> 4); + target[2*i + 1] = encodeChar (source[i]); + } + target[2*sourceLen] = '\0'; +} + +/** Forward Declarations */ +static int +fileServiceFailedSDB (BRFileService fs, + int releaseLock, + sqlite3_status_code code); + /// Return 0 on success, -1 otherwise static int directoryMake (const char *path) { struct stat dirStat; @@ -103,18 +177,58 @@ fileServiceEntityTypeAddHandler (BRFileServiceEntityType *entityType, /// /// struct BRFileServiceRecord { - const char *pathToType; + char *sdbPath; + sqlite3 *sdb; + sqlite3_stmt *sdbInsertStmt; + sqlite3_stmt *sdbSelectStmt; + sqlite3_stmt *sdbSelectAllStmt; + sqlite3_stmt *sdbUpdateStmt; + sqlite3_stmt *sdbDeleteStmt; + sqlite3_stmt *sdbDeleteAllTypeStmt; + sqlite3_stmt *sdbDeleteAllStmt; + uint8_t sdbClosed; + + char *currency; + char *network; + + pthread_mutex_t lock; + BRArrayOf(BRFileServiceEntityType) entityTypes; BRFileServiceContext context; BRFileServiceErrorHandler handler; }; +static BRFileService +fileServiceCreateReturnError (BRFileService fs, + int releaseLock, + BRFileServiceError error) { + // Nothing with 'error' at this point; a placeholder for now. + if (releaseLock) pthread_mutex_unlock (&fs->lock); + fileServiceRelease (fs); + return NULL; +} + +static char * +fileServiceCreateFilePath (const char *basePath, + const char *currency, + const char *network, + const char *filename) { + size_t sdbPathLength = strlen (basePath) + 1 + strlen(currency) + 1 + strlen(network) + 1 + strlen (filename) + 1; + char *sdbPath = malloc (sdbPathLength); + sprintf (sdbPath, "%s/%s-%s-%s", basePath, currency, network, filename); + return sdbPath; +} + extern BRFileService fileServiceCreate (const char *basePath, const char *currency, const char *network, BRFileServiceContext context, BRFileServiceErrorHandler handler) { + if (NULL == basePath || 0 == strlen(basePath)) return NULL; + if (NULL == currency || 0 == strlen(currency)) return NULL; + if (NULL == network || 0 == strlen(network)) return NULL; + // Reasonable limits on `network` and `currency` (ensure subsequent stack allocation works). if (strlen(network) > FILENAME_MAX || strlen(currency) > FILENAME_MAX) return NULL; @@ -127,34 +241,177 @@ fileServiceCreate (const char *basePath, if (NULL == dir) return NULL; closedir(dir); - // Create the directory hierarchy - size_t pathToTypeSize = strlen(basePath) + 1 + strlen(currency) + 1 + strlen(network) + 1; - char dirPath[pathToTypeSize]; - - sprintf (dirPath, "%s/%s", basePath, currency); - if (-1 == directoryMake(dirPath)) return NULL; - - sprintf(dirPath, "%s/%s/%s", basePath, currency, network); - if (-1 == directoryMake(dirPath)) return NULL; + // Require SQLite to support 'MULTI_THREADED' or 'SERIALIZED'. We'll lock our connection. + // and thus 'MULTI_THREADED' is appropriate. + if (0 == sqlite3_threadsafe()) return NULL; + // Create the file service itself BRFileService fs = calloc (1, sizeof (struct BRFileServiceRecord)); - fs->pathToType = strdup(dirPath); - array_new (fs->entityTypes, FILE_SERVICE_INITIAL_TYPE_COUNT); + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); + + pthread_mutex_init(&fs->lock, &attr); + pthread_mutexattr_destroy(&attr); + } + // Set the error handler - early fileServiceSetErrorHandler (fs, context, handler); + fs->sdb = NULL; + fs->sdbPath = NULL; + fs->sdbClosed = 0; + + // Save currency and network + fs->currency = strdup (currency); + fs->network = strdup (network); + + // Locate the SQLITE Database + fs->sdbPath = fileServiceCreateFilePath (basePath, currency, network, FILE_SERVICE_SDB_FILENAME); + + // Create/Open the SQLITE Database + sqlite3_status_code status = sqlite3_open(fs->sdbPath, &fs->sdb); + if (SQLITE_OK != status) { + fileServiceRelease (fs); + return NULL; + } + + // Create the SQLite 'Entity' Table + sqlite3_stmt *sdbCreateTableStmt; + status = sqlite3_prepare_v2 (fs->sdb, FILE_SERVICE_SDB_ENTITY_TABLE, -1, &sdbCreateTableStmt, NULL); + if (SQLITE_OK != status) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + + if (SQLITE_DONE != sqlite3_step(sdbCreateTableStmt)) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + sqlite3_finalize(sdbCreateTableStmt); + + // Create the SQLITE 'Insert into Entity' Statement + status = sqlite3_prepare_v2 (fs->sdb, FILE_SERVICE_SDB_INSERT_ENTITY, -1, &fs->sdbInsertStmt, NULL); + if (SQLITE_OK != status) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + + // Create the SQLITE "Select Entity By Hash' Statement + status = sqlite3_prepare_v2 (fs->sdb, FILE_SERVICE_SDB_QUERY_ENTITY, -1, &fs->sdbSelectStmt, NULL); + if (SQLITE_OK != status) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + + // Create the SQLITE "Select Entity ' Statement + status = sqlite3_prepare_v2 (fs->sdb, FILE_SERVICE_SDB_QUERY_ALL_ENTITY, -1, &fs->sdbSelectAllStmt, NULL); + if (SQLITE_OK != status) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + + status = sqlite3_prepare_v2 (fs->sdb, FILE_SERVICE_SDB_UPDATE_ENTITY, -1, &fs->sdbUpdateStmt, NULL); + if (SQLITE_OK != status) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + + status = sqlite3_prepare_v2 (fs->sdb, FILE_SERVICE_SDB_DELETE_ENTITY, -1, &fs->sdbDeleteStmt, NULL); + if (SQLITE_OK != status) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + + status = sqlite3_prepare_v2 (fs->sdb, FILE_SERVICE_SDB_DELETE_ALL_TYPE_ENTITY, -1, &fs->sdbDeleteAllTypeStmt, NULL); + if (SQLITE_OK != status) + return fileServiceCreateReturnError (fs, 1, (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { status }} + }); + + // Allocate the `entityTypes` array + array_new (fs->entityTypes, FILE_SERVICE_INITIAL_TYPE_COUNT); + +#if defined(DEBUG) + if (needSQLiteCompileOptions) { + needSQLiteCompileOptions = 0; + printf ("SQLITE ThreadSafe Mutex: %d\n", sqlite3_threadsafe()); + printf ("SQLITE Compile Options:\n"); + const char *option = NULL; + for (int index = 0; + NULL != (option = sqlite3_compileoption_get(index)); + index++) { + printf ("-DSQLITE_%s\n", option); + } + } +#endif return fs; } +static void +_fileServiceFinalizeStmt (BRFileService fs, sqlite3_stmt **stmt) { + if (NULL != stmt && NULL != *stmt) { + sqlite3_finalize (*stmt); + *stmt = NULL; + } +} + +static void +_fileServiceCloseInternal (BRFileService fs) { + if (fs->sdbClosed) return; + + fs->sdbClosed = 1; + _fileServiceFinalizeStmt (fs, &fs->sdbInsertStmt); + _fileServiceFinalizeStmt (fs, &fs->sdbSelectStmt); + _fileServiceFinalizeStmt (fs, &fs->sdbSelectAllStmt); + _fileServiceFinalizeStmt (fs, &fs->sdbUpdateStmt); + _fileServiceFinalizeStmt (fs, &fs->sdbDeleteStmt); + _fileServiceFinalizeStmt (fs, &fs->sdbDeleteAllTypeStmt); + _fileServiceFinalizeStmt (fs, &fs->sdbDeleteAllStmt); + + if (NULL != fs->sdb) sqlite3_close (fs->sdb); + fs->sdb = NULL; +} + +extern void +fileServiceClose (BRFileService fs) { + pthread_mutex_lock (&fs->lock); + _fileServiceCloseInternal(fs); + pthread_mutex_unlock (&fs->lock); +} + +// This is callable with a partially allocated BRFileService. So, be +// careful with fields that might not yet exist. extern void fileServiceRelease (BRFileService fs) { - size_t typesCount = array_count(fs->entityTypes); - for (size_t index = 0; index < typesCount; index++) - fileServiceEntityTypeRelease (&fs->entityTypes[index]); + pthread_mutex_lock (&fs->lock); + + _fileServiceCloseInternal(fs); + + if (NULL != fs->entityTypes) { + size_t typesCount = array_count(fs->entityTypes); + for (size_t index = 0; index < typesCount; index++) + fileServiceEntityTypeRelease (&fs->entityTypes[index]); + array_free(fs->entityTypes); + } + + if (NULL != fs->network) free (fs->network); + if (NULL != fs->currency) free (fs->currency); + if (NULL != fs->sdbPath) free (fs->sdbPath); + + pthread_mutex_unlock (&fs->lock); + pthread_mutex_destroy(&fs->lock); - free ((char *) fs->pathToType); - if (NULL != fs->entityTypes) array_free (fs->entityTypes); free (fs); } @@ -203,12 +460,15 @@ fileServiceLookupEntityHandler (const BRFileService fs, static int fileServiceFailedInternal (BRFileService fs, - void* bufferToFree, - FILE* fileToClose, - BRFileServiceError error) { + int releaseLock, + void* bufferToFree, + FILE* fileToClose, + BRFileServiceError error) { if (NULL != bufferToFree) free (bufferToFree); if (NULL != fileToClose) fclose (fileToClose); + if (releaseLock) pthread_mutex_unlock (&fs->lock); + // Handler invoked w/o the lock. Avoid a possible recursive use of FS. if (NULL != fs->handler) fs->handler (fs->context, fs, error); @@ -217,39 +477,160 @@ fileServiceFailedInternal (BRFileService fs, static int fileServiceFailedImpl(BRFileService fs, - void* bufferToFree, - FILE* fileToClose, - const char *reason) { - return fileServiceFailedInternal (fs, bufferToFree, fileToClose, - (BRFileServiceError) { - FILE_SERVICE_IMPL, - { .impl = { reason }} - }); + int releaseLock, + void* bufferToFree, + FILE* fileToClose, + const char *reason) { + return fileServiceFailedInternal (fs, releaseLock, bufferToFree, fileToClose, + (BRFileServiceError) { + FILE_SERVICE_IMPL, + { .impl = { reason }} + }); } +#pragma clang diagnostic push +#pragma GCC diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-function" static int fileServiceFailedUnix(BRFileService fs, - void* bufferToFree, - FILE* fileToClose, - int error) { - return fileServiceFailedInternal (fs, bufferToFree, fileToClose, - (BRFileServiceError) { - FILE_SERVICE_UNIX, - { ._unix = {error }} - }); + int releaseLock, + void* bufferToFree, + FILE* fileToClose, + int error) { + return fileServiceFailedInternal (fs, releaseLock, bufferToFree, fileToClose, + (BRFileServiceError) { + FILE_SERVICE_UNIX, + { ._unix = {error }} + }); +} +#pragma clang diagnostic pop +#pragma GCC diagnostic pop + +static int +fileServiceFailedSDB (BRFileService fs, + int releaseLock, + sqlite3_status_code code) { + return fileServiceFailedInternal (fs, releaseLock, NULL, NULL, + (BRFileServiceError) { + FILE_SERVICE_SDB, + { .sdb = { code, sqlite3_errstr(code) }} + }); } static int fileServiceFailedEntity(BRFileService fs, - void* bufferToFree, - FILE* fileToClose, - const char *type, - const char *reason) { - return fileServiceFailedInternal (fs, bufferToFree, fileToClose, - (BRFileServiceError) { - FILE_SERVICE_ENTITY, - { .entity = { type, reason }} - }); + int releaseLock, + void* bufferToFree, + FILE* fileToClose, + const char *type, + const char *reason) { + return fileServiceFailedInternal (fs, releaseLock, bufferToFree, fileToClose, + (BRFileServiceError) { + FILE_SERVICE_ENTITY, + { .entity = { type, reason }} + }); +} + +/// MARK: - Save + +static int +_fileServiceSave (BRFileService fs, + const char *type, /* block, peers, transactions, logs, ... */ + const void *entity, + int needLock) { /* BRMerkleBlock*, BRTransaction, BREthereumTransaction, ... */ + + BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); + if (NULL == entityType) { fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); return 0; }; + + BRFileServiceEntityHandler *handler = fileServiceEntityTypeLookupHandler(entityType, entityType->currentVersion); + if (NULL == handler) { fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type handler"); return 0; }; + + // Get then hex-encode the identifer + UInt256 identifier = handler->identifier (handler->context, fs, entity); + const char *hash = u256hex(identifier); + + // Get the entity bytes + uint32_t entityBytesCount; + uint8_t *entityBytes = handler->writer (handler->context, fs, entity, &entityBytesCount); + + // Always, always write the header for the currentHeaderFormatVersion + + // Extend the entity bytes with the current header format, which is: + // {HeaderFormatVersion, Current(Type)Version, EntityBytesCount, EntityBytes} + size_t offset = 0; + size_t bytesCount = 1 + 1 + sizeof(uint32_t) + entityBytesCount; + uint8_t *bytes = malloc (bytesCount); + + bytes[offset] = (uint8_t) currentHeaderFormatVersion; + offset += 1; + + bytes[offset] = (uint8_t) entityType->currentVersion; + offset += 1; + + UInt32SetBE (&bytes[offset], entityBytesCount); + offset += sizeof (uint32_t); + + memcpy (&bytes[offset], entityBytes, entityBytesCount); + free (entityBytes); + + // Nex encode bytes + size_t dataCount = 2 * bytesCount + 1; + char *data = malloc (dataCount); + encodeHex (data, dataCount, bytes, bytesCount); + free (bytes); + + // Fill out the SQL statement + sqlite3_status_code status; + + if (needLock) + pthread_mutex_lock (&fs->lock); + + if (fs->sdbClosed) + return fileServiceFailedImpl (fs, needLock, NULL, NULL, "closed"); + + sqlite3_reset (fs->sdbInsertStmt); + sqlite3_clear_bindings(fs->sdbInsertStmt); + + status = sqlite3_bind_text (fs->sdbInsertStmt, 1, type, -1, SQLITE_STATIC); + if (SQLITE_OK != status) { + free (data); + return fileServiceFailedSDB (fs, needLock, status); + } + + status = sqlite3_bind_text (fs->sdbInsertStmt, 2, hash, -1, SQLITE_STATIC); + if (SQLITE_OK != status) { + free (data); + return fileServiceFailedSDB (fs, needLock, status); + } + + status = sqlite3_bind_text (fs->sdbInsertStmt, 3, data, -1, SQLITE_STATIC); + if (SQLITE_OK != status) { + free (data); + return fileServiceFailedSDB (fs, needLock, status); + } + + status = sqlite3_step (fs->sdbInsertStmt); + if (SQLITE_DONE != status) { + free (data); + return fileServiceFailedSDB (fs, needLock, status); + } + + // Ensure the 'implicit DB transaction' is committed. + sqlite3_reset (fs->sdbInsertStmt); + + if (needLock) + pthread_mutex_unlock (&fs->lock); + + free (data); + return 1; +} + +extern int +fileServiceSave (BRFileService fs, + const char *type, /* block, peers, transactions, logs, ... */ + const void *entity) { /* BRMerkleBlock*, BRTransaction, BREthereumTransaction, ... */ + return _fileServiceSave (fs, type, entity, 1); } /// MARK: - Load @@ -260,213 +641,278 @@ fileServiceLoad (BRFileService fs, const char *type, int updateVersion) { BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); - if (NULL == entityType) return fileServiceFailedImpl (fs, NULL, NULL, "missed type"); + if (NULL == entityType) return fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); BRFileServiceEntityHandler *entityHandlerCurrent = fileServiceEntityTypeLookupHandler(entityType, entityType->currentVersion); - if (NULL == entityHandlerCurrent) return fileServiceFailedImpl (fs, NULL, NULL, "missed type handler"); - - DIR *dir; - struct dirent *dirEntry; - - char dirPath[strlen(fs->pathToType) + 1 + strlen(type) + 1]; - sprintf (dirPath, "%s/%s", fs->pathToType, type); - - char filename[strlen(dirPath) + 1 + 2 * sizeof(UInt256) + 1]; - - if (-1 == directoryMake(dirPath) || NULL == (dir = opendir(dirPath))) - return fileServiceFailedUnix (fs, NULL, NULL, errno); - - // Allocate some storage for entity bytes; - size_t bufferSize = 8 * 1024; - uint8_t *buffer = malloc (bufferSize); - if (NULL == buffer) return fileServiceFailedUnix (fs, NULL, NULL, ENOMEM); - - // Process each directory entry. - while (NULL != (dirEntry = readdir(dir))) - if (dirEntry->d_type == DT_REG) { - sprintf (filename, "%s/%s", dirPath, dirEntry->d_name); - FILE *file = fopen (filename, "rb"); - if (NULL == file) return fileServiceFailedUnix (fs, buffer, NULL, errno); - - - BRFileServiceVersion version; - uint32_t bytesCount; - - // read the header version - BRFileServiceHeaderFormatVersion headerVersion; - if (1 != fread (&headerVersion, sizeof(BRFileServiceHeaderFormatVersion), 1, file)) - return fileServiceFailedUnix (fs, buffer, file, errno); - - // read the header - switch (headerVersion) { - case HEADER_FORMAT_1: { - // read the version - if (1 != fread (&version, sizeof(BRFileServiceVersion), 1, file) || - // read the checksum - // read the bytesCount - 1 != fread (&bytesCount, sizeof(uint32_t), 1, file)) - return fileServiceFailedUnix (fs, buffer, file, errno); - - break; - } - } - - // Ensure `buffer` is large enough - if (bytesCount > bufferSize) { - bufferSize = bytesCount; - - uint8_t *bufferNew = realloc (buffer, bufferSize); - if (NULL == bufferNew) return fileServiceFailedUnix (fs, buffer, NULL, ENOMEM); - buffer = bufferNew; - } - - // read the bytes - multiple might be required - if (bytesCount != fread (buffer, 1, bytesCount, file)) - return fileServiceFailedUnix (fs, buffer, file, errno); - - // All file reading is complete; next read should be EOF. - - // Done with file. - if (0 != fclose (file)) - return fileServiceFailedUnix (fs, buffer, NULL, errno); - - // We now have everything - - // This will need some later rework. If a header includes some data, like a checksum, - // we won't have that value in this context when needed. - - // Do something header specific - switch (headerVersion) { - case HEADER_FORMAT_1: - // compute the checksum - // compare the checksum - break; - } - - // Look up the entity handler - BRFileServiceEntityHandler *handler = fileServiceEntityTypeLookupHandler(entityType, version); - if (NULL == handler) return fileServiceFailedImpl (fs, buffer, NULL, "missed type handler"); - - // Read the entity from buffer and add to results. - void *entity = handler->reader (handler->context, fs, buffer, bytesCount); - if (NULL == entity) return fileServiceFailedEntity (fs, buffer, NULL, type, "reader"); - - // Update restuls with the newly restored entity - BRSetAdd (results, entity); - - // If the read version is not the current version, update - if (updateVersion && - (version != entityType->currentVersion || - headerVersion != currentHeaderFormatVersion)) - fileServiceSave (fs, type, entity); + if (NULL == entityHandlerCurrent) return fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type handler"); + + sqlite3_status_code status; + + pthread_mutex_lock (&fs->lock); + if (fs->sdbClosed) + return fileServiceFailedImpl (fs, 1, NULL, NULL, "closed"); + + sqlite3_reset (fs->sdbSelectAllStmt); + sqlite3_clear_bindings (fs->sdbSelectAllStmt); + + status = sqlite3_bind_text (fs->sdbSelectAllStmt, 1, type, -1, SQLITE_STATIC); + if (SQLITE_OK != status) + return fileServiceFailedSDB (fs, 1, status); + + uint8_t dataBytesBuffer[8196]; + uint8_t *dataBytes = dataBytesBuffer; + size_t dataBytesCount = 8196; + + while (SQLITE_ROW == sqlite3_step(fs->sdbSelectAllStmt)) { + const char *hash = (const char *) sqlite3_column_text (fs->sdbSelectAllStmt, 0); + const char *data = (const char *) sqlite3_column_text (fs->sdbSelectAllStmt, 1); + + if (NULL == hash || NULL == data) + return fileServiceFailedImpl (fs, 1, (dataBytes == dataBytesBuffer ? NULL : dataBytes), NULL, + "missed query `hash` or `data`"); + + assert (64 == strlen (hash)); + + // Ensure `dataBytes` is large enough for hex-decoded `data` + size_t dataCount = strlen (data); + assert (0 == dataCount % 2); // Surely 'even' + if ((dataCount/2) > dataBytesCount) { + if (dataBytes != dataBytesBuffer) free (dataBytes); + dataBytesCount = dataCount/2; + dataBytes = malloc (dataBytesCount); } - free (buffer); - closedir (dir); + // Actually decode `data` into `dataBytes` + decodeHex (dataBytes, dataCount/2, data, dataCount); - return 1; -} - -/// MARK: - Save - -extern void /* error code? */ -fileServiceSave (BRFileService fs, - const char *type, /* block, peers, transactions, logs, ... */ - const void *entity) { /* BRMerkleBlock*, BRTransaction, BREthereumTransaction, ... */ - BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); - if (NULL == entityType) { fileServiceFailedImpl (fs, NULL, NULL, "missed type"); return; }; + size_t offset = 0; + BRFileServiceVersion version; + uint32_t entityBytesCount; + uint8_t *entityBytes; - BRFileServiceEntityHandler *handler = fileServiceEntityTypeLookupHandler(entityType, entityType->currentVersion); - if (NULL == handler) { fileServiceFailedImpl (fs, NULL, NULL, "missed type handler"); return; }; + BRFileServiceHeaderFormatVersion headerVersion = dataBytes[offset]; + offset += 1; - UInt256 identifier = handler->identifier (handler->context, fs, entity); + switch (headerVersion) { + case HEADER_FORMAT_1: + version = dataBytes[offset]; + offset += 1; - uint32_t bytesCount; - uint8_t *bytes = handler->writer (handler->context, fs, entity, &bytesCount); + entityBytesCount = UInt32GetBE (&dataBytes[offset]); + offset += sizeof (uint32_t); - char filename[strlen(fs->pathToType) + 1 + strlen(type) + 1 + 2*sizeof(UInt256) + 1]; - sprintf (filename, "%s/%s/%s", fs->pathToType, type, u256hex(identifier)); + break; + } - FILE *file = fopen (filename, "wb"); - if (NULL == file) { fileServiceFailedUnix (fs, bytes, NULL, errno); return; } + // Assert entityBytesCount remain in dataBytes + if (offset + entityBytesCount > dataBytesCount) { + assert (0); // In DEBUG builds. + return fileServiceFailedImpl (fs, 1, (dataBytes == dataBytesBuffer ? NULL : dataBytes), NULL, + "missed bytes count"); + } + entityBytes = &dataBytes[offset]; - // Always, always write the header for the currentHeaderFormatVersion + switch (headerVersion) { + case HEADER_FORMAT_1: + // compute then compare checksum + break; + } - if (// write the header version - 1 != fwrite(¤tHeaderFormatVersion, sizeof(BRFileServiceHeaderFormatVersion), 1, file) || - // then the version - 1 != fwrite (&entityType->currentVersion, sizeof(BRFileServiceVersion), 1, file) || - // then the checksum? - // write the bytesCount - 1 != fwrite(&bytesCount, sizeof (uint32_t), 1, file)) { - fileServiceFailedUnix (fs, bytes, file, errno); - return; + // Look up the entity handler + BRFileServiceEntityHandler *handler = fileServiceEntityTypeLookupHandler(entityType, version); + if (NULL == handler) + return fileServiceFailedImpl (fs, 1, (dataBytes == dataBytesBuffer ? NULL : dataBytes), NULL, + "missed type handler"); + + // Read the entity from buffer and add to results. + void *entity = handler->reader (handler->context, fs, entityBytes, entityBytesCount); + if (NULL == entity) + return fileServiceFailedEntity (fs, 1, (dataBytes == dataBytesBuffer ? NULL : dataBytes), NULL, + type, "reader"); + + // Update restuls with the newly restored entity + BRSetAdd (results, entity); + + // If the read version is not the current version, update + if (updateVersion && + (version != entityType->currentVersion || + headerVersion != currentHeaderFormatVersion)) + // This could signal an error. Perhaps we should test the return result and + // if `0` skip out here? We won't - we couldn't save the entity in the new format + // but we'll continue and will try next time we load it. + _fileServiceSave (fs, type, entity, 0); } - // write the bytes. - if (bytesCount != fwrite(bytes, 1, bytesCount, file)) { - fileServiceFailedUnix (fs, bytes, file, errno); - return; - } + // Ensure the 'implicit DB transaction' is committed. + sqlite3_reset (fs->sdbSelectAllStmt); - if (0 != fclose (file)) { fileServiceFailedUnix (fs, bytes, NULL, errno); return; } + pthread_mutex_unlock (&fs->lock); - free (bytes); + if (dataBytes != dataBytesBuffer) free (dataBytes); + + return 1; } /// MARK: - Remove, Clear -extern void +extern int fileServiceRemove (BRFileService fs, const char *type, UInt256 identifier) { BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); - if (NULL == entityType) { fileServiceFailedImpl (fs, NULL, NULL, "missed type"); return; }; + if (NULL == entityType) + return fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); + + // Hex-Encode identifier + const char *hash = u256hex(identifier); + + sqlite3_status_code status; - char filename[strlen(fs->pathToType) + 1 + strlen(type) + 1 + 2*sizeof(UInt256) + 1]; - sprintf (filename, "%s/%s/%s", fs->pathToType, type, u256hex(identifier)); + pthread_mutex_lock (&fs->lock); + if (fs->sdbClosed) + return fileServiceFailedImpl (fs, 1, NULL, NULL, "closed"); - // If failed, then what? - remove (filename); + sqlite3_reset (fs->sdbDeleteStmt); + sqlite3_clear_bindings (fs->sdbDeleteStmt); + + status = sqlite3_bind_text (fs->sdbDeleteStmt, 1, type, -1, SQLITE_STATIC); + if (SQLITE_OK != status) + return fileServiceFailedSDB (fs, 1, status); + + status = sqlite3_bind_text (fs->sdbDeleteStmt, 2, hash, -1, SQLITE_STATIC); + if (SQLITE_OK != status) + return fileServiceFailedSDB (fs, 1, status); + + status = sqlite3_step (fs->sdbDeleteStmt); + if (SQLITE_DONE != status) + return fileServiceFailedSDB (fs, 1, status); + + // Ensure the 'implicit DB transaction' is committed. + sqlite3_reset (fs->sdbDeleteStmt); + + pthread_mutex_unlock (&fs->lock); + + return 1; } -static void +static int fileServiceClearForType (BRFileService fs, - BRFileServiceEntityType *entityType) { - DIR *dir; - struct dirent *dirEntry; + BRFileServiceEntityType *entityType, + int needLock) { + const char *type = entityType->type; - char dirPath[strlen(fs->pathToType) + 1 + strlen(entityType->type) + 1]; - sprintf (dirPath, "%s/%s", fs->pathToType, entityType->type); + sqlite3_status_code status; - if (-1 == directoryMake(dirPath) || NULL == (dir = opendir(dirPath))) { - fileServiceFailedUnix (fs, NULL, NULL, errno); - return; - } + if (needLock) pthread_mutex_lock (&fs->lock); + if (fs->sdbClosed) + return fileServiceFailedImpl (fs, needLock, NULL, NULL, "closed"); - while (NULL != (dirEntry = readdir(dir))) - if (dirEntry->d_type == DT_REG) - remove (dirEntry->d_name); // If failed, then what? + sqlite3_reset (fs->sdbDeleteAllTypeStmt); + sqlite3_clear_bindings (fs->sdbDeleteAllTypeStmt); - closedir(dir); + status = sqlite3_bind_text (fs->sdbDeleteAllTypeStmt, 1, type, -1, SQLITE_STATIC); + if (SQLITE_OK != status) + return fileServiceFailedSDB (fs, needLock, status); + + status = sqlite3_step (fs->sdbDeleteAllTypeStmt); + if (SQLITE_DONE != status) + return fileServiceFailedSDB (fs, needLock, status); + + // Ensure the 'implicit DB transaction' is committed. + sqlite3_reset (fs->sdbDeleteAllTypeStmt); + + if (needLock) pthread_mutex_unlock (&fs->lock); + + return 1; } -extern void +extern int fileServiceClear (BRFileService fs, const char *type) { BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); - if (NULL == entityType) { fileServiceFailedImpl (fs, NULL, NULL, "missed type"); return; }; + if (NULL == entityType) + return fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); - fileServiceClearForType(fs, entityType); + return fileServiceClearForType(fs, entityType, 1); } -extern void +extern int fileServiceClearAll (BRFileService fs) { + int success = 1; size_t typeCount = array_count(fs->entityTypes); for (size_t index = 0; index < typeCount; index++) - fileServiceClearForType (fs, &fs->entityTypes[index]); + success &= fileServiceClearForType (fs, &fs->entityTypes[index], 1); + return success; +} + +static int +fileServiceReplaceFailed (BRFileService fs, int needUnlock) { + if (needUnlock) pthread_mutex_unlock (&fs->lock); + return 0; +} + +extern int +fileServiceReplace (BRFileService fs, + const char *type, + const void **entities, + size_t entitiesCount) { + BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); + if (NULL == entityType) + return fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); + + sqlite3_status_code status; + + pthread_mutex_lock (&fs->lock); + if (fs->sdbClosed) + return fileServiceFailedImpl (fs, 1, NULL, NULL, "closed"); + + status = sqlite3_exec (fs->sdb, "BEGIN", NULL, NULL, NULL); + if (SQLITE_OK != status) + return fileServiceFailedSDB (fs, 1, status); + + if (0 == fileServiceClearForType (fs, entityType, 0)) + return fileServiceReplaceFailed (fs, 1); + + for (size_t index = 0; index < entitiesCount; index++) + if (0 == _fileServiceSave (fs, type, entities[index], 0)) + return fileServiceReplaceFailed (fs, 1); + status = sqlite3_exec (fs->sdb, "COMMIT", NULL, NULL, NULL); + if (SQLITE_OK != status) + return fileServiceFailedSDB (fs, 1, status); + + pthread_mutex_unlock (&fs->lock); + + return 1; +} + +extern int +fileServiceWipe (const char *basePath, + const char *currency, + const char *network) { + + // Locate the SQLITE Database + char *sdbPath = fileServiceCreateFilePath (basePath, currency, network, FILE_SERVICE_SDB_FILENAME); + + // Remove it. + int result = 0 == remove (sdbPath) ? 0 : errno; + free (sdbPath); + return result; +} + +extern UInt256 +fileServiceGetIdentifier (BRFileService fs, + const char *type, + const void *entity) { + + BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); + if (NULL == entityType) { fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); return UINT256_ZERO; }; + + BRFileServiceEntityHandler *handler = fileServiceEntityTypeLookupHandler(entityType, entityType->currentVersion); + if (NULL == handler) { fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type handler"); return UINT256_ZERO; }; + + return handler->identifier (handler->context, fs, entity); } extern int @@ -502,13 +948,6 @@ fileServiceDefineType (BRFileService fs, // otherwise add one else { - // Confirm that the directory can be made. - char dirPath[strlen(fs->pathToType) + 1 + strlen(type) + 1]; - sprintf (dirPath, "%s/%s", fs->pathToType, type); - - if (-1 == directoryMake(dirPath)) - return fileServiceFailedUnix (fs, NULL, NULL, errno); - fileServiceEntityTypeAddHandler (entityType, &newEntityHander); } @@ -521,14 +960,54 @@ fileServiceDefineCurrentVersion (BRFileService fs, BRFileServiceVersion version) { // Find the entityType for `type` BRFileServiceEntityType *entityType = fileServiceLookupType (fs, type); - if (NULL == entityType) return fileServiceFailedImpl (fs, NULL, NULL, "missed type"); + if (NULL == entityType) return fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type"); // Find the entityHandler, for version. BRFileServiceEntityHandler *entityHandler = fileServiceEntityTypeLookupHandler (entityType, version); - if (NULL == entityHandler) return fileServiceFailedImpl (fs, NULL, NULL, "missed type handler"); + if (NULL == entityHandler) return fileServiceFailedImpl (fs, 0, NULL, NULL, "missed type handler"); // We have a handler, therefore it can be the current one. entityType->currentVersion = version; return 1; } + +extern BRFileService +fileServiceCreateFromTypeSpecfications (const char *basePath, + const char *currency, + const char *network, + BRFileServiceContext context, + BRFileServiceErrorHandler handler, + size_t specificationsCount, + BRFileServiceTypeSpecification *specfications) { + int success = 1; + + BRFileService fileService = fileServiceCreate (basePath, + currency, + network, + context, + handler); + if (NULL == fileService) return NULL; + + for (size_t index = 0; index < specificationsCount; index++) { + BRFileServiceTypeSpecification *specification = &specfications[index]; + for (size_t vindex = 0; vindex < specification->versionsCount; vindex++) { + success &= fileServiceDefineType (fileService, + specification->type, + specification->versions[vindex].version, + context, + specification->versions[vindex].identifier, + specification->versions[vindex].reader, + specification->versions[vindex].writer); + if (!success) break; + } + + success &= fileServiceDefineCurrentVersion (fileService, + specification->type, + specification->defaultVersion); + if (!success) break; + } + + if (success) return fileService; + else { fileServiceRelease (fileService); return NULL; } +} diff --git a/ThirdParty/breadwallet-core/support/BRFileService.h b/ThirdParty/breadwallet-core/support/BRFileService.h index 50343edc2..c57a7d28f 100644 --- a/ThirdParty/breadwallet-core/support/BRFileService.h +++ b/ThirdParty/breadwallet-core/support/BRFileService.h @@ -30,9 +30,9 @@ #include "BRSet.h" #include "BRInt.h" -//Both Bitcoin and Ethereum Wallet Managers include the ability to save and load peers, block, -//transactions and logs (for Ethereum) to the file system. But, they both implement the file -//operations independently. Pull out the implementation into BRFileService. +// Both Bitcoin and Ethereum Wallet Managers include the ability to save and load peers, block, +// transactions and logs (for Ethereum) to the file system. But, they both implement the file +// operations independently. Pull out the implementation into BRFileService. // // Allow each WalletManager (Bitcoin, Bitcash, Ethereum) to create their own BRFileService (storing // stuff in a subdirectory specific to the manager+network+whatever for a given base path). Allow @@ -48,6 +48,7 @@ typedef void* BRFileServiceContext; typedef enum { FILE_SERVICE_IMPL, // generally a fatal condition FILE_SERVICE_UNIX, // something in the file system (fopen, fwrite, ... errorred) + FILE_SERVICE_SDB, // something in the sqlite3 database FILE_SERVICE_ENTITY // entity read/write (parse/serialize) error } BRFileServiceErrorType; @@ -62,6 +63,11 @@ typedef struct { int error; } _unix; + struct { + int code; // sqlite3_status_code + const char *reason; + } sdb; + struct { const char *type; const char *reason; @@ -74,10 +80,10 @@ typedef void BRFileService fs, BRFileServiceError error); - /// This *must* be the same fixed size type forever. It is uint8_t. typedef uint8_t BRFileServiceVersion; +/// TODO: There are limitations on `currency`, `network`, and `type`. extern BRFileService fileServiceCreate (const char *basePath, const char *currency, @@ -85,9 +91,20 @@ fileServiceCreate (const char *basePath, BRFileServiceContext context, BRFileServiceErrorHandler handler); +/** + * Release fs. This will close `fs` if it hasn't been already and then free the memory and any + * other resources associaed with the fs (such as locks). + */ extern void fileServiceRelease (BRFileService fs); +/** + * Close fs. This will close the DB associated with `fs`. The `fs` must not be used after closing; + * if it is then an IMPL error is raised. This can be called multiple times (but shouldn't be). + */ +extern void +fileServiceClose (BRFileService fs); + extern void fileServiceSetErrorHandler (BRFileService fs, BRFileServiceContext context, @@ -111,23 +128,34 @@ fileServiceLoad (BRFileService fs, const char *type, /* blocks, peers, transactions, logs, ... */ int updateVersion); -extern void /* error code? */ +extern int // 1 -> success, 0 -> failure fileServiceSave (BRFileService fs, const char *type, /* block, peers, transactions, logs, ... */ const void *entity); /* BRMerkleBlock*, BRTransaction, BREthereumTransaction, ... */ -extern void +extern int fileServiceRemove (BRFileService fs, const char *type, UInt256 identifier); -extern void +extern int +fileServiceReplace (BRFileService fs, + const char *type, + const void **entities, + size_t entitiesCount); + +extern int fileServiceClear (BRFileService fs, const char *type); -extern void +extern int fileServiceClearAll (BRFileService fs); +extern UInt256 +fileServiceGetIdentifier (BRFileService fs, + const char *type, + const void *entity); + /** * A function type to produce an identifer from an entity. The identifer must be constant for * a particulary entity through time. The identifier is used to derive a filename (more generally @@ -156,6 +184,8 @@ typedef uint8_t* const void* entity, uint32_t *bytesCount); +/// TODO: There is a limitation on `type`. + /** * Define a 'type', such as {block, peer, transaction, logs, etc}, that is to be stored in the * file system. @@ -184,4 +214,42 @@ fileServiceDefineCurrentVersion (BRFileService fs, const char *type, BRFileServiceVersion version); +// Version limit can increase with maximum number of version, historically. +#define FILE_SERVICE_TYPE_SPECIFICATION_NUMBER_OF_VERSION_LIMIT (5) + +typedef struct { + const char *type; + BRFileServiceVersion defaultVersion; + size_t versionsCount; + struct { + BRFileServiceVersion version; + BRFileServiceIdentifier identifier; + BRFileServiceReader reader; + BRFileServiceWriter writer; + } versions [FILE_SERVICE_TYPE_SPECIFICATION_NUMBER_OF_VERSION_LIMIT]; +} BRFileServiceTypeSpecification; + +extern BRFileService +fileServiceCreateFromTypeSpecfications (const char *basePath, + const char *currency, + const char *network, + BRFileServiceContext context, + BRFileServiceErrorHandler handler, + size_t specificationsCount, + BRFileServiceTypeSpecification *specfications); + +/// +/// Deletes file system data +/// +/// @param basePath +/// @param currency +/// @param network +/// +/// @return 0 on success, errno on failure +/// +extern int +fileServiceWipe (const char *basePath, + const char *currency, + const char *network); + #endif /* BRFileService_h */ diff --git a/ThirdParty/libcurl/CMakeLists.txt b/ThirdParty/libcurl/CMakeLists.txt new file mode 100644 index 000000000..13cf2c3ff --- /dev/null +++ b/ThirdParty/libcurl/CMakeLists.txt @@ -0,0 +1,120 @@ +project(libcurl) + +include(ProjectDefaults) +include(ExternalProject) + +# "--with-zlib=${PROJECT_INT_DIST_DIR}" +# "--with-ssl=${PROJECT_INT_DIST_DIR}/../deps/libspvsdk/external/src/libspvsdk-build/intermediates" + +if(WIN32) + set(PATCH_ARGS -s -p1 < "${CMAKE_CURRENT_LIST_DIR}/curl.patch") + + if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + set(MACHINE x64) + else() + set(MACHINE x86) + endif() + + if("${CMAKE_BUILD_TYPE}" MATCHES "(Debug|RelWithDebInfo)") + set(DEBUG yes) + set(BUILD_SUFFIX "_debug") + else() + set(DEBUG no) + endif() + + set(CONFIGURE_CMD "echo") + set(CONFIGURE_ARGS "Done") + set(BUILD_CMD cd winbuild && nmake) + set(BUILD_ARGS + "/f" "Makefile.vc" + "mode=static" + "VC=15" + #"WITH_PREFIX=${PROJECT_INT_DIST_DIR}" + "WITH_DEVEL=${PROJECT_INT_DIST_DIR}" + "WITH_SSL=static" + "WITH_ZLIB=static" + "ENABLE_WINSSL=no" + "ENABLE_SSPI=no" + "ENABLE_IPV6=no" + "ENABLE_IDN=no" + "ENABLE_OPENSSL_AUTO_LOAD_CONFIG=no" + "DEBUG=${DEBUG}" + "MACHINE=${MACHINE}") + + set(OUTPUT_PATH builds\\libcurl) + set(INSTALL_CMD + xcopy "${OUTPUT_PATH}\\include\\curl" "${PROJECT_INT_DIST_DIR}\\include\\curl" /e /i /h /y && + copy "${OUTPUT_PATH}\\lib\\libcurl_a${BUILD_SUFFIX}.lib" "${PROJECT_INT_DIST_DIR}\\lib\\curl.lib" /y) +else() + include(ExternalConfigureArgs) + + set(PATCH_EXE "echo") + set(PATCH_ARGS "Done") + + set(CONFIGURE_CMD "./configure") + set(CONFIGURE_ARGS + "--prefix=${PROJECT_INT_DIST_DIR}" + "--with-ssl=${PROJECT_INT_DIST_DIR}" + "--disable-shared" + "--enable-static" + "--without-brotli" + "--without-ldap-lib" + "--without-lber-lib" + "--without-winssl" + "--without-schannel" + "--without-darwinssl" + "--without-gnutls" + "--without-polarssl" + "--without-mbedtls" + "--without-cyassl" + "--without-wolfssl" + "--without-libpsl" + "--without-libmetalink" + "--without-libssh2" + "--without-libssh" + "--without-librtmp" + "--without-winidn" + "--without-libidn2" + "--without-nghttp2" + "--enable-http" + "--disable-ftp" + "--disable-file" + "--disable-ldap" + "--disable-ldaps" + "--disable-rtsp" + "--disable-proxy" + "--disable-dict" + "--disable-telnet" + "--disable-tftp" + "--disable-pop3" + "--disable-imap" + "--disable-smb" + "--disable-smtp" + "--disable-gopher" + "--disable-manual" + ${CONFIGURE_ARGS_INIT} + "PKG_CONFIG= " + "ac_tool_prefix=") + + set(INSTALL_CMD + make install) +endif() + +ExternalProject_Add( + libcurl + + PREFIX ${PROJECT_DEPS_BUILD_PREFIX} + URL "https://github.com/curl/curl/releases/download/curl-7_71_1/curl-7.71.1.tar.bz2" + URL_HASH SHA256=9d52a4d80554f9b0d460ea2be5d7be99897a1a9f681ffafe739169afd6b4f224 + DOWNLOAD_NAME "curl-7.71.1.tar.bz2" + DOWNLOAD_DIR ${PROJECT_DEPS_TARBALL_DIR} + DOWNLOAD_NO_PROGRESS 1 + LOG_DOWNLOAD FALSE + + BUILD_IN_SOURCE 1 + + PATCH_COMMAND ${PATCH_EXE} ${PATCH_ARGS} + CONFIGURE_COMMAND ${CONFIGURE_CMD} ${CONFIGURE_ARGS} + BUILD_COMMAND ${BUILD_CMD} ${BUILD_ARGS} + INSTALL_COMMAND ${INSTALL_CMD} +) diff --git a/ThirdParty/libcurl/curl.patch b/ThirdParty/libcurl/curl.patch new file mode 100644 index 000000000..f49e4061b --- /dev/null +++ b/ThirdParty/libcurl/curl.patch @@ -0,0 +1,129 @@ +diff -ruN curl-7.64.0/lib/config-win32.h curl-7.64.0-mod/lib/config-win32.h +--- curl-7.64.0/lib/config-win32.h 2018-12-17 15:03:06.000000000 +0800 ++++ curl-7.64.0-mod/lib/config-win32.h 2019-03-06 17:20:53.000000000 +0800 +@@ -740,4 +740,6 @@ + # define ENABLE_IPV6 1 + #endif + ++#define HTTP_ONLY ++ + #endif /* HEADER_CURL_CONFIG_WIN32_H */ +diff -ruN curl-7.64.0/winbuild/Makefile.vc curl-7.64.0-mod/winbuild/Makefile.vc +--- curl-7.64.0/winbuild/Makefile.vc 2018-10-27 18:00:54.000000000 +0800 ++++ curl-7.64.0-mod/winbuild/Makefile.vc 2019-03-06 17:59:39.000000000 +0800 +@@ -198,55 +198,55 @@ + SSH2 = static + !ENDIF + +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-vc$(VC)-$(MACHINE) ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-vc$(VC)-$(MACHINE) + +-!IF "$(DEBUG)"=="yes" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-debug +-!ELSE +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-release +-!ENDIF +- +-!IF "$(AS_DLL)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-dll +-!ELSE +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-static +-!ENDIF +- +-!IF "$(USE_SSL)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ssl-$(SSL) +-!ENDIF +- +-!IF "$(USE_MBEDTLS)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-mbedtls-$(MBEDTLS) +-!ENDIF +- +-!IF "$(USE_CARES)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-cares-$(CARES) +-!ENDIF +- +-!IF "$(USE_ZLIB)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-zlib-$(ZLIB) +-!ENDIF +- +-!IF "$(USE_SSH2)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ssh2-$(SSH2) +-!ENDIF +- +-!IF "$(USE_IPV6)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ipv6 +-!ENDIF +- +-!IF "$(USE_SSPI)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-sspi +-!ENDIF +- +-!IF "$(USE_WINSSL)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-winssl +-!ENDIF +- +-!IF "$(USE_NGHTTP2)"=="true" +-CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-nghttp2-$(NGHTTP2) +-!ENDIF ++#!IF "$(DEBUG)"=="yes" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-debug ++#!ELSE ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-release ++#!ENDIF ++ ++#!IF "$(AS_DLL)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-dll ++#!ELSE ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-static ++#!ENDIF ++ ++#!IF "$(USE_SSL)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ssl-$(SSL) ++#!ENDIF ++ ++#!IF "$(USE_MBEDTLS)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-mbedtls-$(MBEDTLS) ++#!ENDIF ++ ++#!IF "$(USE_CARES)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-cares-$(CARES) ++#!ENDIF ++ ++#!IF "$(USE_ZLIB)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-zlib-$(ZLIB) ++#!ENDIF ++ ++#!IF "$(USE_SSH2)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ssh2-$(SSH2) ++#!ENDIF ++ ++#!IF "$(USE_IPV6)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-ipv6 ++#!ENDIF ++ ++#!IF "$(USE_SSPI)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-sspi ++#!ENDIF ++ ++#!IF "$(USE_WINSSL)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-winssl ++#!ENDIF ++ ++#!IF "$(USE_NGHTTP2)"=="true" ++#CONFIG_NAME_LIB = $(CONFIG_NAME_LIB)-nghttp2-$(NGHTTP2) ++#!ENDIF + + !MESSAGE configuration name: $(CONFIG_NAME_LIB) + +diff -ruN curl-7.64.0/winbuild/MakefileBuild.vc curl-7.64.0-mod/winbuild/MakefileBuild.vc +--- curl-7.64.0/winbuild/MakefileBuild.vc 2019-02-05 22:00:12.000000000 +0800 ++++ curl-7.64.0-mod/winbuild/MakefileBuild.vc 2019-03-06 17:19:34.000000000 +0800 +@@ -502,7 +502,7 @@ + + EXE_OBJS = $(CURL_OBJS) $(CURL_DIROBJ)\curl.res + +-all : $(TARGET) $(PROGRAM_NAME) ++all : $(TARGET) + + package: $(TARGET) + @cd $(DIRDIST) diff --git a/ThirdParty/zlib/CMakeLists.txt b/ThirdParty/zlib/CMakeLists.txt new file mode 100644 index 000000000..ac271d192 --- /dev/null +++ b/ThirdParty/zlib/CMakeLists.txt @@ -0,0 +1,32 @@ +project(zlib) + +include(ProjectDefaults) +include(ExternalProject) +include(ExternalCMakeArgs) + +ExternalProject_Add( + zlib + + PREFIX ${PROJECT_DEPS_BUILD_PREFIX} + URL "https://github.com/madler/zlib/archive/v1.2.8.tar.gz" + URL_HASH SHA256=e380bd1bdb6447508beaa50efc653fe45f4edc1dafe11a251ae093e0ee97db9a + DOWNLOAD_NAME "zlib-1.2.8.tar.gz" + DOWNLOAD_DIR ${PROJECT_DEPS_TARBALL_DIR} + DOWNLOAD_NO_PROGRESS 1 + LOG_DOWNLOAD FALSE + + PATCH_COMMAND ${PATCH_EXE} -s -p1 < ${CMAKE_CURRENT_LIST_DIR}/zlib.patch + + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PROJECT_INT_DIST_DIR} + ${CMAKE_ARGS_INIT} +) + +if(WIN32) + ExternalProject_Add_Step( + zlib post-install + DEPENDEES install + WORKING_DIRECTORY "${PROJECT_INT_DIST_DIR}/lib" + COMMAND copy zlib1.lib z.lib /y + COMMAND copy zlib1.lib zlib_a.lib /y + ) +endif() diff --git a/ThirdParty/zlib/zlib.patch b/ThirdParty/zlib/zlib.patch new file mode 100644 index 000000000..ef5d357ca --- /dev/null +++ b/ThirdParty/zlib/zlib.patch @@ -0,0 +1,84 @@ +diff -ru zlib-1.2.8/CMakeLists.txt zlib-1.2.8-mod/CMakeLists.txt +--- zlib-1.2.8/CMakeLists.txt 2013-04-29 08:23:49.000000000 +0800 ++++ zlib-1.2.8-mod/CMakeLists.txt 2018-06-01 15:07:55.000000000 +0800 +@@ -60,7 +60,7 @@ + check_include_file(unistd.h Z_HAVE_UNISTD_H) + + if(MSVC) +- set(CMAKE_DEBUG_POSTFIX "d") ++ # set(CMAKE_DEBUG_POSTFIX "d") + add_definitions(-D_CRT_SECURE_NO_DEPRECATE) + add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +@@ -183,10 +183,10 @@ + set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj) + endif(MINGW) + +-add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) ++# add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) + add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS}) +-set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) +-set_target_properties(zlib PROPERTIES SOVERSION 1) ++# set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL) ++# set_target_properties(zlib PROPERTIES SOVERSION 1) + + if(NOT CYGWIN) + # This property causes shared libraries on Linux to have the full version +@@ -196,22 +196,26 @@ + # + # This has no effect with MSVC, on that platform the version info for + # the DLL comes from the resource file win32/zlib1.rc +- set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION}) ++ # set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION}) + endif() + + if(UNIX) + # On unix-like platforms the library is almost always called libz +- set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z) ++ set_target_properties(zlibstatic PROPERTIES OUTPUT_NAME z) + if(NOT APPLE) +- set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"") ++ # set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"") + endif() +-elseif(BUILD_SHARED_LIBS AND WIN32) +- # Creates zlib1.dll when building shared library version +- set_target_properties(zlib PROPERTIES SUFFIX "1.dll") ++elseif(WIN32) ++ if (BUILD_SHARED_LIBS) ++ # Creates zlib1.dll when building shared library version ++ # set_target_properties(zlib PROPERTIES SUFFIX "1.dll") ++ else() ++ set_target_properties(zlibstatic PROPERTIES OUTPUT_NAME zlib1) ++ endif() + endif() + + if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL ) +- install(TARGETS zlib zlibstatic ++ install(TARGETS zlibstatic + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" ) +@@ -231,19 +235,19 @@ + #============================================================================ + + add_executable(example test/example.c) +-target_link_libraries(example zlib) ++target_link_libraries(example zlibstatic) + add_test(example example) + + add_executable(minigzip test/minigzip.c) +-target_link_libraries(minigzip zlib) ++target_link_libraries(minigzip zlibstatic) + + if(HAVE_OFF64_T) + add_executable(example64 test/example.c) +- target_link_libraries(example64 zlib) ++ target_link_libraries(example64 zlibstatic) + set_target_properties(example64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64") + add_test(example64 example64) + + add_executable(minigzip64 test/minigzip.c) +- target_link_libraries(minigzip64 zlib) ++ target_link_libraries(minigzip64 zlibstatic) + set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64") + endif() diff --git a/Wallet/CMakeLists.txt b/Wallet/CMakeLists.txt index 281160626..87a5c0251 100644 --- a/Wallet/CMakeLists.txt +++ b/Wallet/CMakeLists.txt @@ -43,11 +43,11 @@ link_directories( ) if (SPV_ENABLE_SHARED) - set(LIBS spvsdk resolv) - set(ELAWALLET_DEPENDS libspvsdk) + set(LIBS spvsdk resolv curl z crypto ssl) + set(ELAWALLET_DEPENDS libspvsdk libcurl) elseif(SPV_ENABLE_STATIC) - set(LIBS spvsdk-static sqlite3 crypto ssl boost_system boost_filesystem boost_thread fruit resolv) - set(ELAWALLET_DEPENDS libspvsdk libfruit boost libressl json libsqlite) + set(LIBS spvsdk-static sqlite3 crypto ssl boost_system boost_filesystem boost_thread fruit resolv curl z) + set(ELAWALLET_DEPENDS libspvsdk libfruit boost libressl json libsqlite libcurl) else() message(FATAL_ERROR "libspvsdk should enable static or shared") endif() diff --git a/Wallet/RPCHelper.cpp b/Wallet/RPCHelper.cpp new file mode 100644 index 000000000..0c28c62d6 --- /dev/null +++ b/Wallet/RPCHelper.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "RPCHelper.h" + +namespace Elastos { + namespace ElaWallet { + + typedef struct HttpResponseBody { + size_t used; + size_t sz; + void *data; + } HttpResponseBody; + + static size_t HttpResponseBodyWriteCallback(char *ptr, + size_t size, size_t nmemb, void *userdata) { + HttpResponseBody *response = (HttpResponseBody *) userdata; + size_t length = size * nmemb; + + if (response->sz - response->used < length) { + size_t new_sz; + size_t last_try; + void *new_data; + + if (response->sz + length < response->sz) { + response->used = 0; + return 0; + } + + for (new_sz = response->sz ? response->sz << 1 : 512, last_try = response->sz; + new_sz > last_try && new_sz <= response->sz + length; + last_try = new_sz, new_sz <<= 1); + + if (new_sz <= last_try) + new_sz = response->sz + length; + + new_sz += 16; + + new_data = realloc(response->data, new_sz); + if (!new_data) { + response->used = 0; + return 0; + } + + response->data = new_data; + response->sz = new_sz; + } + + memcpy((char *) response->data + response->used, ptr, length); + response->used += length; + + return length; + } + + typedef struct HttpRequestBody { + size_t used; + size_t sz; + char *data; + } HttpRequestBody; + + static size_t HttpRequestBodyReadCallback(void *dest, size_t size, + size_t nmemb, void *userdata) + { + HttpRequestBody *request = (HttpRequestBody *)userdata; + size_t length = size * nmemb; + size_t bytes_copy = request->sz - request->used; + + if (bytes_copy) { + if(bytes_copy > length) + bytes_copy = length; + + memcpy(dest, request->data + request->used, bytes_copy); + + request->used += bytes_copy; + return bytes_copy; + } + + return 0; + } + + RPCHelper::RPCHelper() { + /* In windows, this will init the winsock stuff */ + curl_global_init(CURL_GLOBAL_ALL); + + /* get a curl handle */ + _curl = curl_easy_init(); + } + + RPCHelper::~RPCHelper() { + if (_curl != nullptr) { + /* always cleanup */ + curl_easy_cleanup(_curl); + } + + curl_global_cleanup(); + } + + nlohmann::json RPCHelper::Get(const std::string &url) const { + nlohmann::json j; + if (_curl == nullptr) { + return j; + } + HttpResponseBody response; + + curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(_curl, CURLOPT_FOLLOWLOCATION, 1L); + /* use a GET to fetch this */ + curl_easy_setopt(_curl, CURLOPT_HTTPGET, 1L); + + curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, HttpResponseBodyWriteCallback); + curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &response); + + memset(&response, 0, sizeof(response)); + /* Perform the request */ + CURLcode rc = curl_easy_perform(_curl); + if (rc != CURLE_OK) { + fprintf(stderr, "RPC call error, status: %d, message: %s", rc, curl_easy_strerror(rc)); + if (response.data) + free(response.data); + + return j; + } + + ((char *)response.data)[response.used] = 0; + std::string jstring = std::string((char *)response.data); + j = nlohmann::json::parse(jstring); + free(response.data); + + return j; + } + + nlohmann::json RPCHelper::Post(const std::string &url, const std::string &rawData) const { + nlohmann::json j; + if (_curl == nullptr) { + return j; + } + HttpRequestBody request; + request.used = 0; + request.sz = rawData.size(); + request.data = (char *)rawData.c_str(); + curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(_curl, CURLOPT_POST, 1L); + curl_easy_setopt(_curl, CURLOPT_READFUNCTION, HttpRequestBodyReadCallback); + curl_easy_setopt(_curl, CURLOPT_READDATA, &request); + curl_easy_setopt(_curl, CURLOPT_POSTFIELDSIZE, (long)request.sz); + HttpResponseBody response; + curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, HttpResponseBodyWriteCallback); + curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &response); + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: application/json"); +// headers = curl_slist_append(headers, "Accept: application/json"); + curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, headers); + + memset(&response, 0, sizeof(response)); + CURLcode rc = curl_easy_perform(_curl); + curl_slist_free_all(headers); + if (rc != CURLE_OK) { + fprintf(stderr, "RPC call error, status: %d, message: %s", rc, curl_easy_strerror(rc)); + if (response.data) + free(response.data); + + return j; + } + + ((char *)response.data)[response.used] = 0; + std::string jstring = std::string((char *)response.data); + j = nlohmann::json::parse(jstring); + free(response.data); + + return j; + } + + } +} \ No newline at end of file diff --git a/Wallet/RPCHelper.h b/Wallet/RPCHelper.h new file mode 100644 index 000000000..358249895 --- /dev/null +++ b/Wallet/RPCHelper.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Elastos Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ELASTOS_SPVSDK_RPCHELPER_H__ +#define __ELASTOS_SPVSDK_RPCHELPER_H__ + +#include +#include "nlohmann/json.hpp" + +namespace Elastos { + namespace ElaWallet { + + class RPCHelper { + public: + RPCHelper(); + + ~RPCHelper(); + + nlohmann::json Get(const std::string &url) const; + + nlohmann::json Post(const std::string &url, const std::string &rawData) const; + + private: + CURL *_curl; + }; + + } +} + +#endif diff --git a/Wallet/Wallet.cpp b/Wallet/Wallet.cpp index dafd9852c..908f6c607 100644 --- a/Wallet/Wallet.cpp +++ b/Wallet/Wallet.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include "RPCHelper.h" #include #include @@ -59,6 +61,9 @@ static IMasterWallet *currentWallet = nullptr; static bool verboseMode = false; static const char *SplitLine = "------------------------------------------------------------------------------------------------------------------------"; +static std::string ETHSC_TESTNET_API_URL = "http://api-testnet.elastos.io:21634/api/1/eth/history"; +static std::string ETHSC_MAINNET_API_URL = "http://api.elastos.io:20634/api/1/eth/history"; + class WalletData { public: WalletData() : @@ -200,8 +205,215 @@ class SubWalletCallback : public ISubWalletCallback { } virtual void OnETHSCEventHandled(const nlohmann::json &event) { - if (verboseMode) - std::cout << "*** Wallet " << _chainID << " event: " << event.dump(4) << std::endl; + + } + + /** + * @param id request id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": "0x1dfd14000" // 8049999872 Wei + * } + */ + virtual nlohmann::json GasPrice(int id) { + nlohmann::json j; + // rpc request + // ... + j["id"] = id; + j["result"] = "0x1dfd14000"; + return j; + } + + /** + * @param from + * @param to + * @param amount + * @param gasPrice + * @param data + * @param id request id + * @return If successful, return below. Otherwise {} or null will be returned to indicate the error. + * { + * "id": 0, + * "result": "0x5208" // 21000 + * } + */ + virtual nlohmann::json EstimateGas(const std::string &from, + const std::string &to, + const std::string &amount, + const std::string &gasPrice, + const std::string &data, + int id) { + nlohmann::json j; + // rpc request + // ... + j["id"] = id; + j["result"] = "0x5208"; + return j; + } + + /** + * @param address + * @param id + * @return + * { + * "id": 0, + * "result": "0x0234c8a3397aab58" // 158972490234375000 + * } + */ + virtual nlohmann::json GetBalance(const std::string &address, int id) { + nlohmann::json j; + // rpc request + j["id"] = id; + j["result"] = "0x0234c8a3397aab58"; + return j; + } + + virtual nlohmann::json SubmitTransaction(const std::string &tx, int id) { + nlohmann::json j; + // rpc request + j["id"] = id; + j["result"] = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"; + return j; + } + + virtual nlohmann::json GetTransactions(const std::string &address, + uint64_t begBlockNumber, + uint64_t endBlockNumber, + int id) { + nlohmann::json j, txns = nlohmann::json::array(), tx; + RPCHelper rpcHelper; + + std::string url; + if (network == "MainNet") { + url = ETHSC_MAINNET_API_URL + "?address=" + address + + "&begBlockNumber=" + std::to_string(begBlockNumber) + + "&endBlockNumber=" + std::to_string(endBlockNumber) + + "&sort=desc"; + } else if (network == "TestNet") { + url = ETHSC_TESTNET_API_URL + "?address=" + address + + "&begBlockNumber=" + std::to_string(begBlockNumber) + + "&endBlockNumber=" + std::to_string(endBlockNumber) + + "&sort=desc"; + } else if (network == "RegTest") { + // TODO fix here later + } else if (network == "PrvNet") { + // TODO fix here later + } + + nlohmann::json respond = rpcHelper.Get(url); + + nlohmann::json result = respond["result"]; + if (result.is_array()) { + for (nlohmann::json::iterator it = result.begin(); it != result.end(); ++it) { + nlohmann::json r = *it; + tx["hash"] = r["hash"]; + tx["from"] = r["from"]; + tx["to"] = r["to"]; + tx["contract"] = r["contractAddress"]; // or "", if none was created + tx["amount"] = r["value"]; + tx["gasLimit"] = "5012644"; + tx["gasPrice"] = r["gasPrice"]; + tx["data"] = r["input"]; // input + tx["nonce"] = r["nonce"]; + tx["gasUsed"] = r["gasUsed"]; + tx["blockNumber"] = r["blockNumber"]; + tx["blockHash"] = r["blockHash"]; + tx["blockConfirmations"] = r["confirmations"]; + tx["blockTransactionIndex"] = r["transactionIndex"]; + tx["blockTimestamp"] = r["timeStamp"]; + tx["isError"] = r["isError"]; + tx["transferType"] = r["transferType"]; + + txns.push_back(tx); + } + } + + j["id"] = id; + j["result"] = txns; + return j; + } + + virtual nlohmann::json GetLogs(const std::string &contract, + const std::string &address, + const std::string &event, + uint64_t begBlockNumber, + uint64_t endBlockNumber, + int id) { + nlohmann::json j, log, logs = nlohmann::json::array(); + + if (contract.empty()) { + RPCHelper rpcHelper; + char hex[20]; + nlohmann::json rawJson, param, params = nlohmann::json::array(), topics = nlohmann::json::array(); + + rawJson["jsonrpc"] = "2.0"; + rawJson["method"] = "eth_getLogs"; + + topics.push_back(event); + topics.push_back(nlohmann::json()); + topics.push_back(address); + param["topics"] = topics; + + sprintf(hex, "0x%llx", begBlockNumber); + param["fromBlock"] = hex; + sprintf(hex, "0x%llx", endBlockNumber); + param["toBlock"] = hex; + params.push_back(param); + rawJson["params"] = params; + + rawJson["id"] = id; + std::string url = "http://api.elastos.io:21636"; + std::string rawData = rawJson.dump(); + nlohmann::json respond = rpcHelper.Post(url, rawData); + + nlohmann::json result = respond["result"]; + if (result.is_array()) { + logs = result; + } + } else { + // TODO: ... + } + + j["id"] = id; + j["result"] = logs; + return j; + } + + virtual nlohmann::json GetTokens(int id) { + nlohmann::json j, tokens = nlohmann::json::array(), token; + // rpc request +#if 1 + token["address"] = "0xfdce7fb4050cd43c654c6cecead950343990ce75"; + token["symbol"] = "TTECH"; + token["name"] = "Trinity TECH"; + token["description"] = ""; + token["decimals"] = 0; + token["defaultGasLimit"] = ""; + token["defaultGasPrice"] = ""; + tokens.push_back(token); +#endif + j["id"] = id; + j["result"] = tokens; + return j; + } + + virtual nlohmann::json GetBlockNumber(int id) { + nlohmann::json j; + // rpc request + // ... + j["id"] = id; + j["result"] = "0x4b7"; + return j; + } + + virtual nlohmann::json GetNonce(const std::string &address, int id) { + nlohmann::json j; + // rpc request + // ... + j["id"] = id; + j["result"] = "0x1"; + return j; } private: @@ -305,11 +517,16 @@ static std::string convertAmount(const std::string &amount) { return std::to_string((uint64_t) (std::stod(amount) * SELA_PER_ELA)); } +static std::string convertETHAmount(const std::string &amount) { + return std::to_string((uint64_t) (std::stod(amount) * 1000000000000000000llU)); +} + static void subWalletOpen(IMasterWallet *masterWallet, ISubWallet *subWallet) { WalletData walletData; - ISubWalletCallback *callback = new SubWalletCallback(masterWallet->GetID(), subWallet->GetChainID()); + ISubWalletCallback *callback = new SubWalletCallback(masterWallet->GetID(), subWallet->GetChainID()); walletData.SetCallback(callback); + subWallet->AddCallback(callback); if (masterWalletData.find(masterWallet->GetID()) != masterWalletData.end()) { masterWalletData[masterWallet->GetID()][subWallet->GetChainID()] = walletData; @@ -319,7 +536,6 @@ static void subWalletOpen(IMasterWallet *masterWallet, ISubWallet *subWallet) { masterWalletData[masterWallet->GetID()] = subWalletData; } - subWallet->AddCallback(callback); subWallet->SyncStart(); } @@ -329,7 +545,6 @@ static void subWalletClose(IMasterWallet *masterWallet, ISubWallet *subWallet) { subWallet->SyncStop(); subWallet->RemoveCallback(); - auto callback = static_cast(masterWalletData[walletName][chainID].GetCallback()); delete callback; callback = nullptr; @@ -884,23 +1099,23 @@ static int didtx(int argc, char *argv[]) { getSubWallet(subWallet, currentWallet, CHAINID_ID); std::string didDoc; + nlohmann::json payload; if (argc == 2) { std::string filepath = argv[1]; std::ifstream is(filepath); - std::string line; - while (!is.eof()) { - is >> line; - didDoc += line; - line.clear(); + if (!is.good()) { + std::cout << "file '" << filepath << "' do not exist!" << std::endl; + return ERRNO_APP; } + is >> payload; } else { struct termios told = enableLongInput(); std::cout << "Enter id document: "; - std::getline(std::cin, didDoc); + std::cin >> payload; recoveryTTYSetting(&told); } - nlohmann::json tx = subWallet->CreateIDTransaction(nlohmann::json::parse(didDoc), ""); + nlohmann::json tx = subWallet->CreateIDTransaction(payload, "", "0"); signAndPublishTx(subWallet, tx); } catch (const std::exception &e) { exceptionError(e); @@ -1475,6 +1690,25 @@ static int tracking(int argc, char *argv[]) { return 0; } +// loglevel (trace | debug | info ...) +static int loglevel(int argc, char *argv[]) { + checkParam(2); + if (manager == nullptr) { + std::cerr << "wallet manager is null" << std::endl; \ + return ERRNO_APP; \ + } + + std::string level = argv[1]; + + try { + manager->SetLogLevel(level); + } catch (const std::exception &e) { + exceptionError(e); + return ERRNO_APP; + } + return 0; +} + // vote (cr | dpos) static int vote(int argc, char *argv[]) { checkParam(2); @@ -1561,28 +1795,56 @@ static int _export(int argc, char *argv[]) { return 0; } -// passwd +// passwd [reset] static int passwd(int argc, char *argv[]) { + if (argc > 2) { + invalidCmdError(); + return ERRNO_CMD; + } + checkCurrentWallet(); try { - std::string oldPassword = getpass("Old Password: "); - if (!currentWallet->VerifyPayPassword(oldPassword)) { - std::cerr << "Wrong password" << std::endl; - return ERRNO_APP; - } + if (argc == 1) { + std::string oldPassword = getpass("Old Password: "); + if (!currentWallet->VerifyPayPassword(oldPassword)) { + std::cerr << "Wrong password" << std::endl; + return ERRNO_APP; + } - std::string newPassword = getpass("New Password: "); - std::string newPasswordVerify = getpass("Retype New Password: "); - if (newPassword != newPasswordVerify) { - std::cerr << "Password not match" << std::endl; - return ERRNO_APP; - } + std::string newPassword = getpass("New Password: "); + std::string newPasswordVerify = getpass("Retype New Password: "); + if (newPassword != newPasswordVerify) { + std::cerr << "Password not match" << std::endl; + return ERRNO_APP; + } + + currentWallet->ChangePassword(oldPassword, newPassword); + } else if (argc == 2) { + std::cout << "Enter mnemonic:"; + char line[1024] = {0}; + fgets(line, sizeof(line), stdin); + std::string mnemonic = line; + mnemonic.erase(mnemonic.find_first_of('\n')); + std::cout << "mnemonic: " << mnemonic << std::endl; + + std::string paypasswd, passphrase; + if (0 > createPassphrase(passphrase)) { + std::cerr << "Create failed!" << std::endl; + return ERRNO_APP; + } + + if (0 > createPaymentPassword(paypasswd)) { + std::cerr << "Create failed!" << std::endl; + return ERRNO_APP; + } - currentWallet->ChangePassword(oldPassword, newPassword); + currentWallet->ResetPassword(mnemonic, passphrase, paypasswd); + } } catch (const std::exception &e) { exceptionError(e); return ERRNO_APP; } + return 0; } @@ -1615,26 +1877,56 @@ static int verify(int argc, char *argv[]) { return 0; } -// transfer chainID address amount +// transfer chainID static int transfer(int argc, char *argv[]) { - checkParam(4); + checkParam(2); checkCurrentWallet(); std::string chainID = argv[1]; - std::string addr = argv[2]; - std::string amount = "0"; + std::string addr; + std::string amount; try { + nlohmann::json tx; if (chainID == CHAINID_ETHSC) { - amount = argv[3]; + IEthSidechainSubWallet *subWallet; + getSubWallet(subWallet, currentWallet, chainID); + + std::cout << "address: " << std::endl; + std::getline(std::cin, addr); + + std::cout << "amount: " << std::endl; + std::getline(std::cin, amount); + amount = convertETHAmount(amount); + + std::string gasPrice; + std::cout << "gasPrice: " << std::endl; + std::getline(std::cin, gasPrice); + + std::string gasLimit; + std::cout << "gasLimit: " << std::endl; + std::getline(std::cin, gasLimit); + + std::string data; + std::cout << "data: " << std::endl; + std::getline(std::cin, data); + + tx = subWallet->CreateTransferGeneric(addr, amount, ETHER_WEI, gasPrice, ETHER_WEI, gasLimit, data); + signAndPublishTx(subWallet, tx); } else { + std::cout << "address: " << std::endl; + std::getline(std::cin, addr); + + std::cout << "amount: " << std::endl; + std::getline(std::cin, amount); amount = convertAmount(argv[3]); - } - ISubWallet *subWallet; - getSubWallet(subWallet, currentWallet, chainID); - nlohmann::json tx = subWallet->CreateTransaction("", addr, amount, ""); - signAndPublishTx(subWallet, tx); + ISubWallet *subWallet; + getSubWallet(subWallet, currentWallet, chainID); + + tx = subWallet->CreateTransaction("", addr, amount, ""); + signAndPublishTx(subWallet, tx); + } } catch (const std::exception &e) { exceptionError(e); return ERRNO_APP; @@ -1771,21 +2063,119 @@ static int _tx(int argc, char *argv[]) { struct tm tm; for (nlohmann::json::iterator it = tx.begin(); it != tx.end(); ++it) { if (txHash.empty()) { - std::string txHash = (*it)["TxHash"]; - unsigned int confirm = (*it)["ConfirmStatus"]; + if (chainID == CHAINID_ETHSC) { + std::string Hash = (*it)["Hash"]; + time_t t = (*it)["Timestamp"]; + uint64_t confirm = (*it)["Confirmations"]; + std::string amount = (*it)["Amount"]; + + localtime_r(&t, &tm); + strftime(buf, sizeof(buf), "%F %T", &tm); + + printf("%s %8llu %s %s\n", Hash.c_str(), confirm, buf, amount.c_str()); + } else { + std::string Hash = (*it)["TxHash"]; + unsigned int confirm = (*it)["ConfirmStatus"]; + time_t t = (*it)["Timestamp"]; + std::string dir = (*it)["Direction"]; + std::string status = (*it)["Status"]; + double amount = std::stod((*it)["Amount"].get()) / SELA_PER_ELA; + + localtime_r(&t, &tm); + strftime(buf, sizeof(buf), "%F %T", &tm); + printf("%s %8u %9s %8s %s %.8lf\n", Hash.c_str(), confirm, status.c_str(), dir.c_str(), + buf, amount); + } + } else { + std::cout << (*it).dump(4) << std::endl; + } + } + } + + std::cout << "'txid' Detail, 'n' Next Page, 'b' Previous Page, 'q' Exit: "; + std::getline(std::cin, cmd); + + if (cmd == "n") { + if (curPage < (max + cntPerPage) / cntPerPage) { + curPage++; + show = true; + } else { + std::cout << "already last page" << std::endl; + show = true; + } + } else if (cmd == "b") { + if (curPage > 1) { + curPage--; + show = true; + } else { + std::cout << "already first page" << std::endl; + show = true; + } + } else if (cmd == "q") { + break; + } else if (cmd.size() == 64 || cmd.size() == 66) { + txHash = cmd; + show = true; + } else { + std::cout << "invalid input" << std::endl; + show = false; + } + } while (cmd != "q"); + } catch (const std::exception &e) { + exceptionError(e); + return ERRNO_APP; + } + return 0; +} + +// tokentx tokenSymbol +static int _tokentx(int argc, char *argv[]) { + if (argc != 2) { + invalidCmdError(); + return ERRNO_CMD; + } + checkCurrentWallet(); + + std::string tokenSymbol = argv[1]; + + try { + IEthSidechainSubWallet *subWallet; + getSubWallet(subWallet, currentWallet, CHAINID_ETHSC); + + int cntPerPage = 20; + int curPage = 1; + int start, max; + std::string cmd; + bool show = true; + std::string txHash; + + do { + if (show) { + start = cntPerPage * (curPage - 1); + nlohmann::json txJosn; + txJosn = subWallet->GetTokenTransactions(start, cntPerPage, txHash, tokenSymbol); + + nlohmann::json tx = txJosn["Transactions"]; + max = txJosn["MaxCount"]; + + printf("%d / %d\n", curPage, (max + cntPerPage) / cntPerPage); + char buf[100]; + struct tm tm; + for (nlohmann::json::iterator it = tx.begin(); it != tx.end(); ++it) { + if (txHash.empty()) { + std::string Hash = (*it)["Hash"]; time_t t = (*it)["Timestamp"]; - std::string dir = (*it)["Direction"]; - std::string status = (*it)["Status"]; - double amount = std::stod((*it)["Amount"].get()) / SELA_PER_ELA; + uint64_t confirm = (*it)["Confirmations"]; + std::string amount = (*it)["Amount"]; localtime_r(&t, &tm); strftime(buf, sizeof(buf), "%F %T", &tm); - printf("%s %8u %9s %8s %s %.8lf\n", txHash.c_str(), confirm, status.c_str(), dir.c_str(), buf, amount); + + printf("%s %8llu %s %s\n", Hash.c_str(), confirm, buf, amount.c_str()); } else { std::cout << (*it).dump(4) << std::endl; } } - txHash.clear(); } std::cout << "'txid' Detail, 'n' Next Page, 'b' Previous Page, 'q' Exit: "; @@ -1809,7 +2199,7 @@ static int _tx(int argc, char *argv[]) { } } else if (cmd == "q") { break; - } else if (cmd.size() == 64) { + } else if (cmd.size() == 64 || cmd.size() == 66) { txHash = cmd; show = true; } else { @@ -2107,6 +2497,7 @@ struct command { {"open", _open, "chainID Open wallet of `chainID`."}, {"close", _close, "chainID Close wallet of `chainID`."}, {"tx", _tx, "chainID [coinbase] List all tx/coinbase tx records."}, + {"tokentx", _tokentx, "tokenSymbol List all token tx records."}, {"rawtx", _rawtx, "chainID Convert spv tx to rawtx"}, {"consolidate",consolidate, "chainID Consolidate fragmentary utxo"}, {"signtx", signtx, "chainID Sign tx"}, @@ -2114,7 +2505,7 @@ struct command { {"utxo", utxo, "chainID List all utxos"}, {"balance", balance, "chainID List balance info"}, {"fixpeer", fixpeer, "chainID ip port Set fixed peer to sync."}, - {"transfer", transfer, "chainID address amount Transfer ELA from `chainID`."}, + {"transfer", transfer, "chainID Transfer asset from `chainID`."}, {"receive", _receive, "chainID Get receive address of `chainID`."}, {"address", address, "chainID [internal] Get the revceive addresses or change addresses of chainID."}, {"proposal", proposal, " Create proposal tx."}, @@ -2130,6 +2521,7 @@ struct command { {"network", _network, "[netType] Show current net type or set net type: 'MainNet', 'TestNet', 'RegTest', 'PrvNet'"}, {"verbose", verbose, "(on | off) Set verbose mode."}, {"tracking", tracking, " Create proposal tracking tx."}, + {"loglevel", loglevel, "(trace | debug | info | warning | error | critical | off"}, {"exit", NULL, " Quit wallet."}, {"quit", NULL, " Quit wallet."}, {NULL, NULL, NULL}