From 80c0c053694371f32c8e74124856af78e3d483ef Mon Sep 17 00:00:00 2001 From: cbhaiji Date: Mon, 10 Feb 2020 11:57:41 +0530 Subject: [PATCH] BIP-44 Support for Bitcoin and Litecoin Add support for Bitcoin Testnet --- coins.json | 32 ++- docs/coins.md | 1 + include/PPTrustWalletCore/TWCoinType.h | 1 + src/Any/Signer.cpp | 257 +++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 src/Any/Signer.cpp diff --git a/coins.json b/coins.json index 5fc87eb88cb..54742a3347c 100644 --- a/coins.json +++ b/coins.json @@ -48,7 +48,7 @@ "symbol": "BTC", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/84'/0'/0'/0/0", + "derivationPath": "m/44'/0'/0'/0/0", "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 0, @@ -72,6 +72,34 @@ "clientDocs": "https://github.com/trezor/blockbook/blob/master/docs/api.md" } }, + { + "id": "bitcointest", + "name": "Bitcoin Testnet", + "symbol": "BTC", + "decimals": 8, + "blockchain": "Bitcoin", + "derivationPath": "m/44'/1'/0'/0/0", + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "p2pkhPrefix": 1, + "p2shPrefix": 196, + "hrp": "tb", + "publicKeyHasher": "sha256ripemd", + "base58Hasher": "sha256d", + "xpub": "zpub", + "xprv": "zprv", + "explorer": { + "url": "https://blockchair.com", + "txPath": "/bitcoin/transaction/", + "accountPath": "/bitcoin/address/" + }, + "info": { + "url": "https://bitcoin.org", + "client": "https://github.com/trezor/blockbook", + "clientPublic": "", + "clientDocs": "https://github.com/trezor/blockbook/blob/master/docs/api.md" + } + }, { "id": "bitcoincash", "name": "Bitcoin Cash", @@ -328,7 +356,7 @@ "symbol": "LTC", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/84'/2'/0'/0/0", + "derivationPath": "m/44'/2'/0'/0/0", "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 48, diff --git a/docs/coins.md b/docs/coins.md index bd8b83ac4f5..3905983a533 100644 --- a/docs/coins.md +++ b/docs/coins.md @@ -5,6 +5,7 @@ This list is generated from [./coins.json](../coins.json) | Index | Name | Symbol | Logo | URL | | ------- | ---------------- | ------ | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | | 0 | Bitcoin | BTC | | | +| 1 | Bitcoin Testnet | BTC | | | | 2 | Litecoin | LTC | | | | 3 | Dogecoin | DOGE | | | | 5 | Dash | DASH | | | diff --git a/include/PPTrustWalletCore/TWCoinType.h b/include/PPTrustWalletCore/TWCoinType.h index 0c2a152cec0..ebb18568562 100644 --- a/include/PPTrustWalletCore/TWCoinType.h +++ b/include/PPTrustWalletCore/TWCoinType.h @@ -26,6 +26,7 @@ enum TWCoinType { TWCoinTypeAion = 425, TWCoinTypeBinance = 714, TWCoinTypeBitcoin = 0, + TWCoinTypeBitcoinTestnet = 1, TWCoinTypeBitcoinCash = 145, TWCoinTypeCallisto = 820, TWCoinTypeCardano = 1815, // Note: Cardano Shelley testnet uses purpose 1852 (not 44) 1852/1815 diff --git a/src/Any/Signer.cpp b/src/Any/Signer.cpp new file mode 100644 index 00000000000..fbbadee80bb --- /dev/null +++ b/src/Any/Signer.cpp @@ -0,0 +1,257 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Signer.h" +#include "Binance/Signer.h" +#include "Cosmos/Signer.h" +#include "Data.h" +#include "Ethereum/Signer.h" +#include "Harmony/Signer.h" +#include "HexCoding.h" +#include "IoTeX/Signer.h" +#include "Nano/Signer.h" +#include "Nebulas/Signer.h" +#include "PrivateKey.h" +#include "Tezos/Signer.h" +#include "Tron/Signer.h" +#include "VeChain/Signer.h" +#include "Wanchain/Signer.h" +#include "Waves/Signer.h" +#include "Stellar/Signer.h" +#include "Bitcoin/Transaction.h" +#include "Bitcoin/TransactionSigner.h" +#include "Solana/Signer.h" + +#include +#include + +#include + +using namespace TW; +using namespace google::protobuf; + +TW::Any::Proto::SigningOutput TW::Any::Signer::sign() const noexcept { + const auto coinType = (TWCoinType)input.coin_type(); + const auto transaction = input.transaction(); + const auto privateKey = PrivateKey(parse_hex(input.private_key())); + + auto output = TW::Any::Proto::SigningOutput(); + + switch (coinType) { + case TWCoinTypeCosmos: { + Cosmos::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Cosmos::Signer(std::move(message)).build(); + output.set_output(signerOutput.json()); + } + break; + } + case TWCoinTypeBinance: { + Binance::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Binance::Signer(std::move(message)).build(); + output.set_output(hex(signerOutput.begin(), signerOutput.end())); + } + break; + } + case TWCoinTypeTomoChain: + case TWCoinTypeCallisto: + case TWCoinTypeThunderToken: + case TWCoinTypePOANetwork: + case TWCoinTypeEthereumClassic: + case TWCoinTypeEthereum: { + Ethereum::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Ethereum::Signer(load(message.chain_id())).sign(message); + auto encoded = signerOutput.encoded(); + output.set_output(hex(encoded.begin(), encoded.end())); + } + break; + } + case TWCoinTypeTezos: { + Tezos::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto operations = message.operation_list().operations(); + auto operation_list = Tezos::OperationList(message.operation_list().branch()); + operation_list.operation_list.assign(operations.begin(), operations.end()); + auto signerOutput = Tezos::Signer().signOperationList(privateKey, operation_list); + output.set_output(hex(signerOutput.begin(), signerOutput.end())); + } + break; + } + case TWCoinTypeIoTeX: { + IoTeX::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_privatekey(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = IoTeX::Signer(std::move(message)).build(); + auto encoded = signerOutput.encoded(); + output.set_output(hex(encoded.begin(), encoded.end())); + } + break; + } + case TWCoinTypeWanchain: { + Ethereum::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Wanchain::Signer(load(message.chain_id())).sign(message); + auto encoded = signerOutput.encoded(); + output.set_output(hex(encoded.begin(), encoded.end())); + } + break; + } + case TWCoinTypeWaves: { + Waves::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); + auto wavesTransaction = Waves::Transaction(std::move(message), publicKey.bytes); + auto signature = Waves::Signer::sign(privateKey, wavesTransaction); + auto jsonOutput = wavesTransaction.buildJson(signature).dump(); + output.set_output(jsonOutput); + } + break; + } + case TWCoinTypeNebulas: { + Nebulas::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Nebulas::Signer(load(message.chain_id())).sign(message); + auto signature = signerOutput.signature(); + output.set_output(hex(signature.begin(), signature.end())); + } + break; + } + case TWCoinTypeTron: { + Tron::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Tron::Signer::sign(message); + output.set_output(signerOutput.json()); + } + break; + } + case TWCoinTypeVeChain: { + VeChain::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = VeChain::Signer::sign(message); + auto encoded = signerOutput.encoded(); + output.set_output(hex(encoded.begin(), encoded.end())); + } + break; + } + case TWCoinTypeHarmony: { + Harmony::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Harmony::Signer::sign(message); + auto encoded = signerOutput.encoded(); + output.set_output(hex(encoded.begin(), encoded.end())); + } + break; + } + case TWCoinTypeNano: { + Nano::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signer = Nano::Signer(message); + // only signature is included (signer.blockHash not) + auto signature = signer.sign(); + output.set_output(hex(signature.begin(), signature.end())); + } + break; + } + case TWCoinTypeStellar: { + Stellar::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signer = Stellar::Signer(message); + auto signerOutput = signer.sign(); + output.set_output(signerOutput); + } + break; + } + case TWCoinTypeSolana: { + Solana::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signerOutput = Solana::Signer::signProtobuf(message); + auto encoded = signerOutput.encoded(); + output.set_output(hex(encoded.begin(), encoded.end())); + } + break; + } + case TWCoinTypeBitcoinCash: + case TWCoinTypeLitecoin: + case TWCoinTypeBitcoin: + case TWCoinTypeBitcoinTestnet: { + Bitcoin::Proto::SigningInput message; + parse(transaction, &message, output); + if (output.success()) { + message.add_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto signer = + Bitcoin::TransactionSigner(std::move(message)); + auto signerOutput = signer.sign(); + auto signedTx = signerOutput.payload(); + Data serialized; + signedTx.encode(true, serialized); + output.set_output(hex(serialized)); + } + break; + } + default: + auto error = new Proto::SigningOutput_Error(); + error->set_code(SignerErrorCodeNotSupported); + error->set_description("Network not supported"); + output.set_allocated_error(error); + } + + return output; +} + +void TW::Any::Signer::parse(const std::string &transaction, Message *message, + TW::Any::Proto::SigningOutput &output) const noexcept { + util::JsonParseOptions options; + options.case_insensitive_enum_parsing = true; + options.ignore_unknown_fields = false; + + auto result = JsonStringToMessage(transaction, message, options); + + if (result.ok()) { + output.set_success(true); + return; + } + + auto error = new TW::Any::Proto::SigningOutput_Error(); + error->set_code(SignerErrorCodeInvalidJson); + error->set_description(result.error_message()); + output.set_allocated_error(error); +} + +void TW::Any::Signer::toJson(const google::protobuf::Message &message, std::string *json_string) const + noexcept { + util::JsonPrintOptions options; + options.preserve_proto_field_names = true; + + MessageToJsonString(message, json_string, options); +}