Skip to content
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
4 changes: 2 additions & 2 deletions .github/workflows/komodod_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Install deps (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get remove php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm
sudo apt-get remove php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get update
Expand Down Expand Up @@ -259,7 +259,7 @@ jobs:
DEBIAN_FRONTEND: noninteractive
if: runner.os == 'Linux'
run: |
sudo apt-get remove php5.6-fpm php7.0-fpm php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm
sudo apt-get remove php5.6-fpm php7.0-fpm php7.1-fpm php7.2-fpm php7.3-fpm php7.3-common php7.4-fpm msodbcsql17
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install build-essential pkg-config libc6-dev m4 g++-multilib autoconf libtool libncurses-dev unzip git python zlib1g-dev wget bsdmainutils automake libboost-all-dev libssl-dev libprotobuf-dev protobuf-compiler libqrencode-dev libdb++-dev ntp ntpdate nano software-properties-common curl libevent-dev libcurl4-gnutls-dev cmake clang libsodium-dev -y
Expand Down
12 changes: 12 additions & 0 deletions doc/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,15 @@ release-notes at release time)
Notable changes
===============

Low-level RPC changes
---------------------

- Bare multisig outputs to our keys are no longer automatically treated as
incoming payments. As this feature was only available for multisig outputs for
which you had all private keys in your wallet, there was generally no use for
them compared to single-key schemes. Furthermore, no address format for such
outputs is defined, and wallet software can't easily send to it. These outputs
will no longer show up in `listtransactions`, `listunspent`, or contribute to
your balance, unless they are explicitly watched (using `importaddress` or
`importmulti` with hex script argument). `signrawtransaction*` also still
works for them.
12 changes: 7 additions & 5 deletions qa/pytest_komodo/basic/test_rawtransactions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# Copyright (c) 2019 SuperNET developers
# Copyright (c) 2020 SuperNET developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# file COPYING or https://www.opensource.org/licenses/mit-license.php.

import pytest
from decimal import *
Expand Down Expand Up @@ -32,11 +32,13 @@ def test_rawtransactions(self, test_params): # create, fund, sign, send calls
txid = res[0].get('txid')
vout = res[0].get('vout')
base_amount = res[0].get('amount')
# python float() is double precision floating point number,
# createrawtransaction method expects C float (8 digits) value
# "{0:.8f}".format(value)) returns number string with 8 digit precision and float() corrects the type
if isinstance(base_amount, Decimal):
amount = float(base_amount) * 0.9
print(amount)
amount = float("{0:.8f}".format(float(base_amount) * 0.9))
else:
amount = base_amount * 0.9
amount = float("{0:.8f}".format(base_amount * 0.9))
address = rpc.getnewaddress()
ins = [{'txid': txid, 'vout': vout}]
outs = {address: amount}
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ BITCOIN_TESTS =\
test/scheduler_tests.cpp \
test/script_P2SH_tests.cpp \
test/script_tests.cpp \
test/script_standard_tests.cpp \
test/scriptnum_tests.cpp \
test/serialize_tests.cpp \
test/sighash_tests.cpp \
Expand Down
4 changes: 2 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1267,9 +1267,9 @@ bool ContextualCheckTransaction(int32_t slowflag,const CBlock *block, CBlockInde
if (IsExpiredTx(tx, nHeight)) {
// Don't increase banscore if the transaction only just expired
int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? (dosLevel > 10 ? dosLevel : 10) : 0;
string strHex = EncodeHexTx(tx);
//string strHex = EncodeHexTx(tx);
//fprintf(stderr, "transaction exipred.%s\n",strHex.c_str());
return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction %s is expired, expiry block %i vs current block %i\n txhex.%s",tx.GetHash().ToString(),tx.nExpiryHeight,nHeight,strHex), REJECT_INVALID, "tx-overwinter-expired");
return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction %s is expired, expiry block %i vs current block %i\n",tx.GetHash().ToString(),tx.nExpiryHeight,nHeight), REJECT_INVALID, "tx-overwinter-expired");
}
}

Expand Down
13 changes: 9 additions & 4 deletions src/pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ class CPubKey
/**
* secp256k1:
*/
static const unsigned int PUBLIC_KEY_SIZE = 65;
static const unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
static const unsigned int SIGNATURE_SIZE = 72;
static const unsigned int COMPACT_SIGNATURE_SIZE = 65;
static constexpr unsigned int PUBLIC_KEY_SIZE = 65;
static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
static constexpr unsigned int SIGNATURE_SIZE = 72;
static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
/**
* see www.keylength.com
* script supports up to 75 for single byte push
Expand Down Expand Up @@ -85,6 +85,11 @@ class CPubKey
}

public:

bool static ValidSize(const std::vector<unsigned char> &vch) {
return vch.size() > 0 && GetLen(vch[0]) == vch.size();
}

//! Construct an invalid public key.
CPubKey()
{
Expand Down
22 changes: 17 additions & 5 deletions src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,6 @@ const char* GetOpName(opcodetype opcode)

case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";

// Note:
// The template matching params OP_SMALLDATA/etc are defined in opcodetype enum
// as kind of implementation hack, they are *NOT* real opcodes. If found in real
// Script, just let the default: case deal with them.

default:
return "OP_UNKNOWN";
}
Expand Down Expand Up @@ -419,6 +414,23 @@ bool CScript::IsCoinImport() const
return false;
}

bool CScript::IsPushOnly(const_iterator pc) const
{
while (pc < end())
{
opcodetype opcode;
if (!GetOp(pc, opcode))
return false;
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
// push-type opcode, however execution of OP_RESERVED fails, so
// it's not relevant to P2SH/BIP62 as the scriptSig would fail prior to
// the P2SH special validation code being executed.
if (opcode > OP_16)
return false;
}
return true;
}

bool CScript::IsPushOnly() const
{
const_iterator pc = begin();
Expand Down
5 changes: 1 addition & 4 deletions src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,6 @@ enum opcodetype

// template matching params
OP_SMALLDATA = 0xf9,
OP_SMALLINTEGER = 0xfa,
OP_PUBKEYS = 0xfb,
OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe,
OP_CRYPTOCONDITION = 0xfc,

OP_INVALIDOPCODE = 0xff,
Expand Down Expand Up @@ -603,6 +599,7 @@ class CScript : public CScriptBase
bool MayAcceptCryptoCondition() const;

/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
bool IsPushOnly(const_iterator pc) const;
bool IsPushOnly() const;

/** if the front of the script has check lock time verify. this is a fairly simple check.
Expand Down
175 changes: 75 additions & 100 deletions src/script/standard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,26 +155,57 @@ const char* GetTxnOutputType(txnouttype t)
/**
* Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
*/
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
{
// Templates
static multimap<txnouttype, CScript> mTemplates;
if (mTemplates.empty())
{
// Standard tx, sender provides pubkey, receiver adds signature
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1);
return CPubKey::ValidSize(pubkey);
}
if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1);
return CPubKey::ValidSize(pubkey);
}
return false;
}

// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
{
if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
return true;
}
return false;
}

// Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
static constexpr bool IsSmallInteger(opcodetype opcode)
{
return opcode >= OP_1 && opcode <= OP_16;
}

// Empty, provably prunable, data-carrying output
if (GetBoolArg("-datacarrier", true))
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA));
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN));
static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
{
opcodetype opcode;
valtype data;
CScript::const_iterator it = script.begin();
if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;

if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) return false;
required = CScript::DecodeOP_N(opcode);
while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
pubkeys.emplace_back(std::move(data));
}

if (!IsSmallInteger(opcode)) return false;
unsigned int keys = CScript::DecodeOP_N(opcode);
if (pubkeys.size() != keys || keys < required) return false;
return (it + 1 == script.end());
}


bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
{
vSolutionsRet.clear();

// Shortcut for pay-to-script-hash, which are more constrained than the other types:
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
Expand All @@ -186,6 +217,16 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
return true;
}

// Provably prunable, data-carrying output
//
// So long as script passes the IsUnspendable() test and all but the first
// byte passes the IsPushOnly() test we don't care what exactly is in the
// script.
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
typeRet = TX_NULL_DATA;
return true;
}

if (IsCryptoConditionsEnabled()) {
// Shortcut for pay-to-crypto-condition
CScript ccSubScript = CScript();
Expand Down Expand Up @@ -220,93 +261,27 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
}
}

// Scan templates
const CScript& script1 = scriptPubKey;
BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates)
{
const CScript& script2 = tplate.second;
vSolutionsRet.clear();

opcodetype opcode1, opcode2;
vector<unsigned char> vch1, vch2;

// Compare
CScript::const_iterator pc1 = script1.begin();
CScript::const_iterator pc2 = script2.begin();
while (true)
{
if (pc1 == script1.end() && pc2 == script2.end())
{
// Found a match
typeRet = tplate.first;
if (typeRet == TX_MULTISIG)
{
// Additional checks for TX_MULTISIG:
unsigned char m = vSolutionsRet.front()[0];
unsigned char n = vSolutionsRet.back()[0];
if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
return false;
}
return true;
}
if (!script1.GetOp(pc1, opcode1, vch1))
break;
if (!script2.GetOp(pc2, opcode2, vch2))
break;
std::vector<unsigned char> data;
if (MatchPayToPubkey(scriptPubKey, data)) {
typeRet = TX_PUBKEY;
vSolutionsRet.push_back(std::move(data));
return true;
}

// Template matching opcodes:
if (opcode2 == OP_PUBKEYS)
{
while (vch1.size() >= 33 && vch1.size() <= 65)
{
vSolutionsRet.push_back(vch1);
if (!script1.GetOp(pc1, opcode1, vch1))
break;
}
if (!script2.GetOp(pc2, opcode2, vch2))
break;
// Normal situation is to fall through
// to other if/else statements
}
if (MatchPayToPubkeyHash(scriptPubKey, data)) {
typeRet = TX_PUBKEYHASH;
vSolutionsRet.push_back(std::move(data));
return true;
}

if (opcode2 == OP_PUBKEY)
{
if (vch1.size() < 33 || vch1.size() > 65)
break;
vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_PUBKEYHASH)
{
if (vch1.size() != sizeof(uint160))
break;
vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_SMALLINTEGER)
{ // Single-byte small integer pushed onto vSolutions
if (opcode1 == OP_0 ||
(opcode1 >= OP_1 && opcode1 <= OP_16))
{
char n = (char)CScript::DecodeOP_N(opcode1);
vSolutionsRet.push_back(valtype(1, n));
}
else
break;
}
else if (opcode2 == OP_SMALLDATA)
{
// small pushdata, <= nMaxDatacarrierBytes
if (vch1.size() > nMaxDatacarrierBytes)
{
//fprintf(stderr,"size.%d > nMaxDatacarrier.%d\n",(int32_t)vch1.size(),(int32_t)nMaxDatacarrierBytes);
break;
}
}
else if (opcode1 != opcode2 || vch1 != vch2)
{
// Others must match exactly
break;
}
}
unsigned int required;
std::vector<std::vector<unsigned char>> keys;
if (MatchMultisig(scriptPubKey, required, keys)) {
typeRet = TX_MULTISIG;
vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16
return true;
}

vSolutionsRet.clear();
Expand Down
2 changes: 1 addition & 1 deletion src/script/standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class CScriptID : public uint160
{
public:
CScriptID() : uint160() {}
CScriptID(const CScript& in);
explicit CScriptID(const CScript& in);
CScriptID(const uint160& in) : uint160(in) {}
};

Expand Down
Loading