diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -304,6 +304,7 @@ pubkey.cpp \ pubkey.h \ script/bitcoinconsensus.cpp \ + script/sighashtype.h \ script/interpreter.cpp \ script/interpreter.h \ script/script.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -116,6 +116,7 @@ test/script_antireplay_tests.cpp \ test/script_P2SH_tests.cpp \ test/script_tests.cpp \ + test/script_sighashtype_tests.cpp \ test/scriptflags.cpp \ test/scriptflags.h \ test/scriptnum_tests.cpp \ diff --git a/src/script/sighashtype.h b/src/script/sighashtype.h new file mode 100644 --- /dev/null +++ b/src/script/sighashtype.h @@ -0,0 +1,71 @@ +// Copyright (c) 2017 Bitcoin ABC developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SCRIPT_HASH_TYPE_H +#define BITCOIN_SCRIPT_HASH_TYPE_H + +#include +#include + +/** Signature hash types/flags */ +enum { + SIGHASH_ALL = 1, + SIGHASH_NONE = 2, + SIGHASH_SINGLE = 3, + SIGHASH_FORKID = 0x40, + SIGHASH_ANYONECANPAY = 0x80, +}; + +/** Base signature hash types */ +enum class BaseSigHashType : uint32_t { + ALL = SIGHASH_ALL, + NONE = SIGHASH_NONE, + SINGLE = SIGHASH_SINGLE +}; + +/** Signature hash type wrapper class */ +class SigHashType { +private: + uint32_t sigHash; + +public: + explicit SigHashType() : sigHash(SIGHASH_ALL) {} + + explicit SigHashType(uint32_t sigHashIn) : sigHash(sigHashIn) { + if (((sigHash & 0x1f) < SIGHASH_ALL) || + ((sigHash & 0x1f) > SIGHASH_SINGLE)) { + throw std::runtime_error("Base sighash must be specified"); + } + } + + SigHashType withBaseSigHash(BaseSigHashType baseSigHashType) const { + return SigHashType((sigHash & ~0x1f) | uint32_t(baseSigHashType)); + } + + SigHashType withForkId(bool forkId) const { + return SigHashType((sigHash & ~SIGHASH_FORKID) | + (forkId ? SIGHASH_FORKID : 0)); + } + + SigHashType withAnyoneCanPay(bool anyoneCanPay) const { + return SigHashType((sigHash & ~SIGHASH_ANYONECANPAY) | + (anyoneCanPay ? SIGHASH_ANYONECANPAY : 0)); + } + + BaseSigHashType getBaseSigHashType() const { + return BaseSigHashType(sigHash & 0x1f); + } + + bool hasForkId() const { + return (sigHash & SIGHASH_FORKID) == SIGHASH_FORKID; + } + + bool hasAnyoneCanPay() const { + return (sigHash & SIGHASH_ANYONECANPAY) == SIGHASH_ANYONECANPAY; + } + + uint32_t getRawSigHashType() const { return sigHash; } +}; + +#endif // BITCOIN_SCRIPT_HASH_TYPE_H diff --git a/src/test/script_sighashtype_tests.cpp b/src/test/script_sighashtype_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/script_sighashtype_tests.cpp @@ -0,0 +1,121 @@ +// 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. + +#include "script/sighashtype.h" +#include "test/test_bitcoin.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(script_sighashtype_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(SigHashTypeTests) { + BOOST_CHECK(SigHashType().getBaseSigHashType() == BaseSigHashType::ALL); + + BOOST_CHECK(SigHashType(SIGHASH_ALL).getBaseSigHashType() == + BaseSigHashType::ALL); + + BOOST_CHECK(SigHashType(SIGHASH_NONE).getBaseSigHashType() == + BaseSigHashType::NONE); + + BOOST_CHECK(SigHashType(SIGHASH_SINGLE).getBaseSigHashType() == + BaseSigHashType::SINGLE); + + BOOST_CHECK_EQUAL(SigHashType(SIGHASH_ALL | SIGHASH_FORKID).hasForkId(), + true); + BOOST_CHECK_EQUAL( + SigHashType(SIGHASH_ALL | SIGHASH_FORKID).hasAnyoneCanPay(), false); + + BOOST_CHECK_EQUAL( + SigHashType(SIGHASH_ALL | SIGHASH_ANYONECANPAY).hasForkId(), false); + BOOST_CHECK_EQUAL( + SigHashType(SIGHASH_ALL | SIGHASH_ANYONECANPAY).hasAnyoneCanPay(), + true); + + BOOST_CHECK(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .getBaseSigHashType() == BaseSigHashType::ALL); + BOOST_CHECK(SigHashType() + .withBaseSigHash(BaseSigHashType::NONE) + .getBaseSigHashType() == BaseSigHashType::NONE); + BOOST_CHECK(SigHashType() + .withBaseSigHash(BaseSigHashType::SINGLE) + .getBaseSigHashType() == BaseSigHashType::SINGLE); + BOOST_CHECK_EQUAL(SigHashType().withForkId(true).hasForkId(), true); + BOOST_CHECK_EQUAL(SigHashType().withAnyoneCanPay(true).hasAnyoneCanPay(), + true); + + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withForkId(true) + .getRawSigHashType(), + SIGHASH_ALL | SIGHASH_FORKID); + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::NONE) + .withForkId(true) + .getRawSigHashType(), + SIGHASH_NONE | SIGHASH_FORKID); + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::SINGLE) + .withForkId(true) + .getRawSigHashType(), + SIGHASH_SINGLE | SIGHASH_FORKID); + + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withAnyoneCanPay(true) + .getRawSigHashType(), + SIGHASH_ALL | SIGHASH_ANYONECANPAY); + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::NONE) + .withAnyoneCanPay(true) + .getRawSigHashType(), + SIGHASH_NONE | SIGHASH_ANYONECANPAY); + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::SINGLE) + .withAnyoneCanPay(true) + .getRawSigHashType(), + SIGHASH_SINGLE | SIGHASH_ANYONECANPAY); + + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withAnyoneCanPay(true) + .withForkId(true) + .getRawSigHashType(), + SIGHASH_ALL | SIGHASH_ANYONECANPAY | SIGHASH_FORKID); + + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withForkId(true) + .withForkId(false) + .hasForkId(), + false); + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withForkId(false) + .withForkId(true) + .hasForkId(), + true); + + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withAnyoneCanPay(true) + .withAnyoneCanPay(false) + .hasAnyoneCanPay(), + false); + BOOST_CHECK_EQUAL(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withAnyoneCanPay(false) + .withAnyoneCanPay(true) + .hasAnyoneCanPay(), + true); + + BOOST_CHECK(SigHashType() + .withBaseSigHash(BaseSigHashType::ALL) + .withAnyoneCanPay(true) + .withForkId(true) + .withBaseSigHash(BaseSigHashType::NONE) + .getBaseSigHashType() == BaseSigHashType::NONE); +} + +BOOST_AUTO_TEST_SUITE_END()