Skip to content

Commit c580cda

Browse files
fix: update key retrieval methods to return nullable types on failure
1 parent cf4d0ad commit c580cda

16 files changed

Lines changed: 139 additions & 97 deletions

File tree

include/TrustWalletCore/TWHDWallet.h

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ TWData* _Nonnull TWHDWalletEntropy(struct TWHDWallet* _Nonnull wallet);
9696
/// \param wallet non-null TWHDWallet
9797
/// \param curve a curve
9898
/// \note Returned object needs to be deleted with \TWPrivateKeyDelete
99-
/// \return Non-null corresponding private key
99+
/// \note Null is returned if the key cannot be derived (e.g. empty mnemonic entropy)
100+
/// \return Nullable corresponding private key
100101
TW_EXPORT_METHOD
101-
struct TWPrivateKey* _Nonnull TWHDWalletGetMasterKey(struct TWHDWallet* _Nonnull wallet, enum TWCurve curve);
102+
struct TWPrivateKey* _Nullable TWHDWalletGetMasterKey(struct TWHDWallet* _Nonnull wallet, enum TWCurve curve);
102103

103104
/// Generates the default private key for the specified coin, using default derivation.
104105
///
@@ -107,28 +108,31 @@ struct TWPrivateKey* _Nonnull TWHDWalletGetMasterKey(struct TWHDWallet* _Nonnull
107108
/// \param wallet non-null TWHDWallet
108109
/// \param coin a coin type
109110
/// \note Returned object needs to be deleted with \TWPrivateKeyDelete
110-
/// \return return the default private key for the specified coin
111+
/// \note Null is returned if the key cannot be derived (e.g. empty mnemonic entropy)
112+
/// \return the default private key for the specified coin, or null on failure
111113
TW_EXPORT_METHOD
112-
struct TWPrivateKey* _Nonnull TWHDWalletGetKeyForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin);
114+
struct TWPrivateKey* _Nullable TWHDWalletGetKeyForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin);
113115

114116
/// Generates the default address for the specified coin (without exposing intermediary private key), default derivation.
115117
///
116118
/// \see TWHDWalletGetAddressDerivation
117119
/// \param wallet non-null TWHDWallet
118120
/// \param coin a coin type
119-
/// \return return the default address for the specified coin as a non-null TWString
121+
/// \note Null is returned if the address cannot be derived (e.g. empty mnemonic entropy)
122+
/// \return the default address for the specified coin, or null on failure
120123
TW_EXPORT_METHOD
121-
TWString* _Nonnull TWHDWalletGetAddressForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin);
124+
TWString* _Nullable TWHDWalletGetAddressForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin);
122125

123126
/// Generates the default address for the specified coin and derivation (without exposing intermediary private key).
124127
///
125128
/// \see TWHDWalletGetAddressForCoin
126129
/// \param wallet non-null TWHDWallet
127130
/// \param coin a coin type
128131
/// \param derivation a (custom) derivation to use
129-
/// \return return the default address for the specified coin as a non-null TWString
132+
/// \note Null is returned if the address cannot be derived (e.g. empty mnemonic entropy)
133+
/// \return the default address for the specified coin, or null on failure
130134
TW_EXPORT_METHOD
131-
TWString* _Nonnull TWHDWalletGetAddressDerivation(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation);
135+
TWString* _Nullable TWHDWalletGetAddressDerivation(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation);
132136

133137
/// Generates the private key for the specified derivation path.
134138
///
@@ -150,9 +154,10 @@ struct TWPrivateKey* _Nullable TWHDWalletGetKey(struct TWHDWallet* _Nonnull wall
150154
/// \param coin a coin type
151155
/// \param derivation a (custom) derivation to use
152156
/// \note Returned object needs to be deleted with \TWPrivateKeyDelete
153-
/// \return The private key for the specified derivation path/coin
157+
/// \note Null is returned if the key cannot be derived (e.g. empty mnemonic entropy)
158+
/// \return The private key for the specified derivation path/coin, or null on failure
154159
TW_EXPORT_METHOD
155-
struct TWPrivateKey* _Nonnull TWHDWalletGetKeyDerivation(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation);
160+
struct TWPrivateKey* _Nullable TWHDWalletGetKeyDerivation(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation);
156161

157162
/// Generates the private key for the specified derivation path and curve.
158163
///
@@ -174,9 +179,10 @@ struct TWPrivateKey* _Nullable TWHDWalletGetKeyByCurve(struct TWHDWallet* _Nonnu
174179
/// \param change valid bip44 change
175180
/// \param address valid bip44 address
176181
/// \note Returned object needs to be deleted with \TWPrivateKeyDelete
177-
/// \return The private key for the specified bip44 parameters
182+
/// \note Null is returned if the key cannot be derived (e.g. empty mnemonic entropy)
183+
/// \return The private key for the specified bip44 parameters, or null on failure
178184
TW_EXPORT_METHOD
179-
struct TWPrivateKey* _Nonnull TWHDWalletGetDerivedKey(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, uint32_t account, uint32_t change, uint32_t address);
185+
struct TWPrivateKey* _Nullable TWHDWalletGetDerivedKey(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, uint32_t account, uint32_t change, uint32_t address);
180186

181187
/// Returns the extended private key (for default 0 account).
182188
///

src/HDWallet.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ static HDNode getMasterNode(const HDWallet<seedSize>& wallet, TWCurve curve) {
114114
// Derives the root Cardano HDNode from a passphrase and the entropy encoded in
115115
// a BIP-0039 mnemonic using the Icarus derivation (V2) scheme
116116
const auto entropy = wallet.getEntropy();
117+
if (entropy.empty()) {
118+
// Entropy is empty for mnemonics created with `check=false` that are not valid
119+
// BIP-0039 English mnemonics (e.g. non-English wordlists), or for wallets
120+
// constructed directly from a raw seed. Deriving from empty entropy would
121+
// produce the same constant secret for every such wallet.
122+
throw std::invalid_argument("Cannot derive a Cardano key: mnemonic entropy is empty");
123+
}
117124
uint8_t secret[CARDANO_SECRET_LENGTH];
118125
secret_from_entropy_cardano_icarus((const uint8_t*)"", 0, entropy.data(), int(entropy.size()), secret, nullptr);
119126
hdnode_from_secret_cardano(secret, &node);

src/interface/TWHDWallet.cpp

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,23 +60,31 @@ TWData *_Nonnull TWHDWalletEntropy(struct TWHDWallet *_Nonnull wallet) {
6060
return TWDataCreateWithBytes(wallet->impl.getEntropy().data(), wallet->impl.getEntropy().size());
6161
}
6262

63-
struct TWPrivateKey *_Nonnull TWHDWalletGetMasterKey(struct TWHDWallet *_Nonnull wallet, TWCurve curve) {
64-
return new TWPrivateKey{ wallet->impl.getMasterKey(curve) };
63+
struct TWPrivateKey *_Nullable TWHDWalletGetMasterKey(struct TWHDWallet *_Nonnull wallet, TWCurve curve) {
64+
try {
65+
return new TWPrivateKey{ wallet->impl.getMasterKey(curve) };
66+
} catch (...) {
67+
return nullptr;
68+
}
6569
}
6670

67-
struct TWPrivateKey *_Nonnull TWHDWalletGetKeyForCoin(struct TWHDWallet *wallet, TWCoinType coin) {
71+
struct TWPrivateKey *_Nullable TWHDWalletGetKeyForCoin(struct TWHDWallet *wallet, TWCoinType coin) {
6872
return TWHDWalletGetKeyDerivation(wallet, coin, TWDerivationDefault);
6973
}
7074

71-
TWString *_Nonnull TWHDWalletGetAddressForCoin(struct TWHDWallet *wallet, TWCoinType coin) {
75+
TWString *_Nullable TWHDWalletGetAddressForCoin(struct TWHDWallet *wallet, TWCoinType coin) {
7276
return TWHDWalletGetAddressDerivation(wallet, coin, TWDerivationDefault);
7377
}
7478

75-
TWString *_Nonnull TWHDWalletGetAddressDerivation(struct TWHDWallet *wallet, TWCoinType coin, enum TWDerivation derivation) {
76-
auto derivationPath = TW::derivationPath(coin, derivation);
77-
PrivateKey privateKey = wallet->impl.getKey(coin, derivationPath);
78-
std::string address = deriveAddress(coin, privateKey, derivation);
79-
return TWStringCreateWithUTF8Bytes(address.c_str());
79+
TWString *_Nullable TWHDWalletGetAddressDerivation(struct TWHDWallet *wallet, TWCoinType coin, enum TWDerivation derivation) {
80+
try {
81+
auto derivationPath = TW::derivationPath(coin, derivation);
82+
PrivateKey privateKey = wallet->impl.getKey(coin, derivationPath);
83+
std::string address = deriveAddress(coin, privateKey, derivation);
84+
return TWStringCreateWithUTF8Bytes(address.c_str());
85+
} catch (...) {
86+
return nullptr;
87+
}
8088
}
8189

8290
struct TWPrivateKey *_Nullable TWHDWalletGetKey(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, TWString *_Nonnull derivationPath) {
@@ -89,14 +97,22 @@ struct TWPrivateKey *_Nullable TWHDWalletGetKey(struct TWHDWallet *_Nonnull wall
8997
}
9098
}
9199

92-
struct TWPrivateKey *_Nonnull TWHDWalletGetKeyDerivation(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation) {
93-
auto derivationPath = TW::derivationPath(coin, derivation);
94-
return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) };
100+
struct TWPrivateKey *_Nullable TWHDWalletGetKeyDerivation(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, enum TWDerivation derivation) {
101+
try {
102+
auto derivationPath = TW::derivationPath(coin, derivation);
103+
return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) };
104+
} catch (...) {
105+
return nullptr;
106+
}
95107
}
96108

97-
struct TWPrivateKey *_Nonnull TWHDWalletGetDerivedKey(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, uint32_t account, uint32_t change, uint32_t address) {
98-
const auto derivationPath = DerivationPath(TW::purpose(coin), TW::slip44Id(coin), account, change, address);
99-
return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) };
109+
struct TWPrivateKey *_Nullable TWHDWalletGetDerivedKey(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, uint32_t account, uint32_t change, uint32_t address) {
110+
try {
111+
const auto derivationPath = DerivationPath(TW::purpose(coin), TW::slip44Id(coin), account, change, address);
112+
return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) };
113+
} catch (...) {
114+
return nullptr;
115+
}
100116
}
101117

102118
struct TWPrivateKey *_Nullable TWHDWalletGetKeyByCurve(struct TWHDWallet *_Nonnull wallet, enum TWCurve curve, TWString *_Nonnull derivationPath) {

swift/Example/Example/ContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct ContentView: View {
99

1010
let wallet = HDWallet(strength: 256, passphrase: "")!
1111
var body: some View {
12-
Text("Ethereum address: \(wallet.getAddressForCoin(coin: .ethereum))")
12+
Text("Ethereum address: \(wallet.getAddressForCoin(coin: .ethereum)!)")
1313
.padding()
1414
}
1515
}

swift/Example/ExampleMac/ContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import WalletCore
88
struct ContentView: View {
99
let wallet = HDWallet(strength: 256, passphrase: "")!
1010
var body: some View {
11-
Text("Ethereum address: \(wallet.getAddressForCoin(coin: .ethereum))")
11+
Text("Ethereum address: \(wallet.getAddressForCoin(coin: .ethereum)!)")
1212
.padding()
1313
}
1414
}

swift/Example/ExampleTests/ExampleTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ExampleTests: XCTestCase {
1010

1111
func testEthereum() {
1212
let wallet = HDWallet(mnemonic: "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal", passphrase: "TREZOR")!
13-
let address = wallet.getAddressForCoin(coin: .ethereum)
13+
let address = wallet.getAddressForCoin(coin: .ethereum)!
1414

1515
XCTAssertEqual(address, "0x27Ef5cDBe01777D62438AfFeb695e33fC2335979")
1616
}

swift/Tests/Blockchains/BinanceChainTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class BinanceChainTests: XCTestCase {
1818

1919
func testBinanceMainnet() {
2020
let wallet = HDWallet(mnemonic: "rabbit tilt arm protect banner ill produce vendor april bike much identify pond upset front easily glass gallery address hair priority focus forest angle", passphrase: "")!
21-
let key = wallet.getKeyForCoin(coin: .binance)
21+
let key = wallet.getKeyForCoin(coin: .binance)!
2222
let address = CoinType.binance.deriveAddress(privateKey: key)
2323

2424
XCTAssertEqual(key.data.hexString, "727f677b390c151caf9c206fd77f77918f56904b5504243db9b21e51182c4c06")
@@ -27,7 +27,7 @@ class BinanceChainTests: XCTestCase {
2727

2828
func testBinanceTestnet() {
2929
let wallet = HDWallet(mnemonic: "rabbit tilt arm protect banner ill produce vendor april bike much identify pond upset front easily glass gallery address hair priority focus forest angle", passphrase: "")!
30-
let privateKey = wallet.getKeyForCoin(coin: .binance)
30+
let privateKey = wallet.getKeyForCoin(coin: .binance)!
3131
let publicKey = privateKey.getPublicKeySecp256k1(compressed: true)
3232
let address = AnyAddress(publicKey: publicKey, coin: .binance, hrp: "tbnb")
3333

swift/Tests/Blockchains/CardanoTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class CardanoTests: XCTestCase {
1818

1919
func testDeriveAddressWallet() {
2020
let wallet = HDWallet(mnemonic: "cost dash dress stove morning robust group affair stomach vacant route volume yellow salute laugh", passphrase: "")!
21-
let privateKey = wallet.getKeyForCoin(coin: .cardano)
21+
let privateKey = wallet.getKeyForCoin(coin: .cardano)!
2222
XCTAssertEqual(privateKey.data.hexString, "e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b574437aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fae0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276")
2323
let address = CoinType.cardano.deriveAddress(privateKey: privateKey)
2424
XCTAssertEqual(address, "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq")

swift/Tests/Blockchains/EthereumTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ class EthereumTests: XCTestCase {
266266
XCTAssertEqual(btcXpub, "xpub6Cq43Vqyvb2DwXzjzNeMpPuxXRCN1WnmRCmYLPaaSv2XZXM2yCwUHpWEyB3zQ3FGCQsvY21gecMaQR7b2zhhgiHnjzDYpKCE2LACueaSMuR")
267267
XCTAssertEqual(ethXpub, "xpub6Bgma7boPVudhExmB97iySvatGfnXkfBxYZYNTFYJvVzigUPk1X2iE8VhJPPxVuzjH8wBuTqRBMKCbwMYQNLrFCwYzMugYw4RM5VGNeVDpp")
268268

269-
let ethAddress = wallet.getAddressForCoin(coin: .ethereum)
270-
let btcAddress = wallet.getAddressForCoin(coin: .bitcoin)
269+
let ethAddress = wallet.getAddressForCoin(coin: .ethereum)!
270+
let btcAddress = wallet.getAddressForCoin(coin: .bitcoin)!
271271

272272
XCTAssertEqual(ethAddress, "0xa4531dE99E22B2166d340E7221669DF565c52024")
273273
XCTAssertEqual(btcAddress, "bc1q97jc0jdgsyvvhxydxxd6np8sa920c39l3qpscf")

swift/Tests/Blockchains/EvmosTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ class EvmosTests: XCTestCase {
99

1010
func testAddressData() throws {
1111
let wallet = HDWallet(strength: 256, passphrase: "")!
12-
let nativeEvmos = wallet.getAddressForCoin(coin: .nativeEvmos)
13-
let evmos = wallet.getAddressForCoin(coin: .evmos)
12+
let nativeEvmos = wallet.getAddressForCoin(coin: .nativeEvmos)!
13+
let evmos = wallet.getAddressForCoin(coin: .evmos)!
1414

1515
let addr1 = AnyAddress(string: nativeEvmos, coin: .nativeEvmos)
1616
let addr2 = AnyAddress(string: evmos, coin: .evmos)
@@ -21,7 +21,7 @@ class EvmosTests: XCTestCase {
2121
func testSigningNativeTransfer() {
2222

2323
let wallet = HDWallet(mnemonic: "glue blanket noodle name bring castle degree vibrant great joy usual mother pyramid cat balance swear diagram green split goat token day arm shoe", passphrase: "")!
24-
let privateKey = wallet.getKeyForCoin(coin: .nativeEvmos)
24+
let privateKey = wallet.getKeyForCoin(coin: .nativeEvmos)!
2525
let publicKey = privateKey.getPublicKeySecp256k1(compressed: false)
2626

2727
XCTAssertEqual(publicKey.data.hexString, "049475c9fa23ec693667baa76c4da69b49cccfdf058c4dcb27ba67cfbc9082d9ed9074786560aa698b19bb9729526b1c75934f3d4a78f7be719e4386b749b36310")

0 commit comments

Comments
 (0)