From cb38e0fc35920e34b2e8451128b3724d590ffd59 Mon Sep 17 00:00:00 2001 From: Peter John Bushnell Date: Wed, 3 May 2023 12:43:54 +0100 Subject: [PATCH] Ethereum address support (#1951) * Add HD Wallet Ethereum address support * Fix failing test * Remove now invalid test types from valid data * Use hex variable in argument to IsHex Co-authored-by: Mihailo Milenkovic * Minor changes. Skip OP_16 in test. --------- Co-authored-by: Mihailo Milenkovic --- src/Makefile.am | 2 + src/chainparams.h | 3 + src/crypto/sha3.cpp | 182 ++++++++++++++++++++++++++++++ src/crypto/sha3.h | 8 ++ src/hash.h | 8 ++ src/init.cpp | 2 +- src/key.cpp | 10 +- src/key.h | 4 +- src/key_io.cpp | 14 ++- src/masternodes/rpc_accounts.cpp | 7 +- src/outputtype.cpp | 9 +- src/outputtype.h | 1 + src/pubkey.h | 6 + src/rpc/blockchain.cpp | 1 + src/rpc/util.cpp | 10 ++ src/script/interpreter.cpp | 30 ++++- src/script/interpreter.h | 2 + src/script/script.cpp | 1 + src/script/script.h | 1 + src/script/script_error.cpp | 2 + src/script/script_error.h | 1 + src/script/sign.cpp | 20 +++- src/script/signingprovider.cpp | 16 ++- src/script/signingprovider.h | 6 +- src/script/standard.cpp | 20 +++- src/script/standard.h | 15 ++- src/test/data/script_tests.json | 1 - src/test/data/tx_valid.json | 10 -- src/test/transaction_tests.cpp | 12 +- src/wallet/ismine.cpp | 24 +++- src/wallet/rpcdump.cpp | 103 ++++++++++++----- src/wallet/rpcwallet.cpp | 11 +- src/wallet/test/wallet_tests.cpp | 8 +- src/wallet/wallet.cpp | 84 ++++++++------ src/wallet/wallet.h | 20 ++-- src/wallet/walletdb.cpp | 20 +++- src/wallet/walletdb.h | 24 +++- test/functional/p2p_segwit.py | 6 +- test/functional/rpc_blockchain.py | 1 + test/functional/wallet_hd.py | 55 +++++++++ 40 files changed, 622 insertions(+), 138 deletions(-) create mode 100644 src/crypto/sha3.cpp create mode 100644 src/crypto/sha3.h diff --git a/src/Makefile.am b/src/Makefile.am index e029f868a3..2af13c4e1e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/chainparams.h b/src/chainparams.h index d462243c8f..a6db21aea2 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -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 of the chosen chain. * @returns a CChainParams* of the chosen chain. diff --git a/src/crypto/sha3.cpp b/src/crypto/sha3.cpp new file mode 100644 index 0000000000..8116de0d61 --- /dev/null +++ b/src/crypto/sha3.cpp @@ -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 +#include +#include + +#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 &input, std::vector &output) +{ + output.resize(32); + sha3_256(output.data(), 32, input.data(), input.size()); + return true; +} diff --git a/src/crypto/sha3.h b/src/crypto/sha3.h new file mode 100644 index 0000000000..1f6db21edd --- /dev/null +++ b/src/crypto/sha3.h @@ -0,0 +1,8 @@ +#ifndef DEFI_CRYPTO_SHA3_H +#define DEFI_CRYPTO_SHA3_H + +#include + +bool sha3(const std::vector &input, std::vector &output); + +#endif // DEFI_CRYPTO_SHA3_H \ No newline at end of file diff --git a/src/hash.h b/src/hash.h index 598a376560..e24d87ef7f 100644 --- a/src/hash.h +++ b/src/hash.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,13 @@ inline uint160 Hash160(const prevector& vch) return Hash160(vch.begin(), vch.end()); } +inline uint160 Sha3(const std::vector &input) { + std::vector 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 { diff --git a/src/init.cpp b/src/init.cpp index 4f66367125..dd88253e3b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -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; diff --git a/src/key.cpp b/src/key.cpp index 147f23bc72..3e10e6d3f3 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -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 +bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, const bool ethAddress) const { assert(IsValid()); assert(IsCompressed()); std::vector> vout(64); @@ -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) { diff --git a/src/key.h b/src/key.h index 137a2c0c2d..e1dd79993a 100644 --- a/src/key.h +++ b/src/key.h @@ -129,7 +129,7 @@ class CKey bool SignCompact(const uint256& hash, std::vector& 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. @@ -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 diff --git a/src/key_io.cpp b/src/key_io.cpp index 29e5378554..8628d98ad9 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -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 @@ -68,6 +72,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par { std::vector 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). diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index 068e899821..519385a5b0 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -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()) { diff --git a/src/outputtype.cpp b/src/outputtype.cpp index a3154784f5..3829bdb17a 100644 --- a/src/outputtype.cpp +++ b/src/outputtype.cpp @@ -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) { @@ -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; } @@ -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); } } @@ -58,6 +63,7 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type) return witdest; } } + case OutputType::ETH: return WitnessV16EthHash(key); default: assert(false); } } @@ -70,7 +76,8 @@ std::vector GetAllDestinationsForKey(const CPubKey& key) CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit)); return std::vector{std::move(keyid), std::move(p2sh), std::move(segwit)}; } else { - return std::vector{std::move(keyid)}; + CTxDestination eth = WitnessV16EthHash(key); + return std::vector{std::move(keyid), std::move(eth)}; } } diff --git a/src/outputtype.h b/src/outputtype.h index 6fdc457b80..0c0ca36760 100644 --- a/src/outputtype.h +++ b/src/outputtype.h @@ -17,6 +17,7 @@ enum class OutputType { LEGACY, P2SH_SEGWIT, BECH32, + ETH, /** * Special output type for change outputs only. Automatically choose type diff --git a/src/pubkey.h b/src/pubkey.h index f32458c1bc..dfac302d05 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -157,6 +157,12 @@ class CPubKey return CKeyID(Hash160(vch, vch + size())); } + CKeyID GetEthID() const + { + const size_t HEADER_OFFSET{1}; + return CKeyID(Sha3({vch + HEADER_OFFSET, vch + size()})); + } + //! Get the 256-bit hash of this public key. uint256 GetHash() const { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 0276457c59..767b75de4e 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1353,6 +1353,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) BuriedForkDescPushBack(softforks, "fortcanningepilogue", consensusParams.FortCanningEpilogueHeight); BuriedForkDescPushBack(softforks, "grandcentral", consensusParams.GrandCentralHeight); BuriedForkDescPushBack(softforks, "grandcentralepilogue", consensusParams.GrandCentralEpilogueHeight); + BuriedForkDescPushBack(softforks, "nextnetworkupgrade", consensusParams.NextNetworkUpgradeHeight); BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); obj.pushKV("softforks", softforks); diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index cdc863f57c..2bc8a47b3b 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -252,6 +252,16 @@ class DescribeAddressVisitor obj.pushKV("witness_program", HexStr(id.program, id.program + id.length)); return obj; } + + UniValue operator()(const WitnessV16EthHash& id) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", false); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 16); + obj.pushKV("witness_program", HexStr(id.begin(), id.end())); + return obj; + } }; UniValue DescribeAddress(const CTxDestination& dest) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 11b17ad05a..b56db257f6 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -222,6 +222,10 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SigVersion::WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) { return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE); } + // Only uncompressed keys are accepted in Eth addresses + if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SigVersion::WITNESS_V16 && IsCompressedPubKey(vchPubKey)) { + return set_error(serror, SCRIPT_ERR_WITNESS_ETHKEYTYPE); + } return true; } @@ -475,7 +479,7 @@ bool EvalScript(std::vector >& stack, const CScript& if (stack.size() < 1) return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); valtype& vch = stacktop(-1); - if (sigversion == SigVersion::WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) { + if ((sigversion == SigVersion::WITNESS_V0 || sigversion == SigVersion::WITNESS_V16) && (flags & SCRIPT_VERIFY_MINIMALIF)) { if (vch.size() > 1) return set_error(serror, SCRIPT_ERR_MINIMALIF); if (vch.size() == 1 && vch[0] != 1) @@ -877,6 +881,7 @@ bool EvalScript(std::vector >& stack, const CScript& // case OP_RIPEMD160: case OP_SHA1: + case OP_SHA3: case OP_SHA256: case OP_HASH160: case OP_HASH256: @@ -885,12 +890,16 @@ bool EvalScript(std::vector >& stack, const CScript& if (stack.size() < 1) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype& vch = stacktop(-1); - valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); + valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_SHA3 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) CRIPEMD160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_SHA1) CSHA1().Write(vch.data(), vch.size()).Finalize(vchHash.data()); - else if (opcode == OP_SHA256) + else if (opcode == OP_SHA3) { + const size_t HEADER_OFFSET{1}; + const auto result = Sha3({vch.begin() + HEADER_OFFSET, vch.end()}); + memcpy(vchHash.data(), result.begin(), 20); + } else if (opcode == OP_SHA256) CSHA256().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_HASH160) CHash160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); @@ -1235,7 +1244,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn { assert(nIn < txTo.vin.size()); - if (sigversion == SigVersion::WITNESS_V0) { + if (sigversion == SigVersion::WITNESS_V0 || sigversion == SigVersion::WITNESS_V16) { uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; @@ -1444,6 +1453,17 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, } else { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH); } + } else if (witversion == 16) { + if (program.size() == WITNESS_V16_ETHHASH_SIZE) { + // Special case for pay-to-pubkeyhash; signature + pubkey in witness + if (witness.stack.size() != 2) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness + } + scriptPubKey << OP_DUP << OP_SHA3 << program << OP_EQUALVERIFY << OP_CHECKSIG; + stack = witness.stack; + } else { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH); + } } else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); } else { @@ -1457,7 +1477,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, return set_error(serror, SCRIPT_ERR_PUSH_SIZE); } - if (!EvalScript(stack, scriptPubKey, flags, checker, SigVersion::WITNESS_V0, serror)) { + if (!EvalScript(stack, scriptPubKey, flags, checker, witversion == 0 ? SigVersion::WITNESS_V0 : SigVersion::WITNESS_V16, serror)) { return false; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 6ff51e5539..3147b7db63 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -132,11 +132,13 @@ enum class SigVersion { BASE = 0, WITNESS_V0 = 1, + WITNESS_V16 = 2, }; /** Signature hash sizes */ static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32; static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20; +static constexpr size_t WITNESS_V16_ETHHASH_SIZE = 20; template uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); diff --git a/src/script/script.cpp b/src/script/script.cpp index d5a0aab76a..b7e5b57163 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -117,6 +117,7 @@ const char* GetOpName(opcodetype opcode) // crypto case OP_RIPEMD160 : return "OP_RIPEMD160"; case OP_SHA1 : return "OP_SHA1"; + case OP_SHA3 : return "OP_SHA3"; case OP_SHA256 : return "OP_SHA256"; case OP_HASH160 : return "OP_HASH160"; case OP_HASH256 : return "OP_HASH256"; diff --git a/src/script/script.h b/src/script/script.h index 13ee03b722..4846513fab 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -164,6 +164,7 @@ enum opcodetype // crypto OP_RIPEMD160 = 0xa6, OP_SHA1 = 0xa7, + OP_SHA3 = 0xc0, OP_SHA256 = 0xa8, OP_HASH160 = 0xa9, OP_HASH256 = 0xaa, diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index 024a7e2730..46cc87806e 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -89,6 +89,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness provided for non-witness script"; case SCRIPT_ERR_WITNESS_PUBKEYTYPE: return "Using non-compressed keys in segwit"; + case SCRIPT_ERR_WITNESS_ETHKEYTYPE: + return "Using compressed keys in Eth"; case SCRIPT_ERR_OP_CODESEPARATOR: return "Using OP_CODESEPARATOR in non-witness script"; case SCRIPT_ERR_SIG_FINDANDDELETE: diff --git a/src/script/script_error.h b/src/script/script_error.h index 180e8fd42c..4652b63ae1 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -63,6 +63,7 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_MALLEATED_P2SH, SCRIPT_ERR_WITNESS_UNEXPECTED, SCRIPT_ERR_WITNESS_PUBKEYTYPE, + SCRIPT_ERR_WITNESS_ETHKEYTYPE, /* Constant scriptCode */ SCRIPT_ERR_OP_CODESEPARATOR, diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 6e5ba4ef83..084cc2828e 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -11,7 +11,7 @@ #include