Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ERC55 address support #2164

Merged
merged 25 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1c30b91
Import Eth address with WIF key
Bushstar Jul 7, 2023
392656d
Refactor Eth key support
Bushstar Jul 10, 2023
5311c35
Merge branch 'master' into import-eth-addr
prasannavl Jul 11, 2023
5fa6c0b
Decode Eth address secret in DecodeSecret
Bushstar Jul 11, 2023
4d7e465
lint: remove unused var
Bushstar Jul 11, 2023
447c485
Store key metadata for all keys
Bushstar Jul 12, 2023
5f0c556
Add all keys to mapCryptedKeys. Skip existing pubkeys.
Bushstar Jul 12, 2023
a214748
Add key metadata and address book entries for alternative addresses
Bushstar Jul 14, 2023
b5eea22
Updates for wallet labels test
Bushstar Jul 18, 2023
e3a6398
Merge branch 'master' into import-eth-addr
Bushstar Jul 19, 2023
c3eb800
Switch to bech32. Skip multisig address.
Bushstar Jul 19, 2023
7bed645
Exclude ERC55 from listreceivedbyaddress.
Bushstar Jul 19, 2023
efad109
Only copy 20 bytes for hash160
Bushstar Jul 19, 2023
39879c7
Simplify code to get different public key types
Bushstar Jul 19, 2023
4ed8618
More code simplifying
Bushstar Jul 19, 2023
beb28f6
Update setaddressbook to use pubkey helper function
Bushstar Jul 19, 2023
c1021ee
Merge branch 'master' into import-eth-addr
prasannavl Jul 19, 2023
f85b458
Skip IsMine test for uncompressed witness address
Bushstar Jul 20, 2023
6cf3259
Merge branch 'master' into import-eth-addr
prasannavl Jul 24, 2023
b126ec2
Merge branch 'master' into import-eth-addr
prasannavl Jul 26, 2023
37cdcc9
Merge branch 'master' into import-eth-addr
Bushstar Jul 27, 2023
1fd8c37
Use addressmap instead of vmmap
Bushstar Jul 27, 2023
2ad704a
Remove maxFeePerGas result
Bushstar Jul 27, 2023
cb65926
Merge branch 'master' into import-eth-addr
prasannavl Jul 27, 2023
26ae5b4
Resolve lint error
Bushstar Jul 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static bool IsToKeyID(const CScript& script, CKeyID &hash)
if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160
&& script[2] == 20 && script[23] == OP_EQUALVERIFY
&& script[24] == OP_CHECKSIG) {
memcpy(&hash, &script[3], 20);
memcpy(hash.begin(), &script[3], 20);
prasannavl marked this conversation as resolved.
Show resolved Hide resolved
return true;
}
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/ffi/ffiexports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ std::array<uint8_t, 32> getEthPrivKey(std::array<uint8_t, 20> keyID) {
CKey ethPrivKey;
const auto ethKeyID = CKeyID{uint160{std::vector<uint8_t>(keyID.begin(), keyID.end())}};
for (const auto &wallet: GetWallets()) {
if (wallet->GetEthKey(ethKeyID, ethPrivKey)) {
if (wallet->GetKey(ethKeyID, ethPrivKey)) {
std::array<uint8_t, 32> privKeyArray{};
std::copy(ethPrivKey.begin(), ethPrivKey.end(), privKeyArray.begin());
return privKeyArray;
Expand Down
4 changes: 2 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2323,8 +2323,8 @@ bool AppInitMain(InitInterfaces& interfaces)
const auto time{std::time(nullptr)};

auto pwallet = GetWallets()[0];
pwallet->SetAddressBook(dest, "", "receive");
pwallet->ImportPrivKeys({{keyID, {key, false}}}, time);
pwallet->SetAddressBook(dest, "receive", "receive");
pwallet->ImportPrivKeys({{keyID, key}}, time);

// Create masternode
CMasternode node;
Expand Down
8 changes: 4 additions & 4 deletions src/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ bool CKey::Load(const CPrivKey &privkey, const CPubKey &vchPubKey, bool fSkipChe
return VerifyPubKey(vchPubKey);
}

bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, const bool ethAddress) const {
bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const {
assert(IsValid());
assert(IsCompressed());
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
Expand All @@ -285,17 +285,17 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
memcpy(ccChild.begin(), vout.data()+32, 32);
memcpy((unsigned char*)keyChild.begin(), begin(), 32);
bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
keyChild.fCompressed = !ethAddress;
keyChild.fCompressed = true;
keyChild.fValid = ret;
return ret;
}

bool CExtKey::Derive(CExtKey &out, unsigned int _nChild, const bool ethAddress) const {
bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = key.GetPubKey().GetID();
memcpy(&out.vchFingerprint[0], &id, 4);
out.nChild = _nChild;
return key.Derive(out.key, out.chaincode, _nChild, chaincode, ethAddress);
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
}

void CExtKey::SetSeed(const unsigned char *seed, unsigned int nSeedLen) {
Expand Down
4 changes: 2 additions & 2 deletions src/key.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class CKey
bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig) const;

//! Derive BIP32 child key.
bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, const bool ethAddress = false) const;
bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;

/**
* Verify thoroughly whether a private key and a public key match.
Expand Down Expand Up @@ -159,7 +159,7 @@ struct CExtKey {

void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtKey& out, unsigned int nChild, const bool ethAddress = false) const;
bool Derive(CExtKey& out, unsigned int nChild) const;
CExtPubKey Neuter() const;
void SetSeed(const unsigned char* seed, unsigned int nSeedLen);
template <typename Stream>
Expand Down
5 changes: 4 additions & 1 deletion src/key_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ CKey DecodeSecret(const std::string& str)
{
CKey key;
std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) {
if (IsHex(str)) {
const auto vch = ParseHex(str);
key.Set(vch.begin(), vch.end(), false);
} else if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY);
if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) {
Expand Down
8 changes: 5 additions & 3 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3983,9 +3983,11 @@ Res HasAuth(const CTransaction &tx, const CCoinsViewCache &coins, const CScript
if (solution == txnouttype::TX_PUBKEYHASH) {
auto it = input.scriptSig.begin();
CPubKey pubkey(input.scriptSig.begin() + *it + 2, input.scriptSig.end());
auto script = GetScriptForDestination(WitnessV16EthHash(pubkey));
if (script == auth)
return Res::Ok();
if (pubkey.Decompress()) {
auto script = GetScriptForDestination(WitnessV16EthHash(pubkey));
if (script == auth)
return Res::Ok();
}
} else if (solution == txnouttype::TX_WITNESS_V0_KEYHASH) {
CPubKey pubkey(input.scriptWitness.stack[1]);
if (pubkey.Decompress()) {
Expand Down
2 changes: 1 addition & 1 deletion src/masternodes/rpc_accounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ UniValue gettokenbalances(const JSONRPCRequest& request) {
});

if (eth_lookup) {
for (const auto keyID : pwallet->GetEthKeys()) {
for (const auto keyID : pwallet->GetKeys()) {
std::array<uint8_t, 20> address{};
std::copy(keyID.begin(), keyID.end(), address.begin());
const auto evmAmount = evm_get_balance(address);
Expand Down
24 changes: 14 additions & 10 deletions src/outputtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,26 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
return witdest;
}
}
case OutputType::ETH: return WitnessV16EthHash(key);
case OutputType::ETH: {
CPubKey pubkeyCopy = key;
if (pubkeyCopy.IsCompressed()) {
pubkeyCopy.Decompress();
}
return WitnessV16EthHash(pubkeyCopy);
}
default: assert(false);
}
}

std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
{
PKHash keyid(key);
if (key.IsCompressed()) {
CTxDestination segwit = WitnessV0KeyHash(keyid);
CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit));
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
} else {
CTxDestination eth = WitnessV16EthHash(key);
return std::vector<CTxDestination>{std::move(keyid), std::move(eth)};
}
auto [uncomp, comp] = GetBothPubkeyCompressions(key);
PKHash keyid(comp);
WitnessV0KeyHash segwit(comp);
ScriptHash p2sh(GetScriptForDestination(segwit));
PKHash uncompressedLegacy(uncomp);
WitnessV16EthHash eth(uncomp);
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit), std::move(eth), std::move(uncompressedLegacy)};
}

CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType type)
Expand Down
20 changes: 20 additions & 0 deletions src/pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,21 @@

const unsigned int BIP32_EXTKEY_SIZE = 74;

enum class KeyAddressType : uint8_t {
DEFAULT, // Uses whatever is set in the pub / priv key
COMPRESSED,
UNCOMPRESSED
};

/** A reference to a CKey: the Hash160 of its serialized public key */
class CKeyID : public uint160
{
public:
CKeyID() : uint160() {}
explicit CKeyID(const uint160& in) : uint160(in) {}
CKeyID(const uint160& in, const KeyAddressType type) : uint160(in), type(type) {}

KeyAddressType type{KeyAddressType::DEFAULT};

static std::optional<CKeyID> TryFromDestination(const CTxDestination &dest, KeyType filter=KeyType::UnknownKeyType) {
// Explore switching TxDestType to a flag type. Then, we can easily take an allowed
Expand Down Expand Up @@ -253,6 +262,17 @@ class CPubKey
static bool TryRecoverSigCompat(const std::vector<unsigned char>& vchSig, std::vector<unsigned char>* sig = nullptr);
};

inline std::pair<CPubKey, CPubKey> GetBothPubkeyCompressions(const CPubKey &key) {
auto keyCopy = key;
if (key.IsCompressed()) {
keyCopy.Decompress();
return {keyCopy, key};
} else {
keyCopy.Compress();
}
return {key, keyCopy};
}

struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];
Expand Down
19 changes: 9 additions & 10 deletions src/script/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,8 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid
if (!provider.GetKey(address, key))
return false;

// Special case. Bech32 address created from Eth address which has an uncompressed private key.
CKey ethKey;
const auto ethID = key.GetPubKey().GetEthID();
const auto bechFromEth = !key.IsCompressed() && ethID != address && provider.GetEthKey(ethID, ethKey);

// Signing with uncompressed keys is disabled in witness scripts
if (!bechFromEth && sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
// Signing with compressed keys is disabled in eth scripts
if (sigversion == SigVersion::WITNESS_V16 && key.IsCompressed())
if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())
return false;
uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
if (!key.Sign(hash, vchSig))
Expand Down Expand Up @@ -124,7 +116,14 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
ret.push_back(std::move(sig));
return true;
case TX_PUBKEYHASH: {
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
CKeyID keyID;
if (sigversion == SigVersion::WITNESS_V16) {
keyID = CKeyID(uint160(vSolutions[0]), KeyAddressType::UNCOMPRESSED);
} else if (sigversion == SigVersion::WITNESS_V0) {
keyID = CKeyID(uint160(vSolutions[0]), KeyAddressType::COMPRESSED);
} else {
keyID = CKeyID(uint160(vSolutions[0]));
}
CPubKey pubkey;
if (!GetPubKey(provider, sigdata, keyID, pubkey)) {
// Pubkey could not be found, add to missing
Expand Down
75 changes: 22 additions & 53 deletions src/script/signingprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
return ret;
}

void FillableSigningProvider::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey, const CPubKey &compressedPubKey)
void FillableSigningProvider::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
{
AssertLockHeld(cs_KeyStore);
CKeyID key_id = pubkey.GetID();
// This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH
// outputs. Technically P2WPKH outputs don't have a redeemscript to be
// spent. However, our current IsMine logic requires the corresponding
Expand All @@ -84,19 +83,13 @@ void FillableSigningProvider::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pu
// "Implicitly" refers to fact that scripts are derived automatically from
// existing keys, and are present in memory, even without being explicitly
// loaded (e.g. from a file).
if (compressedPubKey.IsValid()) {
auto script = GetScriptForDestination(WitnessV16EthHash(pubkey));
auto witScript = GetScriptForDestination(WitnessV0KeyHash(compressedPubKey.GetID()));
CScriptID id(script);
CScriptID witId(witScript);
mapScripts[id] = std::move(script);
mapScripts[witId] = std::move(witScript);
} else if (pubkey.IsCompressed()) {
CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id));
// This does not use AddCScript, as it may be overridden.
CScriptID id(script);
mapScripts[id] = std::move(script);
}
auto [uncomp, comp] = GetBothPubkeyCompressions(pubkey);
auto script = GetScriptForDestination(WitnessV16EthHash(uncomp));
auto witScript = GetScriptForDestination(WitnessV0KeyHash(comp));
CScriptID id(script);
CScriptID witId(witScript);
mapScripts[id] = std::move(script);
mapScripts[witId] = std::move(witScript);
}

bool FillableSigningProvider::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
Expand All @@ -106,28 +99,25 @@ bool FillableSigningProvider::GetPubKey(const CKeyID &address, CPubKey &vchPubKe
return false;
}
auto pubkey = key.GetPubKey();
CKey ethKey;
const auto ethID = pubkey.GetEthID();
// Special case. Bech32 request from a converted Eth address.
if (!pubkey.IsCompressed() && ethID != address && GetEthKey(ethID, ethKey)) {
if (!pubkey.IsCompressed() && address.type == KeyAddressType::COMPRESSED) {
pubkey.Compress();
vchPubKeyOut = pubkey;
} else {
vchPubKeyOut = pubkey;
} else if (pubkey.IsCompressed() && address.type == KeyAddressType::UNCOMPRESSED) {
pubkey.Decompress();
}
vchPubKeyOut = pubkey;
return true;
}

bool FillableSigningProvider::AddKeyPubKey(const CKey& key, const CPubKey &pubkey, const CPubKey &compressedPubKey)
bool FillableSigningProvider::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
LOCK(cs_KeyStore);
mapKeys[pubkey.GetID()] = key;
if (compressedPubKey.IsValid()) {
mapKeys[pubkey.GetEthID()] = key;
mapEthKeys[pubkey.GetEthID()] = key;
mapKeys[compressedPubKey.GetID()] = key;
}
ImplicitlyLearnRelatedKeyScripts(pubkey, compressedPubKey);

auto [uncomp, comp] = GetBothPubkeyCompressions(pubkey);
mapKeys[uncomp.GetID()] = key;
mapKeys[uncomp.GetEthID()] = key;
mapKeys[comp.GetID()] = key;

ImplicitlyLearnRelatedKeyScripts(pubkey);
return true;
}

Expand All @@ -147,16 +137,6 @@ std::set<CKeyID> FillableSigningProvider::GetKeys() const
return set_address;
}

std::set<CKeyID> FillableSigningProvider::GetEthKeys() const
{
LOCK(cs_KeyStore);
std::set<CKeyID> set_address;
for (const auto& mi : mapEthKeys) {
set_address.insert(mi.first);
}
return set_address;
}

bool FillableSigningProvider::GetKey(const CKeyID &address, CKey &keyOut) const
{
LOCK(cs_KeyStore);
Expand All @@ -168,17 +148,6 @@ bool FillableSigningProvider::GetKey(const CKeyID &address, CKey &keyOut) const
return false;
}

bool FillableSigningProvider::GetEthKey(const CKeyID &address, CKey &keyOut) const
{
LOCK(cs_KeyStore);
auto mi = mapEthKeys.find(address);
if (mi != mapEthKeys.end()) {
keyOut = mi->second;
return true;
}
return false;
}

bool FillableSigningProvider::AddCScript(const CScript& redeemScript)
{
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
Expand Down Expand Up @@ -225,10 +194,10 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination&
return CKeyID(*id);
}
if (auto witness_id = std::get_if<WitnessV0KeyHash>(&dest)) {
return CKeyID(*witness_id);
return {*witness_id, KeyAddressType::COMPRESSED};
}
if (auto witness_id = std::get_if<WitnessV16EthHash>(&dest)) {
return CKeyID(*witness_id);
return {*witness_id, KeyAddressType::UNCOMPRESSED};
}
if (auto script_hash = std::get_if<ScriptHash>(&dest)) {
CScript script;
Expand Down
10 changes: 3 additions & 7 deletions src/script/signingprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class SigningProvider
virtual bool HaveCScript(const CScriptID &scriptid) const { return false; }
virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
virtual bool GetEthKey(const CKeyID &address, CKey &keyOut) const { return false; }
virtual bool HaveKey(const CKeyID &address) const { return false; }
virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
};
Expand Down Expand Up @@ -70,20 +69,17 @@ class FillableSigningProvider : public SigningProvider
using ScriptMap = std::map<CScriptID, CScript>;

KeyMap mapKeys GUARDED_BY(cs_KeyStore);
KeyMap mapEthKeys GUARDED_BY(cs_KeyStore);
ScriptMap mapScripts GUARDED_BY(cs_KeyStore);

void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey, const CPubKey &compressedPubKey = {}) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);

public:
virtual bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey, const CPubKey &compressedPubKey);
virtual bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey(), {}); }
virtual bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
virtual bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
virtual bool HaveKey(const CKeyID &address) const override;
virtual std::set<CKeyID> GetKeys() const;
virtual std::set<CKeyID> GetEthKeys() const;
virtual bool GetKey(const CKeyID &address, CKey &keyOut) const override;
bool GetEthKey(const CKeyID &address, CKey &keyOut) const override;
virtual bool AddCScript(const CScript& redeemScript);
virtual bool HaveCScript(const CScriptID &hash) const override;
virtual std::set<CScriptID> GetCScripts() const;
Expand Down
4 changes: 2 additions & 2 deletions src/spv/spv_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1625,9 +1625,9 @@ UniValue CFakeSpvWrapper::SendBitcoins(CWallet* const pwallet, std::string addre
BRTransactionAddOutput(tx, SATOSHIS, o.script, o.scriptLen);

// Add Bech32 input
TBytes script(2 + sizeof(keyid), OP_0);
TBytes script(2 + 20, OP_0);
script[1] = 0x14;
memcpy(script.data() + 2, keyid.begin(), sizeof(keyid));
memcpy(script.data() + 2, keyid.begin(), 20);
BRTransactionAddInput(tx, toUInt256("1111111111111111111111111111111111111111111111111111111111111111"), 0,
SATOSHIS + 1000, script.data(), script.size(), nullptr, 0, nullptr, 0, TXIN_SEQUENCE);

Expand Down
Loading
Loading