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

Ethereum address support #1951

Merged
merged 6 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,8 @@ crypto_libdefi_crypto_base_a_SOURCES = \
crypto/ripemd160.h \
crypto/sha1.cpp \
crypto/sha1.h \
crypto/sha3.cpp \
crypto/sha3.h \
crypto/sha256.cpp \
crypto/sha256.h \
crypto/sha512.cpp \
Expand Down
3 changes: 3 additions & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ const auto SMART_CONTRACT_DFIP_2201 = "DFIP2201";
const auto SMART_CONTRACT_DFIP_2203 = "DFIP2203";
const auto SMART_CONTRACT_DFIP2206F = "DFIP2206F";

constexpr auto ETH_ADDR_PREFIX = "0x";
constexpr auto ETH_ADDR_LENGTH_INC_PREFIX = 42;

/**
* Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
* @returns a CChainParams* of the chosen chain.
Expand Down
182 changes: 182 additions & 0 deletions src/crypto/sha3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/** libkeccak-tiny
*
* A single-file implementation of SHA-3 and SHAKE.
*
* Implementor: David Leon Gil
* License: CC0, attribution kindly requested. Blame taken too,
* but not liability.
* Source: https://github.com/coruus/keccak-tiny
*/

#include <crypto/sha3.h>
#include <cstdint>
#include <cstring>

#define decshake(bits) \
int shake##bits(uint8_t*, size_t, const uint8_t*, size_t);

#define decsha3(bits) \
int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t);

decshake(128)
decshake(256)
decsha3(224)
decsha3(256)
decsha3(384)
decsha3(512)

/******** The Keccak-f[1600] permutation ********/

/*** Constants. ***/
static const uint8_t rho[24] = \
{ 1, 3, 6, 10, 15, 21,
28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43,
62, 18, 39, 61, 20, 44};
static const uint8_t pi[24] = \
{10, 7, 11, 17, 18, 3,
5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2,
20, 14, 22, 9, 6, 1};
static const uint64_t RC[24] = \
{1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};

/*** Helper macros to unroll the permutation. ***/
#define rol(x, s) (((x) << s) | ((x) >> (64 - s)))
#define REPEAT6(e) e e e e e e
#define REPEAT24(e) REPEAT6(e e e e)
#define REPEAT5(e) e e e e e
#define FOR5(v, s, e) \
v = 0; \
REPEAT5(e; v += s;)

/*** Keccak-f[1600] ***/
static inline void keccakf(void* state) {
uint64_t* a = (uint64_t*)state;
uint64_t b[5] = {0};
uint64_t t = 0;
uint8_t x, y;

for (int i = 0; i < 24; i++) {
// Theta
FOR5(x, 1,
b[x] = 0;
FOR5(y, 5,
b[x] ^= a[x + y]; ))
FOR5(x, 1,
FOR5(y, 5,
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
// Rho and pi
t = a[1];
x = 0;
REPEAT24(b[0] = a[pi[x]];
a[pi[x]] = rol(t, rho[x]);
t = b[0];
x++; )
// Chi
FOR5(y,
5,
FOR5(x, 1,
b[x] = a[y + x];)
FOR5(x, 1,
a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
// Iota
a[0] ^= RC[i];
}
}

/******** The FIPS202-defined functions. ********/

/*** Some helper macros. ***/

#define _(S) do { S } while (0)
#define FOR(i, ST, L, S) \
_(for (size_t i = 0; i < L; i += ST) { S; })
#define mkapply_ds(NAME, S) \
static inline void NAME(uint8_t* dst, \
const uint8_t* src, \
size_t len) { \
FOR(i, 1, len, S); \
}
#define mkapply_sd(NAME, S) \
static inline void NAME(const uint8_t* src, \
uint8_t* dst, \
size_t len) { \
FOR(i, 1, len, S); \
}

mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
mkapply_sd(setout, dst[i] = src[i]) // setout

#define P keccakf
#define Plen 200

// Fold P*F over the full blocks of an input.
#define foldP(I, L, F) \
while (L >= rate) { \
F(a, I, rate); \
P(a); \
I += rate; \
L -= rate; \
}

/** The sponge-based hash construction. **/
static inline int hash(uint8_t* out, size_t outlen,
const uint8_t* in, size_t inlen,
size_t rate, uint8_t delim) {
if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
return -1;
}
uint8_t a[Plen] = {0};
// Absorb input.
foldP(in, inlen, xorin);
// Xor in the DS and pad frame.
a[inlen] ^= delim;
a[rate - 1] ^= 0x80;
// Xor in the last block.
xorin(a, in, inlen);
// Apply P
P(a);
// Squeeze output.
foldP(out, outlen, setout);
setout(a, out, outlen);
memset(a, 0, 200);
return 0;
}

/*** Helper macros to define SHA3 and SHAKE instances. ***/
#define defshake(bits) \
int shake##bits(uint8_t* out, size_t outlen, \
const uint8_t* in, size_t inlen) { \
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \
}
#define defsha3(bits) \
int sha3_##bits(uint8_t* out, size_t outlen, \
const uint8_t* in, size_t inlen) { \
if (outlen > (bits/8)) { \
return -1; \
} \
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \
}

/*** FIPS202 SHAKE VOFs ***/
defshake(128)
defshake(256)

/*** FIPS202 SHA3 FOFs ***/
defsha3(224)
defsha3(256)
defsha3(384)
defsha3(512)

bool sha3(const std::vector<unsigned char> &input, std::vector<unsigned char> &output)
{
output.resize(32);
sha3_256(output.data(), 32, input.data(), input.size());
return true;
}
8 changes: 8 additions & 0 deletions src/crypto/sha3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef DEFI_CRYPTO_SHA3_H
#define DEFI_CRYPTO_SHA3_H

#include <vector>

bool sha3(const std::vector<unsigned char> &input, std::vector<unsigned char> &output);

#endif // DEFI_CRYPTO_SHA3_H
8 changes: 8 additions & 0 deletions src/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <crypto/common.h>
#include <crypto/ripemd160.h>
#include <crypto/sha256.h>
#include <crypto/sha3.h>
#include <prevector.h>
#include <serialize.h>
#include <uint256.h>
Expand Down Expand Up @@ -127,6 +128,13 @@ inline uint160 Hash160(const prevector<N, unsigned char>& vch)
return Hash160(vch.begin(), vch.end());
}

inline uint160 Sha3(const std::vector<unsigned char> &input) {
std::vector<unsigned char> output;
sha3(input, output);
const size_t ADDRESS_OFFSET{12};
return uint160({output.begin() + ADDRESS_OFFSET, output.end()});
}

/** A writer stream (for serialization) that computes a 256-bit hash. */
class CHashWriter
{
Expand Down
2 changes: 1 addition & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2215,7 +2215,7 @@ bool AppInitMain(InitInterfaces& interfaces)

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

// Create masternode
CMasternode node;
Expand Down
10 changes: 5 additions & 5 deletions src/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ 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 {
#include <logging.h>
bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, const bool ethAddress) 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 = true;
keyChild.fCompressed = !ethAddress;
keyChild.fValid = ret;
return ret;
}

bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
bool CExtKey::Derive(CExtKey &out, unsigned int _nChild, const bool ethAddress) 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);
return key.Derive(out.key, out.chaincode, _nChild, chaincode, ethAddress);
}

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 Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, const bool ethAddress = false) 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 Derive(CExtKey& out, unsigned int nChild, const bool ethAddress = false) const;
CExtPubKey Neuter() const;
void SetSeed(const unsigned char* seed, unsigned int nSeedLen);
template <typename Stream>
Expand Down
14 changes: 13 additions & 1 deletion src/key_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include <base58.h>
#include <bech32.h>
#include <chainparams.h>
#include <util/strencodings.h>

#include <assert.h>
#include <string.h>
Expand Down Expand Up @@ -60,6 +59,11 @@ class DestinationEncoder
return bech32::Encode(m_params.Bech32HRP(), data);
}

std::string operator()(const WitnessV16EthHash& id) const
{
return ETH_ADDR_PREFIX + HexStr(id);
}

std::string operator()(const CNoDestination& no) const { return {}; }
};
} // namespace
Expand All @@ -68,6 +72,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
{
std::vector<unsigned char> data;
uint160 hash;
if (str.size() == ETH_ADDR_LENGTH_INC_PREFIX && str.substr(0, 2) == ETH_ADDR_PREFIX) {
const auto hex = str.substr(2);
if (!IsHex(hex)) {
return CNoDestination();
}
data = ParseHex(hex);
return WitnessV16EthHash(uint160(data));
}
if (DecodeBase58Check(str, data)) {
// base58-encoded DFI addresses.
// Public-key-hash-addresses have version 0 (or 111 testnet).
Expand Down
7 changes: 6 additions & 1 deletion src/masternodes/rpc_accounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,16 @@ static BalanceKey decodeBalanceKey(const std::string &str) {
return {hexToScript(pair.first), tokenID};
}

static CAccounts DecodeRecipientsDefaultInternal(CWallet *const pwallet, const UniValue &values) {
static UniValue DecodeRecipientsGetRecipients(const UniValue &values) {
UniValue recipients(UniValue::VOBJ);
for (const auto& key : values.getKeys()) {
recipients.pushKV(key, values[key]);
}
return recipients;
}

static CAccounts DecodeRecipientsDefaultInternal(CWallet *const pwallet, const UniValue &values) {
const auto recipients = DecodeRecipientsGetRecipients(values);
auto accounts = DecodeRecipients(pwallet->chain(), recipients);
for (const auto& account : accounts) {
if (IsMineCached(*pwallet, account.first) != ISMINE_SPENDABLE && account.second.balances.find(DCT_ID{0}) != account.second.balances.end()) {
Expand Down
9 changes: 8 additions & 1 deletion src/outputtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
static const std::string OUTPUT_TYPE_STRING_ETH = "eth";

bool ParseOutputType(const std::string& type, OutputType& output_type)
{
Expand All @@ -29,6 +30,9 @@ bool ParseOutputType(const std::string& type, OutputType& output_type)
} else if (type == OUTPUT_TYPE_STRING_BECH32) {
output_type = OutputType::BECH32;
return true;
} else if (type == OUTPUT_TYPE_STRING_ETH) {
output_type = OutputType::ETH;
return true;
}
return false;
}
Expand All @@ -39,6 +43,7 @@ const std::string& FormatOutputType(OutputType type)
case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
case OutputType::ETH: return OUTPUT_TYPE_STRING_ETH;
default: assert(false);
}
}
Expand All @@ -58,6 +63,7 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
return witdest;
}
}
case OutputType::ETH: return WitnessV16EthHash(key);
default: assert(false);
}
}
Expand All @@ -70,7 +76,8 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit));
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
} else {
return std::vector<CTxDestination>{std::move(keyid)};
CTxDestination eth = WitnessV16EthHash(key);
return std::vector<CTxDestination>{std::move(keyid), std::move(eth)};
}
}

Expand Down
1 change: 1 addition & 0 deletions src/outputtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ enum class OutputType {
LEGACY,
P2SH_SEGWIT,
BECH32,
ETH,

/**
* Special output type for change outputs only. Automatically choose type
Expand Down
Loading