Skip to content

Commit 3bb6a49

Browse files
committed
Filecoin support
1 parent d9fe132 commit 3bb6a49

File tree

23 files changed

+921
-0
lines changed

23 files changed

+921
-0
lines changed

android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,6 @@ class CoinAddressDerivationTests {
8282
POLKADOT -> assertEquals("1b97X8xTpFKMDzJpxiVhdYMNvekBDSfvGFf4DutxFkUjqfR", address)
8383
KAVA -> assertEquals("kava1drpa0x9ptz0fql3frv562rcrhj2nstuz3pas87", address)
8484
CARDANO -> assertEquals("Ae2tdPwUPEZJ2TwjFBMCnjz6t43pr4QVnBjCGSW8BMsttrt2WagC1D7LUWa", address)
85+
FILECOIN -> assertEquals("f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori", address)
8586
}
8687
}

coins.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,5 +1306,26 @@
13061306
"clientPublic": "",
13071307
"clientDocs": "https://cardanodocs.com/introduction/"
13081308
}
1309+
},
1310+
{
1311+
"id": "filecoin",
1312+
"name": "Filecoin",
1313+
"symbol": "FIL",
1314+
"decimals": 18,
1315+
"blockchain": "Filecoin",
1316+
"derivationPath": "m/44'/461'/0'/0/0",
1317+
"curve": "secp256k1",
1318+
"publicKeyType": "secp256k1Extended",
1319+
"explorer": {
1320+
"url": "https://??",
1321+
"txPath": "/??",
1322+
"accountPath": "/??"
1323+
},
1324+
"info": {
1325+
"url": "https://filecoin.io/",
1326+
"client": "https://github.com/filecoin-project/lotus",
1327+
"clientPublic": "",
1328+
"clientDocs": "https://docs.lotu.sh"
1329+
}
13091330
}
13101331
]

docs/coins.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ This list is generated from [./coins.json](../coins.json)
4141
| 434 | Kusama | KSM | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/kusama/info/logo.png" width="32" /> | <https://kusama.network> |
4242
| 457 | Aeternity | AE | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/aeternity/info/logo.png" width="32" /> | <https://aeternity.com> |
4343
| 459 | Kava | KAVA | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/kava/info/logo.png" width="32" /> | <https://kava.io> |
44+
| 461 | Filecoin | FIL | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/filecoin/info/logo.png" width="32" /> | <https://filecoin.io/> |
4445
| 500 | Theta | THETA | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/theta/info/logo.png" width="32" /> | <https://www.thetatoken.org> |
4546
| 501 | Solana | SOL | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/solana/info/logo.png" width="32" /> | <https://solana.com> |
4647
| 714 | Binance | BNB | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/info/logo.png" width="32" /> | <https://binance.org> |

include/TrustWalletCore/TWBlockchain.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ enum TWBlockchain {
4343
TWBlockchainTON = 28,
4444
TWBlockchainPolkadot = 29,
4545
TWBlockchainCardano = 30,
46+
TWBlockchainFilecoin = 31,
4647
};
4748

4849
TW_EXTERN_C_END

include/TrustWalletCore/TWCoinType.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ enum TWCoinType {
7878
TWCoinTypeAlgorand = 283,
7979
TWCoinTypeKusama = 434,
8080
TWCoinTypePolkadot = 354,
81+
TWCoinTypeFilecoin = 461,
8182
};
8283

8384
/// Returns the blockchain for a coin type.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright © 2017-2020 Trust Wallet.
2+
//
3+
// This file is part of Trust. The full Trust copyright notice, including
4+
// terms governing use, modification, and redistribution, is contained in the
5+
// file LICENSE at the root of the source code distribution tree.
6+
7+
#pragma once
8+
9+
#include "TWBase.h"
10+
#include "TWData.h"
11+
#include "TWString.h"
12+
13+
TW_EXTERN_C_BEGIN
14+
15+
/// Represents a Filecoin address.
16+
TW_EXPORT_CLASS
17+
struct TWFilecoinAddress;
18+
19+
/// Compares two addresses for equality.
20+
TW_EXPORT_STATIC_METHOD
21+
bool TWFilecoinAddressEqual(struct TWFilecoinAddress *_Nonnull lhs, struct TWFilecoinAddress *_Nonnull rhs);
22+
23+
/// Determines if the string is a valid Filecoin address.
24+
TW_EXPORT_STATIC_METHOD
25+
bool TWFilecoinAddressIsValidString(TWString *_Nonnull string);
26+
27+
/// Creates an address from a string representation.
28+
TW_EXPORT_STATIC_METHOD
29+
struct TWFilecoinAddress *_Nullable TWFilecoinAddressCreateWithString(TWString *_Nonnull string);
30+
31+
/// Creates an address from a public key.
32+
TW_EXPORT_STATIC_METHOD
33+
struct TWFilecoinAddress *_Nonnull TWFilecoinAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey);
34+
35+
TW_EXPORT_METHOD
36+
void TWFilecoinAddressDelete(struct TWFilecoinAddress *_Nonnull address);
37+
38+
/// Returns the address string representation.
39+
TW_EXPORT_PROPERTY
40+
TWString *_Nonnull TWFilecoinAddressDescription(struct TWFilecoinAddress *_Nonnull address);
41+
42+
TW_EXTERN_C_END
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright © 2017-2020 Trust.
2+
//
3+
// This file is part of Trust. The full Trust copyright notice, including
4+
// terms governing use, modification, and redistribution, is contained in the
5+
// file LICENSE at the root of the source code distribution tree.
6+
7+
#pragma once
8+
9+
#include "TWBase.h"
10+
#include "TWData.h"
11+
#include "TWFilecoinProto.h"
12+
13+
TW_EXTERN_C_BEGIN
14+
15+
/// Helper class to sign Filecoin transactions.
16+
TW_EXPORT_CLASS
17+
struct TWFilecoinSigner;
18+
19+
/// Signs a transaction.
20+
TW_EXPORT_STATIC_METHOD
21+
TW_Filecoin_Proto_SigningOutput TWFilecoinSignerSign(TW_Filecoin_Proto_SigningInput input);
22+
23+
TW_EXTERN_C_END

src/Coin.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "EOS/Address.h"
2121
#include "Ethereum/Address.h"
2222
#include "FIO/Address.h"
23+
#include "Filecoin/Address.h"
2324
#include "Groestlcoin/Address.h"
2425
#include "Harmony/Address.h"
2526
#include "Icon/Address.h"
@@ -186,6 +187,9 @@ bool TW::validateAddress(TWCoinType coin, const std::string& string) {
186187

187188
case TWCoinTypeCardano:
188189
return Cardano::Address::isValid(string);
190+
191+
case TWCoinTypeFilecoin:
192+
return Filecoin::Address::isValid(string);
189193
}
190194
}
191195

@@ -354,6 +358,9 @@ std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) {
354358

355359
case TWCoinTypeCardano:
356360
return Cardano::Address(publicKey).string();
361+
362+
case TWCoinTypeFilecoin:
363+
return Filecoin::Address(publicKey).string();
357364
}
358365
}
359366

src/Filecoin/Address.cpp

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright © 2017-2020 Trust.
2+
//
3+
// This file is part of Trust. The full Trust copyright notice, including
4+
// terms governing use, modification, and redistribution, is contained in the
5+
// file LICENSE at the root of the source code distribution tree.
6+
7+
#include "Address.h"
8+
9+
#include "../Base32.h"
10+
#include "../Data.h"
11+
12+
using namespace TW;
13+
using namespace TW::Filecoin;
14+
15+
static const char BASE32_ALPHABET_FILECOIN[] = "abcdefghijklmnopqrstuvwxyz234567";
16+
static constexpr size_t checksum_size = 4;
17+
18+
bool Address::isValid(const Data& data) {
19+
if (data.size() < 2) {
20+
return false;
21+
}
22+
if (!get_type(data[0])) {
23+
return false;
24+
}
25+
Type type = *get_type(data[0]);
26+
if (type == Type::ID) {
27+
// Verify varuint encoding
28+
if (data.size() > 11)
29+
return false;
30+
if (data.size() == 11 && data[10] > 0x01)
31+
return false;
32+
int i;
33+
for (i = 1; i < data.size(); i++)
34+
if ((data[i] & 0x80) == 0)
35+
break;
36+
return i == data.size() - 1;
37+
} else {
38+
return data.size() == 1 + Address::payload_size(type);
39+
}
40+
}
41+
42+
static bool isValidID(const std::string& string) {
43+
if (string.length() > 22)
44+
return false;
45+
for (int i = 2; i < string.length(); i++) {
46+
if (string[i] < '0' || string[i] > '9') {
47+
return false;
48+
}
49+
}
50+
try {
51+
size_t chars;
52+
std::stoull(string.substr(2), &chars);
53+
return chars > 0;
54+
} catch (...) {
55+
return false;
56+
}
57+
}
58+
59+
static bool isValidBase32(const std::string& string, Address::Type type) {
60+
// Check if valid Base32.
61+
uint8_t size = Address::payload_size(type);
62+
Data decoded;
63+
if (!Base32::decode(string.substr(2), decoded, BASE32_ALPHABET_FILECOIN)) {
64+
return false;
65+
}
66+
67+
// Check size
68+
if (decoded.size() != size + 4) {
69+
return false;
70+
}
71+
72+
// Extract raw address.
73+
Data address;
74+
address.push_back(static_cast<uint8_t>(type));
75+
address.insert(address.end(), decoded.data(), decoded.data() + size);
76+
77+
// Verify checksum.
78+
Data should_sum = Hash::blake2b(address, checksum_size);
79+
return std::memcmp(should_sum.data(), decoded.data() + size, checksum_size) == 0;
80+
}
81+
82+
bool Address::isValid(const std::string& string) {
83+
if (string.length() < 3) {
84+
return false;
85+
}
86+
// Only main net addresses supported.
87+
if (string[0] != PREFIX) {
88+
return false;
89+
}
90+
// Get address type.
91+
auto type = parse_type(string[1]);
92+
if (!type) {
93+
return false;
94+
}
95+
96+
// ID addresses are special, they are just numbers.
97+
return type == Type::ID ? isValidID(string) : isValidBase32(string, *type);
98+
}
99+
100+
Address::Address(const std::string& string) {
101+
if (!isValid(string))
102+
throw std::invalid_argument("Invalid address data");
103+
104+
Type type = *(parse_type(string[1]));
105+
// First byte is type
106+
bytes.push_back(static_cast<uint8_t>(type));
107+
if (type == Type::ID) {
108+
uint64_t id = std::stoull(string.substr(2));
109+
while (id >= 0x80) {
110+
bytes.push_back(((uint8_t)id) | 0x80);
111+
id >>= 7;
112+
}
113+
bytes.push_back((uint8_t)id);
114+
return;
115+
}
116+
117+
Data decoded;
118+
if (!Base32::decode(string.substr(2), decoded, BASE32_ALPHABET_FILECOIN))
119+
throw std::invalid_argument("Invalid address data");
120+
uint8_t payload_size = Address::payload_size(type);
121+
122+
bytes.insert(bytes.end(), decoded.data(), decoded.data() + payload_size);
123+
}
124+
125+
Address::Address(const Data& data) {
126+
if (!isValid(data)) {
127+
throw std::invalid_argument("Invalid address data");
128+
}
129+
bytes = data;
130+
}
131+
132+
Address::Address(const PublicKey& publicKey) {
133+
bytes.push_back(static_cast<uint8_t>(Type::SECP256K1));
134+
Data hash = Hash::blake2b(publicKey.bytes, payload_size(Type::SECP256K1));
135+
bytes.insert(bytes.end(), hash.begin(), hash.end());
136+
}
137+
138+
std::string Address::string() const {
139+
std::string s;
140+
// Main net address prefix
141+
s.push_back(PREFIX);
142+
// Address type prefix
143+
s.push_back(type_ascii(type()));
144+
145+
if (type() == Type::ID) {
146+
uint64_t id = 0;
147+
unsigned shift = 0;
148+
for (int i = 1; i < bytes.size(); i++) {
149+
if (bytes[i] < 0x80) {
150+
id |= bytes[i] << shift;
151+
break;
152+
} else {
153+
id |= ((uint64_t)(bytes[i] & 0x7F)) << shift;
154+
shift += 7;
155+
}
156+
}
157+
s.append(std::to_string(id));
158+
return s;
159+
}
160+
161+
uint8_t payload_size = Address::payload_size(type());
162+
// Base32 encoded body
163+
Data to_encode(payload_size + checksum_size);
164+
// Copy address payload without prefix
165+
std::copy(bytes.data() + 1, bytes.data() + payload_size + 1, to_encode.data());
166+
// Append Blake2b checksum
167+
Data bytes_vec;
168+
bytes_vec.assign(std::begin(bytes), std::end(bytes));
169+
Data sum = Hash::blake2b(bytes_vec, checksum_size);
170+
assert(sum.size() == checksum_size);
171+
std::copy(sum.begin(), sum.end(), to_encode.data() + payload_size);
172+
s.append(Base32::encode(to_encode, BASE32_ALPHABET_FILECOIN));
173+
174+
return s;
175+
}

0 commit comments

Comments
 (0)