mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 04:22:55 +01:00
Partial merge #8499: Add several policy limits and disable uncompressed keys for segwit scripts
67d6ee1
remove redundant tests in p2p-segwit.py (Johnson Lau)9260085
test segwit uncompressed key fixes (Johnson Lau)248f3a7
Fix ismine and addwitnessaddress: no uncompressed keys in segwit (Pieter Wuille)b811124
[qa] Add tests for uncompressed pubkeys in segwit (Suhas Daftuar)9f0397a
Make test framework produce lowS signatures (Johnson Lau)4c0c25a
Require compressed keys in segwit as policy and disable signing with uncompressed keys for segwit scripts (Johnson Lau)3ade2f6
Add standard limits for P2WSH with tests (Johnson Lau)
This commit is contained in:
parent
ead46a6236
commit
4ae641ca18
@ -76,8 +76,20 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Non-canonical public key: neither compressed nor uncompressed
|
||||
return false;
|
||||
// Non-canonical public key: neither compressed nor uncompressed
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool static IsCompressedPubKey(const valtype &vchPubKey) {
|
||||
if (vchPubKey.size() != 33) {
|
||||
// Non-canonical public key: invalid length for compressed key
|
||||
return false;
|
||||
}
|
||||
if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) {
|
||||
// Non-canonical public key: invalid prefix for compressed key
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -202,8 +214,8 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
|
||||
return true;
|
||||
}
|
||||
|
||||
bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
|
||||
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) {
|
||||
bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) {
|
||||
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) {
|
||||
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
|
||||
}
|
||||
return true;
|
||||
@ -967,7 +979,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
||||
FindAndDelete(scriptCode, CScript(vchSig));
|
||||
}
|
||||
|
||||
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
|
||||
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
|
||||
//serror is set
|
||||
return false;
|
||||
}
|
||||
@ -1000,7 +1012,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
||||
valtype &vchMessage = stacktop(-2);
|
||||
valtype &vchPubKey = stacktop(-1);
|
||||
|
||||
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
|
||||
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
|
||||
// serror is set
|
||||
return false;
|
||||
}
|
||||
@ -1084,7 +1096,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
||||
// Note how this makes the exact order of pubkey/signature evaluation
|
||||
// distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
|
||||
// See the script_(in)valid tests for details.
|
||||
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) {
|
||||
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
|
||||
// serror is set
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
||||
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -22,13 +22,25 @@ static bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keyst
|
||||
return true;
|
||||
}
|
||||
|
||||
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest)
|
||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion)
|
||||
{
|
||||
CScript script = GetScriptForDestination(dest);
|
||||
return IsMine(keystore, script);
|
||||
bool isInvalid = false;
|
||||
return IsMine(keystore, scriptPubKey, isInvalid, sigversion);
|
||||
}
|
||||
|
||||
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
||||
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion)
|
||||
{
|
||||
bool isInvalid = false;
|
||||
return IsMine(keystore, dest, isInvalid, sigversion);
|
||||
}
|
||||
|
||||
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion)
|
||||
{
|
||||
CScript script = GetScriptForDestination(dest);
|
||||
return IsMine(keystore, script, isInvalid, sigversion);
|
||||
}
|
||||
|
||||
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion)
|
||||
{
|
||||
std::vector<valtype> vSolutions;
|
||||
txnouttype whichType;
|
||||
@ -46,11 +58,22 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
||||
break;
|
||||
case TX_PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
if (sigversion != SigVersion::BASE && vSolutions[0].size() != 33) {
|
||||
isInvalid = true;
|
||||
return ISMINE_NO;
|
||||
}
|
||||
if (keystore.HaveKey(keyID))
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
case TX_PUBKEYHASH:
|
||||
keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (sigversion != SigVersion::BASE) {
|
||||
CPubKey pubkey;
|
||||
if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
|
||||
isInvalid = true;
|
||||
return ISMINE_NO;
|
||||
}
|
||||
}
|
||||
if (keystore.HaveKey(keyID))
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
@ -59,8 +82,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
||||
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
|
||||
CScript subscript;
|
||||
if (keystore.GetCScript(scriptID, subscript)) {
|
||||
isminetype ret = IsMine(keystore, subscript);
|
||||
if (ret == ISMINE_SPENDABLE)
|
||||
isminetype ret = IsMine(keystore, subscript, isInvalid);
|
||||
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
@ -73,6 +96,14 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
||||
// them) enable spend-out-from-under-you attacks, especially
|
||||
// in shared-wallet situations.
|
||||
std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
|
||||
if (sigversion != SigVersion::BASE) {
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
if (keys[i].size() != 33) {
|
||||
isInvalid = true;
|
||||
return ISMINE_NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (HaveKeys(keys, keystore))
|
||||
return ISMINE_SPENDABLE;
|
||||
break;
|
||||
|
@ -28,7 +28,14 @@ enum isminetype
|
||||
/** used for bitflags of isminetype */
|
||||
typedef uint8_t isminefilter;
|
||||
|
||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
|
||||
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
|
||||
/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
|
||||
* and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
|
||||
* different SIGVERSION may have different network rules. Currently there is no use of isInvalid but it could be
|
||||
* used in the future. See https://github.com/bitcoin/bitcoin/pull/8499 (segwit policy limits) as an example.
|
||||
*/
|
||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion = SigVersion::BASE);
|
||||
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SigVersion::BASE);
|
||||
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SigVersion::BASE);
|
||||
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SigVersion::BASE);
|
||||
|
||||
#endif // BITCOIN_SCRIPT_ISMINE_H
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
||||
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -12,12 +12,6 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
static isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid)
|
||||
{
|
||||
isInvalid = false;
|
||||
return IsMine(keystore, scriptPubKey);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2011-2015 The Bitcoin Core developers
|
||||
// Copyright (c) 2011-2016 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
64
test/functional/test_framework/address.py
Normal file
64
test/functional/test_framework/address.py
Normal file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# address.py
|
||||
#
|
||||
# This file encodes and decodes BASE58 P2PKH and P2SH addresses
|
||||
#
|
||||
|
||||
from .script import hash256, hash160, CScript
|
||||
from .util import bytes_to_hex_str, hex_str_to_bytes
|
||||
|
||||
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
|
||||
def byte_to_base58(b, version):
|
||||
result = ''
|
||||
str = bytes_to_hex_str(b)
|
||||
str = bytes_to_hex_str(chr(version).encode('latin-1')) + str
|
||||
checksum = bytes_to_hex_str(hash256(hex_str_to_bytes(str)))
|
||||
str += checksum[:8]
|
||||
value = int('0x'+str,0)
|
||||
while value > 0:
|
||||
result = chars[value % 58] + result
|
||||
value //= 58
|
||||
while (str[:2] == '00'):
|
||||
result = chars[0] + result
|
||||
str = str[2:]
|
||||
return result
|
||||
|
||||
# TODO: def base58_decode
|
||||
|
||||
def keyhash_to_p2pkh(hash, main = False):
|
||||
assert (len(hash) == 20)
|
||||
version = 76 if main else 140
|
||||
return byte_to_base58(hash, version)
|
||||
|
||||
def scripthash_to_p2sh(hash, main = False):
|
||||
assert (len(hash) == 20)
|
||||
version = 16 if main else 19
|
||||
return byte_to_base58(hash, version)
|
||||
|
||||
def key_to_p2pkh(key, main = False):
|
||||
key = check_key(key)
|
||||
return keyhash_to_p2pkh(hash160(key), main)
|
||||
|
||||
def script_to_p2sh(script, main = False):
|
||||
script = check_script(script)
|
||||
return scripthash_to_p2sh(hash160(script), main)
|
||||
|
||||
def check_key(key):
|
||||
if (type(key) is str):
|
||||
key = hex_str_to_bytes(key) # Assuming this is hex string
|
||||
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
|
||||
return key
|
||||
assert(False)
|
||||
|
||||
def check_script(script):
|
||||
if (type(script) is str):
|
||||
script = hex_str_to_bytes(script) # Assuming this is hex string
|
||||
if (type(script) is bytes or type(script) is CScript):
|
||||
return script
|
||||
assert(False)
|
@ -70,6 +70,9 @@ ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p,
|
||||
# this specifies the curve used with ECDSA.
|
||||
NID_secp256k1 = 714 # from openssl/obj_mac.h
|
||||
|
||||
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
||||
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
|
||||
|
||||
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
|
||||
def _check_result(val, func, args):
|
||||
if val == 0:
|
||||
@ -142,7 +145,7 @@ class CECKey():
|
||||
r = self.get_raw_ecdh_key(other_pubkey)
|
||||
return kdf(r)
|
||||
|
||||
def sign(self, hash):
|
||||
def sign(self, hash, low_s = True):
|
||||
# FIXME: need unit tests for below cases
|
||||
if not isinstance(hash, bytes):
|
||||
raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
|
||||
@ -154,7 +157,25 @@ class CECKey():
|
||||
mb_sig = ctypes.create_string_buffer(sig_size0.value)
|
||||
result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
|
||||
assert 1 == result
|
||||
return mb_sig.raw[:sig_size0.value]
|
||||
assert mb_sig.raw[0] == 0x30
|
||||
assert mb_sig.raw[1] == sig_size0.value - 2
|
||||
total_size = mb_sig.raw[1]
|
||||
assert mb_sig.raw[2] == 2
|
||||
r_size = mb_sig.raw[3]
|
||||
assert mb_sig.raw[4 + r_size] == 2
|
||||
s_size = mb_sig.raw[5 + r_size]
|
||||
s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
|
||||
if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
|
||||
return mb_sig.raw[:sig_size0.value]
|
||||
else:
|
||||
low_s_value = SECP256K1_ORDER - s_value
|
||||
low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
|
||||
while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
|
||||
low_s_bytes = low_s_bytes[1:]
|
||||
new_s_size = len(low_s_bytes)
|
||||
new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
|
||||
new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
|
||||
return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
|
||||
|
||||
def verify(self, hash, sig):
|
||||
"""Verify a DER signature"""
|
||||
|
Loading…
Reference in New Issue
Block a user