Pieter Wuille
5 years ago
commit
40767b27a9
28 changed files with 8194 additions and 0 deletions
@ -0,0 +1,14 @@ |
|||
HEADERS := bitcoin/util/vector.h bitcoin/util/strencodings.h bitcoin/span.h bitcoin/util/spanparsing.h bitcoin/script/script.h bitcoin/script/miniscript.h compiler.h bitcoin/crypto/common.h bitcoin/serialize.h bitcoin/prevector.h bitcoin/compat/endian.h bitcoin/compat/byteswap.h bitcoin/attributes.h bitcoin/tinyformat.h |
|||
SOURCES := bitcoin/util/strencodings.cpp bitcoin/util/spanparsing.cpp bitcoin/script/script.cpp bitcoin/script/miniscript.cpp compiler.cpp |
|||
|
|||
miniscript: $(HEADERS) $(SOURCES) main.cpp |
|||
g++ -O3 -g0 -Wall -march=native -flto -Ibitcoin $(SOURCES) main.cpp -o miniscript |
|||
|
|||
miniscript.js: $(HEADERS) $(SOURCES) js_bindings.cpp |
|||
em++ -O3 -g0 -Wall -std=c++11 -fno-rtti -flto -Ibitcoin $(SOURCES) js_bindings.cpp -s WASM=1 -s FILESYSTEM=0 -s ENVIRONMENT=web -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORTED_FUNCTIONS='["_miniscript_compile","_miniscript_analyze","_malloc","_free"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap","UTF8ToString"]' -o miniscript.js |
|||
|
|||
wrapper.dot: wrapper.txt |
|||
(echo "digraph wrapper {"; cat wrapper.txt | sed -e 's/^ \+//g' | sed -e 's/ \+/ /g' | cut -d ' ' -f 2 | rev | sed -e 's/l/u/g' | sed -e 's/s/a/g' | sort | uniq | sed -e 's/\([a-z]\)/\1,\1/g' | sed -e 's/^[a-z]//g' | sed -e 's/,[a-z]$$//g' | sed -e 's/,\(.\)\(.\)/ \1 -> \2;\n/g' | sort | uniq | sed -e 's/u/"l\/u"/g' | sed -e 's/a/\"a\/s\"/g'; echo "}") >wrapper.dot |
|||
|
|||
wrapper.pdf: wrapper.dot |
|||
dot -Tpdf <wrapper.dot >wrapper.pdf |
@ -0,0 +1,22 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_ATTRIBUTES_H |
|||
#define BITCOIN_ATTRIBUTES_H |
|||
|
|||
#if defined(__has_cpp_attribute) |
|||
# if __has_cpp_attribute(nodiscard) |
|||
# define NODISCARD [[nodiscard]] |
|||
# endif |
|||
#endif |
|||
#ifndef NODISCARD |
|||
# if defined(_MSC_VER) && _MSC_VER >= 1700 |
|||
# define NODISCARD _Check_return_ |
|||
# else |
|||
# define NODISCARD __attribute__((warn_unused_result)) |
|||
# endif |
|||
#endif |
|||
|
|||
#endif // BITCOIN_ATTRIBUTES_H
|
@ -0,0 +1,66 @@ |
|||
// Copyright (c) 2014-2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_COMPAT_BYTESWAP_H |
|||
#define BITCOIN_COMPAT_BYTESWAP_H |
|||
|
|||
#if defined(HAVE_CONFIG_H) |
|||
#include <config/bitcoin-config.h> |
|||
#endif |
|||
|
|||
#include <stdint.h> |
|||
|
|||
#if defined(HAVE_BYTESWAP_H) |
|||
#include <byteswap.h> |
|||
#endif |
|||
|
|||
#if defined(MAC_OSX) |
|||
|
|||
#if !defined(bswap_16) |
|||
|
|||
// Mac OS X / Darwin features; we include a check for bswap_16 because if it is already defined, protobuf has
|
|||
// defined these macros for us already; if it isn't, we do it ourselves. In either case, we get the exact same
|
|||
// result regardless which path was taken
|
|||
#include <libkern/OSByteOrder.h> |
|||
#define bswap_16(x) OSSwapInt16(x) |
|||
#define bswap_32(x) OSSwapInt32(x) |
|||
#define bswap_64(x) OSSwapInt64(x) |
|||
|
|||
#endif // !defined(bswap_16)
|
|||
|
|||
#else |
|||
// Non-Mac OS X / non-Darwin
|
|||
|
|||
#if HAVE_DECL_BSWAP_16 == 0 |
|||
inline uint16_t bswap_16(uint16_t x) |
|||
{ |
|||
return (x >> 8) | (x << 8); |
|||
} |
|||
#endif // HAVE_DECL_BSWAP16 == 0
|
|||
|
|||
#if HAVE_DECL_BSWAP_32 == 0 |
|||
inline uint32_t bswap_32(uint32_t x) |
|||
{ |
|||
return (((x & 0xff000000U) >> 24) | ((x & 0x00ff0000U) >> 8) | |
|||
((x & 0x0000ff00U) << 8) | ((x & 0x000000ffU) << 24)); |
|||
} |
|||
#endif // HAVE_DECL_BSWAP32 == 0
|
|||
|
|||
#if HAVE_DECL_BSWAP_64 == 0 |
|||
inline uint64_t bswap_64(uint64_t x) |
|||
{ |
|||
return (((x & 0xff00000000000000ull) >> 56) |
|||
| ((x & 0x00ff000000000000ull) >> 40) |
|||
| ((x & 0x0000ff0000000000ull) >> 24) |
|||
| ((x & 0x000000ff00000000ull) >> 8) |
|||
| ((x & 0x00000000ff000000ull) << 8) |
|||
| ((x & 0x0000000000ff0000ull) << 24) |
|||
| ((x & 0x000000000000ff00ull) << 40) |
|||
| ((x & 0x00000000000000ffull) << 56)); |
|||
} |
|||
#endif // HAVE_DECL_BSWAP64 == 0
|
|||
|
|||
#endif // defined(MAC_OSX)
|
|||
|
|||
#endif // BITCOIN_COMPAT_BYTESWAP_H
|
@ -0,0 +1,241 @@ |
|||
// Copyright (c) 2014-2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_COMPAT_ENDIAN_H |
|||
#define BITCOIN_COMPAT_ENDIAN_H |
|||
|
|||
#if defined(HAVE_CONFIG_H) |
|||
#include <config/bitcoin-config.h> |
|||
#endif |
|||
|
|||
#include <compat/byteswap.h> |
|||
|
|||
#include <stdint.h> |
|||
|
|||
#if defined(HAVE_ENDIAN_H) |
|||
#include <endian.h> |
|||
#elif defined(HAVE_SYS_ENDIAN_H) |
|||
#include <sys/endian.h> |
|||
#endif |
|||
|
|||
#ifndef HAVE_CONFIG_H |
|||
// While not technically a supported configuration, defaulting to defining these
|
|||
// DECLs when we were compiled without autotools makes it easier for other build
|
|||
// systems to build things like libbitcoinconsensus for strange targets.
|
|||
#ifdef htobe16 |
|||
#define HAVE_DECL_HTOBE16 1 |
|||
#endif |
|||
#ifdef htole16 |
|||
#define HAVE_DECL_HTOLE16 1 |
|||
#endif |
|||
#ifdef be16toh |
|||
#define HAVE_DECL_BE16TOH 1 |
|||
#endif |
|||
#ifdef le16toh |
|||
#define HAVE_DECL_LE16TOH 1 |
|||
#endif |
|||
|
|||
#ifdef htobe32 |
|||
#define HAVE_DECL_HTOBE32 1 |
|||
#endif |
|||
#ifdef htole32 |
|||
#define HAVE_DECL_HTOLE32 1 |
|||
#endif |
|||
#ifdef be32toh |
|||
#define HAVE_DECL_BE32TOH 1 |
|||
#endif |
|||
#ifdef le32toh |
|||
#define HAVE_DECL_LE32TOH 1 |
|||
#endif |
|||
|
|||
#ifdef htobe64 |
|||
#define HAVE_DECL_HTOBE64 1 |
|||
#endif |
|||
#ifdef htole64 |
|||
#define HAVE_DECL_HTOLE64 1 |
|||
#endif |
|||
#ifdef be64toh |
|||
#define HAVE_DECL_BE64TOH 1 |
|||
#endif |
|||
#ifdef le64toh |
|||
#define HAVE_DECL_LE64TOH 1 |
|||
#endif |
|||
|
|||
#endif // HAVE_CONFIG_H
|
|||
|
|||
#if defined(WORDS_BIGENDIAN) |
|||
|
|||
#if HAVE_DECL_HTOBE16 == 0 |
|||
inline uint16_t htobe16(uint16_t host_16bits) |
|||
{ |
|||
return host_16bits; |
|||
} |
|||
#endif // HAVE_DECL_HTOBE16
|
|||
|
|||
#if HAVE_DECL_HTOLE16 == 0 |
|||
inline uint16_t htole16(uint16_t host_16bits) |
|||
{ |
|||
return bswap_16(host_16bits); |
|||
} |
|||
#endif // HAVE_DECL_HTOLE16
|
|||
|
|||
#if HAVE_DECL_BE16TOH == 0 |
|||
inline uint16_t be16toh(uint16_t big_endian_16bits) |
|||
{ |
|||
return big_endian_16bits; |
|||
} |
|||
#endif // HAVE_DECL_BE16TOH
|
|||
|
|||
#if HAVE_DECL_LE16TOH == 0 |
|||
inline uint16_t le16toh(uint16_t little_endian_16bits) |
|||
{ |
|||
return bswap_16(little_endian_16bits); |
|||
} |
|||
#endif // HAVE_DECL_LE16TOH
|
|||
|
|||
#if HAVE_DECL_HTOBE32 == 0 |
|||
inline uint32_t htobe32(uint32_t host_32bits) |
|||
{ |
|||
return host_32bits; |
|||
} |
|||
#endif // HAVE_DECL_HTOBE32
|
|||
|
|||
#if HAVE_DECL_HTOLE32 == 0 |
|||
inline uint32_t htole32(uint32_t host_32bits) |
|||
{ |
|||
return bswap_32(host_32bits); |
|||
} |
|||
#endif // HAVE_DECL_HTOLE32
|
|||
|
|||
#if HAVE_DECL_BE32TOH == 0 |
|||
inline uint32_t be32toh(uint32_t big_endian_32bits) |
|||
{ |
|||
return big_endian_32bits; |
|||
} |
|||
#endif // HAVE_DECL_BE32TOH
|
|||
|
|||
#if HAVE_DECL_LE32TOH == 0 |
|||
inline uint32_t le32toh(uint32_t little_endian_32bits) |
|||
{ |
|||
return bswap_32(little_endian_32bits); |
|||
} |
|||
#endif // HAVE_DECL_LE32TOH
|
|||
|
|||
#if HAVE_DECL_HTOBE64 == 0 |
|||
inline uint64_t htobe64(uint64_t host_64bits) |
|||
{ |
|||
return host_64bits; |
|||
} |
|||
#endif // HAVE_DECL_HTOBE64
|
|||
|
|||
#if HAVE_DECL_HTOLE64 == 0 |
|||
inline uint64_t htole64(uint64_t host_64bits) |
|||
{ |
|||
return bswap_64(host_64bits); |
|||
} |
|||
#endif // HAVE_DECL_HTOLE64
|
|||
|
|||
#if HAVE_DECL_BE64TOH == 0 |
|||
inline uint64_t be64toh(uint64_t big_endian_64bits) |
|||
{ |
|||
return big_endian_64bits; |
|||
} |
|||
#endif // HAVE_DECL_BE64TOH
|
|||
|
|||
#if HAVE_DECL_LE64TOH == 0 |
|||
inline uint64_t le64toh(uint64_t little_endian_64bits) |
|||
{ |
|||
return bswap_64(little_endian_64bits); |
|||
} |
|||
#endif // HAVE_DECL_LE64TOH
|
|||
|
|||
#else // WORDS_BIGENDIAN
|
|||
|
|||
#if HAVE_DECL_HTOBE16 == 0 |
|||
inline uint16_t htobe16(uint16_t host_16bits) |
|||
{ |
|||
return bswap_16(host_16bits); |
|||
} |
|||
#endif // HAVE_DECL_HTOBE16
|
|||
|
|||
#if HAVE_DECL_HTOLE16 == 0 |
|||
inline uint16_t htole16(uint16_t host_16bits) |
|||
{ |
|||
return host_16bits; |
|||
} |
|||
#endif // HAVE_DECL_HTOLE16
|
|||
|
|||
#if HAVE_DECL_BE16TOH == 0 |
|||
inline uint16_t be16toh(uint16_t big_endian_16bits) |
|||
{ |
|||
return bswap_16(big_endian_16bits); |
|||
} |
|||
#endif // HAVE_DECL_BE16TOH
|
|||
|
|||
#if HAVE_DECL_LE16TOH == 0 |
|||
inline uint16_t le16toh(uint16_t little_endian_16bits) |
|||
{ |
|||
return little_endian_16bits; |
|||
} |
|||
#endif // HAVE_DECL_LE16TOH
|
|||
|
|||
#if HAVE_DECL_HTOBE32 == 0 |
|||
inline uint32_t htobe32(uint32_t host_32bits) |
|||
{ |
|||
return bswap_32(host_32bits); |
|||
} |
|||
#endif // HAVE_DECL_HTOBE32
|
|||
|
|||
#if HAVE_DECL_HTOLE32 == 0 |
|||
inline uint32_t htole32(uint32_t host_32bits) |
|||
{ |
|||
return host_32bits; |
|||
} |
|||
#endif // HAVE_DECL_HTOLE32
|
|||
|
|||
#if HAVE_DECL_BE32TOH == 0 |
|||
inline uint32_t be32toh(uint32_t big_endian_32bits) |
|||
{ |
|||
return bswap_32(big_endian_32bits); |
|||
} |
|||
#endif // HAVE_DECL_BE32TOH
|
|||
|
|||
#if HAVE_DECL_LE32TOH == 0 |
|||
inline uint32_t le32toh(uint32_t little_endian_32bits) |
|||
{ |
|||
return little_endian_32bits; |
|||
} |
|||
#endif // HAVE_DECL_LE32TOH
|
|||
|
|||
#if HAVE_DECL_HTOBE64 == 0 |
|||
inline uint64_t htobe64(uint64_t host_64bits) |
|||
{ |
|||
return bswap_64(host_64bits); |
|||
} |
|||
#endif // HAVE_DECL_HTOBE64
|
|||
|
|||
#if HAVE_DECL_HTOLE64 == 0 |
|||
inline uint64_t htole64(uint64_t host_64bits) |
|||
{ |
|||
return host_64bits; |
|||
} |
|||
#endif // HAVE_DECL_HTOLE64
|
|||
|
|||
#if HAVE_DECL_BE64TOH == 0 |
|||
inline uint64_t be64toh(uint64_t big_endian_64bits) |
|||
{ |
|||
return bswap_64(big_endian_64bits); |
|||
} |
|||
#endif // HAVE_DECL_BE64TOH
|
|||
|
|||
#if HAVE_DECL_LE64TOH == 0 |
|||
inline uint64_t le64toh(uint64_t little_endian_64bits) |
|||
{ |
|||
return little_endian_64bits; |
|||
} |
|||
#endif // HAVE_DECL_LE64TOH
|
|||
|
|||
#endif // WORDS_BIGENDIAN
|
|||
|
|||
#endif // BITCOIN_COMPAT_ENDIAN_H
|
@ -0,0 +1,103 @@ |
|||
// Copyright (c) 2014-2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_CRYPTO_COMMON_H |
|||
#define BITCOIN_CRYPTO_COMMON_H |
|||
|
|||
#if defined(HAVE_CONFIG_H) |
|||
#include <config/bitcoin-config.h> |
|||
#endif |
|||
|
|||
#include <stdint.h> |
|||
#include <string.h> |
|||
|
|||
#include <compat/endian.h> |
|||
|
|||
uint16_t static inline ReadLE16(const unsigned char* ptr) |
|||
{ |
|||
uint16_t x; |
|||
memcpy((char*)&x, ptr, 2); |
|||
return le16toh(x); |
|||
} |
|||
|
|||
uint32_t static inline ReadLE32(const unsigned char* ptr) |
|||
{ |
|||
uint32_t x; |
|||
memcpy((char*)&x, ptr, 4); |
|||
return le32toh(x); |
|||
} |
|||
|
|||
uint64_t static inline ReadLE64(const unsigned char* ptr) |
|||
{ |
|||
uint64_t x; |
|||
memcpy((char*)&x, ptr, 8); |
|||
return le64toh(x); |
|||
} |
|||
|
|||
void static inline WriteLE16(unsigned char* ptr, uint16_t x) |
|||
{ |
|||
uint16_t v = htole16(x); |
|||
memcpy(ptr, (char*)&v, 2); |
|||
} |
|||
|
|||
void static inline WriteLE32(unsigned char* ptr, uint32_t x) |
|||
{ |
|||
uint32_t v = htole32(x); |
|||
memcpy(ptr, (char*)&v, 4); |
|||
} |
|||
|
|||
void static inline WriteLE64(unsigned char* ptr, uint64_t x) |
|||
{ |
|||
uint64_t v = htole64(x); |
|||
memcpy(ptr, (char*)&v, 8); |
|||
} |
|||
|
|||
uint32_t static inline ReadBE32(const unsigned char* ptr) |
|||
{ |
|||
uint32_t x; |
|||
memcpy((char*)&x, ptr, 4); |
|||
return be32toh(x); |
|||
} |
|||
|
|||
uint64_t static inline ReadBE64(const unsigned char* ptr) |
|||
{ |
|||
uint64_t x; |
|||
memcpy((char*)&x, ptr, 8); |
|||
return be64toh(x); |
|||
} |
|||
|
|||
void static inline WriteBE32(unsigned char* ptr, uint32_t x) |
|||
{ |
|||
uint32_t v = htobe32(x); |
|||
memcpy(ptr, (char*)&v, 4); |
|||
} |
|||
|
|||
void static inline WriteBE64(unsigned char* ptr, uint64_t x) |
|||
{ |
|||
uint64_t v = htobe64(x); |
|||
memcpy(ptr, (char*)&v, 8); |
|||
} |
|||
|
|||
/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ |
|||
uint64_t static inline CountBits(uint64_t x) |
|||
{ |
|||
#if HAVE_DECL___BUILTIN_CLZL |
|||
if (sizeof(unsigned long) >= sizeof(uint64_t)) { |
|||
return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; |
|||
} |
|||
#endif |
|||
#if HAVE_DECL___BUILTIN_CLZLL |
|||
if (sizeof(unsigned long long) >= sizeof(uint64_t)) { |
|||
return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; |
|||
} |
|||
#endif |
|||
int ret = 0; |
|||
while (x) { |
|||
x >>= 1; |
|||
++ret; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
#endif // BITCOIN_CRYPTO_COMMON_H
|
@ -0,0 +1,528 @@ |
|||
// Copyright (c) 2015-2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_PREVECTOR_H |
|||
#define BITCOIN_PREVECTOR_H |
|||
|
|||
#include <assert.h> |
|||
#include <stdlib.h> |
|||
#include <stdint.h> |
|||
#include <string.h> |
|||
|
|||
#include <algorithm> |
|||
#include <cstddef> |
|||
#include <iterator> |
|||
#include <type_traits> |
|||
|
|||
#pragma pack(push, 1) |
|||
/** Implements a drop-in replacement for std::vector<T> which stores up to N
|
|||
* elements directly (without heap allocation). The types Size and Diff are |
|||
* used to store element counts, and can be any unsigned + signed type. |
|||
* |
|||
* Storage layout is either: |
|||
* - Direct allocation: |
|||
* - Size _size: the number of used elements (between 0 and N) |
|||
* - T direct[N]: an array of N elements of type T |
|||
* (only the first _size are initialized). |
|||
* - Indirect allocation: |
|||
* - Size _size: the number of used elements plus N + 1 |
|||
* - Size capacity: the number of allocated elements |
|||
* - T* indirect: a pointer to an array of capacity elements of type T |
|||
* (only the first _size are initialized). |
|||
* |
|||
* The data type T must be movable by memmove/realloc(). Once we switch to C++, |
|||
* move constructors can be used instead. |
|||
*/ |
|||
template<unsigned int N, typename T, typename Size = uint32_t, typename Diff = int32_t> |
|||
class prevector { |
|||
public: |
|||
typedef Size size_type; |
|||
typedef Diff difference_type; |
|||
typedef T value_type; |
|||
typedef value_type& reference; |
|||
typedef const value_type& const_reference; |
|||
typedef value_type* pointer; |
|||
typedef const value_type* const_pointer; |
|||
|
|||
class iterator { |
|||
T* ptr; |
|||
public: |
|||
typedef Diff difference_type; |
|||
typedef T value_type; |
|||
typedef T* pointer; |
|||
typedef T& reference; |
|||
typedef std::random_access_iterator_tag iterator_category; |
|||
iterator(T* ptr_) : ptr(ptr_) {} |
|||
T& operator*() const { return *ptr; } |
|||
T* operator->() const { return ptr; } |
|||
T& operator[](size_type pos) { return ptr[pos]; } |
|||
const T& operator[](size_type pos) const { return ptr[pos]; } |
|||
iterator& operator++() { ptr++; return *this; } |
|||
iterator& operator--() { ptr--; return *this; } |
|||
iterator operator++(int) { iterator copy(*this); ++(*this); return copy; } |
|||
iterator operator--(int) { iterator copy(*this); --(*this); return copy; } |
|||
difference_type friend operator-(iterator a, iterator b) { return (&(*a) - &(*b)); } |
|||
iterator operator+(size_type n) { return iterator(ptr + n); } |
|||
iterator& operator+=(size_type n) { ptr += n; return *this; } |
|||
iterator operator-(size_type n) { return iterator(ptr - n); } |
|||
iterator& operator-=(size_type n) { ptr -= n; return *this; } |
|||
bool operator==(iterator x) const { return ptr == x.ptr; } |
|||
bool operator!=(iterator x) const { return ptr != x.ptr; } |
|||
bool operator>=(iterator x) const { return ptr >= x.ptr; } |
|||
bool operator<=(iterator x) const { return ptr <= x.ptr; } |
|||
bool operator>(iterator x) const { return ptr > x.ptr; } |
|||
bool operator<(iterator x) const { return ptr < x.ptr; } |
|||
}; |
|||
|
|||
class reverse_iterator { |
|||
T* ptr; |
|||
public: |
|||
typedef Diff difference_type; |
|||
typedef T value_type; |
|||
typedef T* pointer; |
|||
typedef T& reference; |
|||
typedef std::bidirectional_iterator_tag iterator_category; |
|||
reverse_iterator(T* ptr_) : ptr(ptr_) {} |
|||
T& operator*() { return *ptr; } |
|||
const T& operator*() const { return *ptr; } |
|||
T* operator->() { return ptr; } |
|||
const T* operator->() const { return ptr; } |
|||
reverse_iterator& operator--() { ptr++; return *this; } |
|||
reverse_iterator& operator++() { ptr--; return *this; } |
|||
reverse_iterator operator++(int) { reverse_iterator copy(*this); ++(*this); return copy; } |
|||
reverse_iterator operator--(int) { reverse_iterator copy(*this); --(*this); return copy; } |
|||
bool operator==(reverse_iterator x) const { return ptr == x.ptr; } |
|||
bool operator!=(reverse_iterator x) const { return ptr != x.ptr; } |
|||
}; |
|||
|
|||
class const_iterator { |
|||
const T* ptr; |
|||
public: |
|||
typedef Diff difference_type; |
|||
typedef const T value_type; |
|||
typedef const T* pointer; |
|||
typedef const T& reference; |
|||
typedef std::random_access_iterator_tag iterator_category; |
|||
const_iterator(const T* ptr_) : ptr(ptr_) {} |
|||
const_iterator(iterator x) : ptr(&(*x)) {} |
|||
const T& operator*() const { return *ptr; } |
|||
const T* operator->() const { return ptr; } |
|||
const T& operator[](size_type pos) const { return ptr[pos]; } |
|||
const_iterator& operator++() { ptr++; return *this; } |
|||
const_iterator& operator--() { ptr--; return *this; } |
|||
const_iterator operator++(int) { const_iterator copy(*this); ++(*this); return copy; } |
|||
const_iterator operator--(int) { const_iterator copy(*this); --(*this); return copy; } |
|||
difference_type friend operator-(const_iterator a, const_iterator b) { return (&(*a) - &(*b)); } |
|||
const_iterator operator+(size_type n) { return const_iterator(ptr + n); } |
|||
const_iterator& operator+=(size_type n) { ptr += n; return *this; } |
|||
const_iterator operator-(size_type n) { return const_iterator(ptr - n); } |
|||
const_iterator& operator-=(size_type n) { ptr -= n; return *this; } |
|||
bool operator==(const_iterator x) const { return ptr == x.ptr; } |
|||
bool operator!=(const_iterator x) const { return ptr != x.ptr; } |
|||
bool operator>=(const_iterator x) const { return ptr >= x.ptr; } |
|||
bool operator<=(const_iterator x) const { return ptr <= x.ptr; } |
|||
bool operator>(const_iterator x) const { return ptr > x.ptr; } |
|||
bool operator<(const_iterator x) const { return ptr < x.ptr; } |
|||
}; |
|||
|
|||
class const_reverse_iterator { |
|||
const T* ptr; |
|||
public: |
|||
typedef Diff difference_type; |
|||
typedef const T value_type; |
|||
typedef const T* pointer; |
|||
typedef const T& reference; |
|||
typedef std::bidirectional_iterator_tag iterator_category; |
|||
const_reverse_iterator(const T* ptr_) : ptr(ptr_) {} |
|||
const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {} |
|||
const T& operator*() const { return *ptr; } |
|||
const T* operator->() const { return ptr; } |
|||
const_reverse_iterator& operator--() { ptr++; return *this; } |
|||
const_reverse_iterator& operator++() { ptr--; return *this; } |
|||
const_reverse_iterator operator++(int) { const_reverse_iterator copy(*this); ++(*this); return copy; } |
|||
const_reverse_iterator operator--(int) { const_reverse_iterator copy(*this); --(*this); return copy; } |
|||
bool operator==(const_reverse_iterator x) const { return ptr == x.ptr; } |
|||
bool operator!=(const_reverse_iterator x) const { return ptr != x.ptr; } |
|||
}; |
|||
|
|||
private: |
|||
size_type _size = 0; |
|||
union direct_or_indirect { |
|||
char direct[sizeof(T) * N]; |
|||
struct { |
|||
size_type capacity; |
|||
char* indirect; |
|||
}; |
|||
} _union = {}; |
|||
|
|||
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; } |
|||
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; } |
|||
T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect) + pos; } |
|||
const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect) + pos; } |
|||
bool is_direct() const { return _size <= N; } |
|||
|
|||
void change_capacity(size_type new_capacity) { |
|||
if (new_capacity <= N) { |
|||
if (!is_direct()) { |
|||
T* indirect = indirect_ptr(0); |
|||
T* src = indirect; |
|||
T* dst = direct_ptr(0); |
|||
memcpy(dst, src, size() * sizeof(T)); |
|||
free(indirect); |
|||
_size -= N + 1; |
|||
} |
|||
} else { |
|||
if (!is_direct()) { |
|||
/* FIXME: Because malloc/realloc here won't call new_handler if allocation fails, assert
|
|||
success. These should instead use an allocator or new/delete so that handlers |
|||
are called as necessary, but performance would be slightly degraded by doing so. */ |
|||
_union.indirect = static_cast<char*>(realloc(_union.indirect, ((size_t)sizeof(T)) * new_capacity)); |
|||
assert(_union.indirect); |
|||
_union.capacity = new_capacity; |
|||
} else { |
|||
char* new_indirect = static_cast<char*>(malloc(((size_t)sizeof(T)) * new_capacity)); |
|||
assert(new_indirect); |
|||
T* src = direct_ptr(0); |
|||
T* dst = reinterpret_cast<T*>(new_indirect); |
|||
memcpy(dst, src, size() * sizeof(T)); |
|||
_union.indirect = new_indirect; |
|||
_union.capacity = new_capacity; |
|||
_size += N + 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } |
|||
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } |
|||
|
|||
void fill(T* dst, ptrdiff_t count, const T& value = T{}) { |
|||
std::fill_n(dst, count, value); |
|||
} |
|||
|
|||
template<typename InputIterator> |
|||
void fill(T* dst, InputIterator first, InputIterator last) { |
|||
while (first != last) { |
|||
new(static_cast<void*>(dst)) T(*first); |
|||
++dst; |
|||
++first; |
|||
} |
|||
} |
|||
|
|||
public: |
|||
void assign(size_type n, const T& val) { |
|||
clear(); |
|||
if (capacity() < n) { |
|||
change_capacity(n); |
|||
} |
|||
_size += n; |
|||
fill(item_ptr(0), n, val); |
|||
} |
|||
|
|||
template<typename InputIterator> |
|||
void assign(InputIterator first, InputIterator last) { |
|||
size_type n = last - first; |
|||
clear(); |
|||
if (capacity() < n) { |
|||
change_capacity(n); |
|||
} |
|||
_size += n; |
|||
fill(item_ptr(0), first, last); |
|||
} |
|||
|
|||
prevector() {} |
|||
|
|||
explicit prevector(size_type n) { |
|||
resize(n); |
|||
} |
|||
|
|||
explicit prevector(size_type n, const T& val) { |
|||
change_capacity(n); |
|||
_size += n; |
|||
fill(item_ptr(0), n, val); |
|||
} |
|||
|
|||
template<typename InputIterator> |
|||
prevector(InputIterator first, InputIterator last) { |
|||
size_type n = last - first; |
|||
change_capacity(n); |
|||
_size += n; |
|||
fill(item_ptr(0), first, last); |
|||
} |
|||
|
|||
prevector(const prevector<N, T, Size, Diff>& other) { |
|||
size_type n = other.size(); |
|||
change_capacity(n); |
|||
_size += n; |
|||
fill(item_ptr(0), other.begin(), other.end()); |
|||
} |
|||
|
|||
prevector(prevector<N, T, Size, Diff>&& other) { |
|||
swap(other); |
|||
} |
|||
|
|||
prevector& operator=(const prevector<N, T, Size, Diff>& other) { |
|||
if (&other == this) { |
|||
return *this; |
|||
} |
|||
assign(other.begin(), other.end()); |
|||
return *this; |
|||
} |
|||
|
|||
prevector& operator=(prevector<N, T, Size, Diff>&& other) { |
|||
swap(other); |
|||
return *this; |
|||
} |
|||
|
|||
size_type size() const { |
|||
return is_direct() ? _size : _size - N - 1; |
|||
} |
|||
|
|||
bool empty() const { |
|||
return size() == 0; |
|||
} |
|||
|
|||
iterator begin() { return iterator(item_ptr(0)); } |
|||
const_iterator begin() const { return const_iterator(item_ptr(0)); } |
|||
iterator end() { return iterator(item_ptr(size())); } |
|||
const_iterator end() const { return const_iterator(item_ptr(size())); } |
|||
|
|||
reverse_iterator rbegin() { return reverse_iterator(item_ptr(size() - 1)); } |
|||
const_reverse_iterator rbegin() const { return const_reverse_iterator(item_ptr(size() - 1)); } |
|||
reverse_iterator rend() { return reverse_iterator(item_ptr(-1)); } |
|||
const_reverse_iterator rend() const { return const_reverse_iterator(item_ptr(-1)); } |
|||
|
|||
size_t capacity() const { |
|||
if (is_direct()) { |
|||
return N; |
|||
} else { |
|||
return _union.capacity; |
|||
} |
|||
} |
|||
|
|||
T& operator[](size_type pos) { |
|||
return *item_ptr(pos); |
|||
} |
|||
|
|||
const T& operator[](size_type pos) const { |
|||
return *item_ptr(pos); |
|||
} |
|||
|
|||
void resize(size_type new_size) { |
|||
size_type cur_size = size(); |
|||
if (cur_size == new_size) { |
|||
return; |
|||
} |
|||
if (cur_size > new_size) { |
|||
erase(item_ptr(new_size), end()); |
|||
return; |
|||
} |
|||
if (new_size > capacity()) { |
|||
change_capacity(new_size); |
|||
} |
|||
ptrdiff_t increase = new_size - cur_size; |
|||
fill(item_ptr(cur_size), increase); |
|||
_size += increase; |
|||
} |
|||
|
|||
void reserve(size_type new_capacity) { |
|||
if (new_capacity > capacity()) { |
|||
change_capacity(new_capacity); |
|||
} |
|||
} |
|||
|
|||
void shrink_to_fit() { |
|||
change_capacity(size()); |
|||
} |
|||
|
|||
void clear() { |
|||
resize(0); |
|||
} |
|||
|
|||
iterator insert(iterator pos, const T& value) { |
|||
size_type p = pos - begin(); |
|||
size_type new_size = size() + 1; |
|||
if (capacity() < new_size) { |
|||
change_capacity(new_size + (new_size >> 1)); |
|||
} |
|||
T* ptr = item_ptr(p); |
|||
memmove(ptr + 1, ptr, (size() - p) * sizeof(T)); |
|||
_size++; |
|||
new(static_cast<void*>(ptr)) T(value); |
|||
return iterator(ptr); |
|||
} |
|||
|
|||
void insert(iterator pos, size_type count, const T& value) { |
|||
size_type p = pos - begin(); |
|||
size_type new_size = size() + count; |
|||
if (capacity() < new_size) { |
|||
change_capacity(new_size + (new_size >> 1)); |
|||
} |
|||
T* ptr = item_ptr(p); |
|||
memmove(ptr + count, ptr, (size() - p) * sizeof(T)); |
|||
_size += count; |
|||
fill(item_ptr(p), count, value); |
|||
} |
|||
|
|||
template<typename InputIterator> |
|||
void insert(iterator pos, InputIterator first, InputIterator last) { |
|||
size_type p = pos - begin(); |
|||
difference_type count = last - first; |
|||
size_type new_size = size() + count; |
|||
if (capacity() < new_size) { |
|||
change_capacity(new_size + (new_size >> 1)); |
|||
} |
|||
T* ptr = item_ptr(p); |
|||
memmove(ptr + count, ptr, (size() - p) * sizeof(T)); |
|||
_size += count; |
|||
fill(ptr, first, last); |
|||
} |
|||
|
|||
inline void resize_uninitialized(size_type new_size) { |
|||
// resize_uninitialized changes the size of the prevector but does not initialize it.
|
|||
// If size < new_size, the added elements must be initialized explicitly.
|
|||
if (capacity() < new_size) { |
|||
change_capacity(new_size); |
|||
_size += new_size - size(); |
|||
return; |
|||
} |
|||
if (new_size < size()) { |
|||
erase(item_ptr(new_size), end()); |
|||
} else { |
|||
_size += new_size - size(); |
|||
} |
|||
} |
|||
|
|||
iterator erase(iterator pos) { |
|||
return erase(pos, pos + 1); |
|||
} |
|||
|
|||
iterator erase(iterator first, iterator last) { |
|||
// Erase is not allowed to the change the object's capacity. That means
|
|||
// that when starting with an indirectly allocated prevector with
|
|||
// size and capacity > N, the result may be a still indirectly allocated
|
|||
// prevector with size <= N and capacity > N. A shrink_to_fit() call is
|
|||
// necessary to switch to the (more efficient) directly allocated
|
|||
// representation (with capacity N and size <= N).
|
|||
iterator p = first; |
|||
char* endp = (char*)&(*end()); |
|||
if (!std::is_trivially_destructible<T>::value) { |
|||
while (p != last) { |
|||
(*p).~T(); |
|||
_size--; |
|||
++p; |
|||
} |
|||
} else { |
|||
_size -= last - p; |
|||
} |
|||
memmove(&(*first), &(*last), endp - ((char*)(&(*last)))); |
|||
return first; |
|||
} |
|||
|
|||
void push_back(const T& value) { |
|||
size_type new_size = size() + 1; |
|||
if (capacity() < new_size) { |
|||
change_capacity(new_size + (new_size >> 1)); |
|||
} |
|||
new(item_ptr(size())) T(value); |
|||
_size++; |
|||
} |
|||
|
|||
void pop_back() { |
|||
erase(end() - 1, end()); |
|||
} |
|||
|
|||
T& front() { |
|||
return *item_ptr(0); |
|||
} |
|||
|
|||
const T& front() const { |
|||
return *item_ptr(0); |
|||
} |
|||
|
|||
T& back() { |
|||
return *item_ptr(size() - 1); |
|||
} |
|||
|
|||
const T& back() const { |
|||
return *item_ptr(size() - 1); |
|||
} |
|||
|
|||
void swap(prevector<N, T, Size, Diff>& other) { |
|||
std::swap(_union, other._union); |
|||
std::swap(_size, other._size); |
|||
} |
|||
|
|||
~prevector() { |
|||
if (!std::is_trivially_destructible<T>::value) { |
|||
clear(); |
|||
} |
|||
if (!is_direct()) { |
|||
free(_union.indirect); |
|||
_union.indirect = nullptr; |
|||
} |
|||
} |
|||
|
|||
bool operator==(const prevector<N, T, Size, Diff>& other) const { |
|||
if (other.size() != size()) { |
|||
return false; |
|||
} |
|||
const_iterator b1 = begin(); |
|||
const_iterator b2 = other.begin(); |
|||
const_iterator e1 = end(); |
|||
while (b1 != e1) { |
|||
if ((*b1) != (*b2)) { |
|||
return false; |
|||
} |
|||
++b1; |
|||
++b2; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
bool operator!=(const prevector<N, T, Size, Diff>& other) const { |
|||
return !(*this == other); |
|||
} |
|||
|
|||
bool operator<(const prevector<N, T, Size, Diff>& other) const { |
|||
if (size() < other.size()) { |
|||
return true; |
|||
} |
|||
if (size() > other.size()) { |
|||
return false; |
|||
} |
|||
const_iterator b1 = begin(); |
|||
const_iterator b2 = other.begin(); |
|||
const_iterator e1 = end(); |
|||
while (b1 != e1) { |
|||
if ((*b1) < (*b2)) { |
|||
return true; |
|||
} |
|||
if ((*b2) < (*b1)) { |
|||
return false; |
|||
} |
|||
++b1; |
|||
++b2; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
size_t allocated_memory() const { |
|||
if (is_direct()) { |
|||
return 0; |
|||
} else { |
|||
return ((size_t)(sizeof(T))) * _union.capacity; |
|||
} |
|||
} |
|||
|
|||
value_type* data() { |
|||
return item_ptr(0); |
|||
} |
|||
|
|||
const value_type* data() const { |
|||
return item_ptr(0); |
|||
} |
|||
}; |
|||
#pragma pack(pop) |
|||
|
|||
#endif // BITCOIN_PREVECTOR_H
|
@ -0,0 +1,184 @@ |
|||
// Copyright (c) 2019 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 <string> |
|||
#include <vector> |
|||
#include <script/script.h> |
|||
#include <script/miniscript.h> |
|||
|
|||
#include <assert.h> |
|||
|
|||
namespace miniscript { |
|||
namespace internal { |
|||
|
|||
Type SanitizeType(Type e) { |
|||
int num_types = (e << "K"_mst) + (e << "V"_mst) + (e << "B"_mst) + (e << "W"_mst); |
|||
if (num_types == 0) return ""_mst; // No valid type, don't care about the rest
|
|||
assert(num_types == 1); // K, V, B, W all conflict with each other
|
|||
assert(!(e << "z"_mst) || !(e << "o"_mst)); // z conflicts with o
|
|||
assert(!(e << "n"_mst) || !(e << "z"_mst)); // n conflicts with z
|
|||
assert(!(e << "V"_mst) || !(e << "d"_mst)); // V conflicts with d
|
|||
assert(!(e << "K"_mst) || (e << "u"_mst)); // K implies u
|
|||
assert(!(e << "V"_mst) || !(e << "u"_mst)); // V conflicts with u
|
|||
assert(!(e << "e"_mst) || !(e << "f"_mst)); // e conflicts with f
|
|||
assert(!(e << "e"_mst) || (e << "d"_mst)); // e implies d
|
|||
assert(!(e << "V"_mst) || !(e << "e"_mst)); // V conflicts with e
|
|||
assert(!(e << "d"_mst) || !(e << "f"_mst)); // d conflicts with f
|
|||
assert(!(e << "V"_mst) || (e << "f"_mst)); // V implies f
|
|||
assert(!(e << "K"_mst) || (e << "s"_mst)); // K implies s
|
|||
assert(!(e << "z"_mst) || (e << "m"_mst)); // z implies m
|
|||
return e; |
|||
} |
|||
|
|||
Type CalcSimpleType(NodeType nodetype, Type x, Type y, Type z) { |
|||
// Below is the per-nodetype logic for computing the expression types.
|
|||
// It heavily relies on Type's << operator (where "X << a_mst" means
|
|||
// "X has all properties listed in a").
|
|||
switch (nodetype) { |
|||
case NodeType::PK: return "Konudemsx"_mst; |
|||
case NodeType::PK_H: return "Knudemsx"_mst; |
|||
case NodeType::OLDER: return "Bzfmx"_mst; |
|||
case NodeType::AFTER: return "Bzfmx"_mst; |
|||
case NodeType::SHA256: return "Bonudm"_mst; |
|||
case NodeType::RIPEMD160: return "Bonudm"_mst; |
|||
case NodeType::HASH256: return "Bonudm"_mst; |
|||
case NodeType::HASH160: return "Bonudm"_mst; |
|||
case NodeType::TRUE: return "Bzufmx"_mst; |
|||
case NodeType::FALSE: return "Bzudemsx"_mst; |
|||
case NodeType::WRAP_A: return |
|||
"W"_mst.If(x << "B"_mst) | // W=B_x
|
|||
(x & "udfems"_mst) | // u=u_x, d=d_x, f=f_x, e=e_x, m=m_x, s=s_x
|
|||
"x"_mst; // x
|
|||
case NodeType::WRAP_S: return |
|||
"W"_mst.If(x << "Bo"_mst) | // W=B_x*o_x
|
|||
(x & "udfemsx"_mst); // u=u_x, d=d_x, f=f_x, e=e_x, m=m_x, s=s_x, x=x_x
|
|||
case NodeType::WRAP_C: return |
|||
"B"_mst.If(x << "K"_mst) | // B=K_x
|
|||
(x & "ondem"_mst) | // o=o_x, n=n_x, d=d_x, e=e_x, m=m_x
|
|||
"us"_mst; // u, s
|
|||
case NodeType::WRAP_D: return |
|||
"B"_mst.If(x << "Vz"_mst) | // B=V_x*z_x
|
|||
"o"_mst.If(x << "z"_mst) | // o=z_x
|
|||
"e"_mst.If(x << "f"_mst) | // e=f_x
|
|||
(x & "ms"_mst) | // m=m_x, s=s_x
|
|||
"nudx"_mst; // n, u, d, x
|
|||
case NodeType::WRAP_V: return |
|||
"V"_mst.If(x << "B"_mst) | // V=B_x
|
|||
(x & "zonms"_mst) | // z=z_x, o=o_x, n=n_x, m=m_x, s=s_x
|
|||
"fx"_mst; // f, x
|
|||
case NodeType::WRAP_J: return |
|||
"B"_mst.If(x << "Bn"_mst) | // B=B_x*n_x
|
|||
"e"_mst.If(x << "f"_mst) | // e=f_x
|
|||
(x & "oums"_mst) | // o=o_x, u=u_x, m=m_x, s=s_x
|
|||
"ndx"_mst; // n, d, x
|
|||
case NodeType::WRAP_N: return |
|||
(x & "Bzondfems"_mst) | // B=B_x, z=z_x, o=o_x, n=n_x, d=d_x, f=f_x, e=e_x, m=m_x, s=s_x
|
|||
"ux"_mst; // u, x
|
|||
case NodeType::AND_V: return |
|||
(y & "KVB"_mst).If(x << "V"_mst) | // B=V_x*B_y, V=V_x*V_y, K=V_x*K_y
|
|||
(x & "n"_mst) | (y & "n"_mst).If(x << "z"_mst) | // n=n_x+z_x*n_y
|
|||
((x | y) & "o"_mst).If((x | y) << "z"_mst) | // o=o_x*z_y+z_x*o_y
|
|||
(x & y & "dmz"_mst) | // d=d_x*d_y, m=m_x*m_y, z=z_x*z_y
|
|||
((x | y) & "s"_mst) | // s=s_x+s_y
|
|||
(y & "ufx"_mst); // u=u_y, f=f_y, x=x_y
|
|||
case NodeType::AND_B: return |
|||
(x & "B"_mst).If(y << "W"_mst) | // B=B_x*W_y
|
|||
((x | y) & "o"_mst).If((x | y) << "z"_mst) | // o=o_x*z_y+z_x*o_y
|
|||
(x & "n"_mst) | (y & "n"_mst).If(x << "z"_mst) | // n=n_x+z_x*n_y
|
|||
(x & y & "e"_mst).If((x & y) << "s"_mst) | // e=e_x*e_y*s_x*s_y
|
|||
(x & y & "dfzm"_mst) | // d=d_x*d_y, f=f_x*f_y, z=z_x*z_y, m=m_x*m_y
|
|||
((x | y) & "s"_mst) | // s=s_x+s_y
|
|||
"ux"_mst; // u, x
|
|||
case NodeType::OR_B: return |
|||
"B"_mst.If(x << "Bd"_mst && y << "Wd"_mst) | // B=B_x*d_x*W_x*d_y
|
|||
((x | y) & "o"_mst).If((x | y) << "z"_mst) | // o=o_x*z_y+z_x*o_y
|
|||
(x & y & "m"_mst).If((x | y) << "s"_mst && (x & y) << "e"_mst) | // m=m_x*m_y*e_x*e_y*(s_x+s_y)
|
|||
(x & y & "zse"_mst) | // z=z_x*z_y, s=s_x*s_y, e=e_x*e_y
|
|||
"dux"_mst; // d, u, x
|
|||
case NodeType::OR_D: return |
|||
(y & "B"_mst).If(x << "Bdu"_mst) | // B=B_y*B_x*d_x*u_x
|
|||
(x & "o"_mst).If(y << "z"_mst) | // o=o_x*z_y
|
|||
(x & y & "m"_mst).If(x << "e"_mst && (x | y) << "s"_mst) | // m=m_x*m_y*e_x*(s_x+s_y)
|
|||
(x & y & "zes"_mst) | // z=z_x*z_y, e=e_x*e_y, s=s_x*s_y
|
|||
(y & "ufd"_mst) | // u=u_y, f=f_y, d=d_y
|
|||
"x"_mst; // x
|
|||
case NodeType::OR_C: return |
|||
(y & "V"_mst).If(x << "Bdu"_mst) | // V=V_y*B_x*u_x*d_x
|
|||
(x & "o"_mst).If(y << "z"_mst) | // o=o_x*z_y
|
|||
(x & y & "m"_mst).If(x << "e"_mst && (x | y) << "s"_mst) | // m=m_x*m_y*e_x*(s_x*s_y)
|
|||
(x & y & "zs"_mst) | // z=z_x*z_y, s=s_x*s_y
|
|||
"fx"_mst; // f, x
|
|||
case NodeType::OR_I: return |
|||
(x & y & "VBKufs"_mst) | // V=V_x*V_y, B=B_x*B_y, K=K_x*K_y, u=u_x*u_y, f=f_x*f_y, s=s_x*s_y
|
|||
"o"_mst.If((x & y) << "z"_mst) | // o=z_x*z_y
|
|||
((x | y) & "e"_mst).If((x | y) << "f"_mst) | // e=e_x*f_y+f_x*e_y
|
|||
(x & y & "m"_mst).If((x | y) << "s"_mst) | // m=m_x*m_y*(s_x+s_y)
|
|||
((x | y) & "d"_mst) | // d=d_x+d_y
|
|||
"x"_mst; // x
|
|||
case NodeType::ANDOR: return |
|||
(y & z & "BKV"_mst).If(x << "Bdu"_mst) | // B=B_x*d_x*u_x*B_y*B_z, K=B_x*d_x*u_x*K_y*K_z, V=B_x*d_x*u_x*V_y*V_z
|
|||
(x & y & z & "z"_mst) | // z=z_x*z_y*z_z
|
|||
((x | (y & z)) & "o"_mst).If((x | (y & z)) << "z"_mst) | // o=o_x*z_y*z_z+z_x+o_y*o_z
|
|||
(y & z & "fu"_mst) | // f=f_y*f_z, u=u_y*u_z
|
|||
(z & "d"_mst) | // d=d_x
|
|||
(x & z & "e"_mst).If(x << "s"_mst || y << "f"_mst) | // e=e_x*e_z*(s_x+s_y)
|
|||
(x & y & z & "m"_mst).If(x << "e"_mst && (x | y | z) << "s"_mst) | // m=m_x*m_y*m_z*e_x*(s_x+s_y+s_z)
|
|||
(z & (x | y) & "s"_mst) | // s=s_z*(s_x+s_y)
|
|||
"x"_mst; // x
|
|||
case NodeType::THRESH_M: return "Bnudems"_mst; |
|||
case NodeType::THRESH: break; |
|||
} |
|||
assert(false); |
|||
return ""_mst; |
|||
} |
|||
|
|||
bool DecomposeScript(const CScript& script, std::vector<std::pair<opcodetype, std::vector<unsigned char>>>& out) |
|||
{ |
|||
out.clear(); |
|||
CScript::const_iterator it = script.begin(), itend = script.end(); |
|||
while (it != itend) { |
|||
std::vector<unsigned char> push_data; |
|||
opcodetype opcode; |
|||
if (!script.GetOp(it, opcode, push_data)) { |
|||
out.clear(); |
|||
return false; |
|||
} else if (opcode >= OP_1 && opcode <= OP_16) { |
|||
// Deal with OP_n (GetOp does not turn them into pushes).
|
|||
push_data.assign(1, CScript::DecodeOP_N(opcode)); |
|||
} else if (opcode == OP_CHECKSIGVERIFY) { |
|||
// Decompose OP_CHECKSIGVERIFY into OP_CHECKSIG OP_VERIFY
|
|||
out.emplace_back(OP_CHECKSIG, std::vector<unsigned char>()); |
|||
opcode = OP_VERIFY; |
|||
} else if (opcode == OP_CHECKMULTISIGVERIFY) { |
|||
// Decompose OP_CHECKMULTISIGVERIFY into OP_CHECKMULTISIG OP_VERIFY
|
|||
out.emplace_back(OP_CHECKMULTISIG, std::vector<unsigned char>()); |
|||
opcode = OP_VERIFY; |
|||
} else if (opcode == OP_EQUALVERIFY) { |
|||
// Decompose OP_EQUALVERIFY into OP_EQUAL OP_VERIFY
|
|||
out.emplace_back(OP_EQUAL, std::vector<unsigned char>()); |
|||
opcode = OP_VERIFY; |
|||
} |
|||
out.emplace_back(opcode, std::move(push_data)); |
|||
} |
|||
std::reverse(out.begin(), out.end()); |
|||
return true; |
|||
} |
|||
|
|||
bool ParseScriptNumber(const std::pair<opcodetype, std::vector<unsigned char>>& in, int64_t& k) { |
|||
if (in.first == OP_0) { |
|||
k = 0; |
|||
return true; |
|||
} |
|||
if (!in.second.empty()) { |
|||
try { |
|||
k = CScriptNum(in.second, true).GetInt64(); |
|||
return true; |
|||
} catch(const scriptnum_error& error) {} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
} // namespace internal
|
|||
} // namespace miniscript
|
|||
|
File diff suppressed because it is too large
@ -0,0 +1,328 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2018 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/script.h> |
|||
|
|||
#include <util/strencodings.h> |
|||
|
|||
const char* GetOpName(opcodetype opcode) |
|||
{ |
|||
switch (opcode) |
|||
{ |
|||
// push value
|
|||
case OP_0 : return "0"; |
|||
case OP_PUSHDATA1 : return "OP_PUSHDATA1"; |
|||
case OP_PUSHDATA2 : return "OP_PUSHDATA2"; |
|||
case OP_PUSHDATA4 : return "OP_PUSHDATA4"; |
|||
case OP_1NEGATE : return "-1"; |
|||
case OP_RESERVED : return "OP_RESERVED"; |
|||
case OP_1 : return "1"; |
|||
case OP_2 : return "2"; |
|||
case OP_3 : return "3"; |
|||
case OP_4 : return "4"; |
|||
case OP_5 : return "5"; |
|||
case OP_6 : return "6"; |
|||
case OP_7 : return "7"; |
|||
case OP_8 : return "8"; |
|||
case OP_9 : return "9"; |
|||
case OP_10 : return "10"; |
|||
case OP_11 : return "11"; |
|||
case OP_12 : return "12"; |
|||
case OP_13 : return "13"; |
|||
case OP_14 : return "14"; |
|||
case OP_15 : return "15"; |
|||
case OP_16 : return "16"; |
|||
|
|||
// control
|
|||
case OP_NOP : return "OP_NOP"; |
|||
case OP_VER : return "OP_VER"; |
|||
case OP_IF : return "OP_IF"; |
|||
case OP_NOTIF : return "OP_NOTIF"; |
|||
case OP_VERIF : return "OP_VERIF"; |
|||
case OP_VERNOTIF : return "OP_VERNOTIF"; |
|||
case OP_ELSE : return "OP_ELSE"; |
|||
case OP_ENDIF : return "OP_ENDIF"; |
|||
case OP_VERIFY : return "OP_VERIFY"; |
|||
case OP_RETURN : return "OP_RETURN"; |
|||
|
|||
// stack ops
|
|||
case OP_TOALTSTACK : return "OP_TOALTSTACK"; |
|||
case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; |
|||
case OP_2DROP : return "OP_2DROP"; |
|||
case OP_2DUP : return "OP_2DUP"; |
|||
case OP_3DUP : return "OP_3DUP"; |
|||
case OP_2OVER : return "OP_2OVER"; |
|||
case OP_2ROT : return "OP_2ROT"; |
|||
case OP_2SWAP : return "OP_2SWAP"; |
|||
case OP_IFDUP : return "OP_IFDUP"; |
|||
case OP_DEPTH : return "OP_DEPTH"; |
|||
case OP_DROP : return "OP_DROP"; |
|||
case OP_DUP : return "OP_DUP"; |
|||
case OP_NIP : return "OP_NIP"; |
|||
case OP_OVER : return "OP_OVER"; |
|||
case OP_PICK : return "OP_PICK"; |
|||
case OP_ROLL : return "OP_ROLL"; |
|||
case OP_ROT : return "OP_ROT"; |
|||
case OP_SWAP : return "OP_SWAP"; |
|||
case OP_TUCK : return "OP_TUCK"; |
|||
|
|||
// splice ops
|
|||
case OP_CAT : return "OP_CAT"; |
|||
case OP_SUBSTR : return "OP_SUBSTR"; |
|||
case OP_LEFT : return "OP_LEFT"; |
|||
case OP_RIGHT : return "OP_RIGHT"; |
|||
case OP_SIZE : return "OP_SIZE"; |
|||
|
|||
// bit logic
|
|||
case OP_INVERT : return "OP_INVERT"; |
|||
case OP_AND : return "OP_AND"; |
|||
case OP_OR : return "OP_OR"; |
|||
case OP_XOR : return "OP_XOR"; |
|||
case OP_EQUAL : return "OP_EQUAL"; |
|||
case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; |
|||
case OP_RESERVED1 : return "OP_RESERVED1"; |
|||
case OP_RESERVED2 : return "OP_RESERVED2"; |
|||
|
|||
// numeric
|
|||
case OP_1ADD : return "OP_1ADD"; |
|||
case OP_1SUB : return "OP_1SUB"; |
|||
case OP_2MUL : return "OP_2MUL"; |
|||
case OP_2DIV : return "OP_2DIV"; |
|||
case OP_NEGATE : return "OP_NEGATE"; |
|||
case OP_ABS : return "OP_ABS"; |
|||
case OP_NOT : return "OP_NOT"; |
|||
case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; |
|||
case OP_ADD : return "OP_ADD"; |
|||
case OP_SUB : return "OP_SUB"; |
|||
case OP_MUL : return "OP_MUL"; |
|||
case OP_DIV : return "OP_DIV"; |
|||
case OP_MOD : return "OP_MOD"; |
|||
case OP_LSHIFT : return "OP_LSHIFT"; |
|||
case OP_RSHIFT : return "OP_RSHIFT"; |
|||
case OP_BOOLAND : return "OP_BOOLAND"; |
|||
case OP_BOOLOR : return "OP_BOOLOR"; |
|||
case OP_NUMEQUAL : return "OP_NUMEQUAL"; |
|||
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; |
|||
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; |
|||
case OP_LESSTHAN : return "OP_LESSTHAN"; |
|||
case OP_GREATERTHAN : return "OP_GREATERTHAN"; |
|||
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; |
|||
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; |
|||
case OP_MIN : return "OP_MIN"; |
|||
case OP_MAX : return "OP_MAX"; |
|||
case OP_WITHIN : return "OP_WITHIN"; |
|||
|
|||
// crypto
|
|||
case OP_RIPEMD160 : return "OP_RIPEMD160"; |
|||
case OP_SHA1 : return "OP_SHA1"; |
|||
case OP_SHA256 : return "OP_SHA256"; |
|||
case OP_HASH160 : return "OP_HASH160"; |
|||
case OP_HASH256 : return "OP_HASH256"; |
|||
case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; |
|||
case OP_CHECKSIG : return "OP_CHECKSIG"; |
|||
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; |
|||
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; |
|||
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; |
|||
|
|||
// expansion
|
|||
case OP_NOP1 : return "OP_NOP1"; |
|||
case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; |
|||
case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY"; |
|||
case OP_NOP4 : return "OP_NOP4"; |
|||
case OP_NOP5 : return "OP_NOP5"; |
|||
case OP_NOP6 : return "OP_NOP6"; |
|||
case OP_NOP7 : return "OP_NOP7"; |
|||
case OP_NOP8 : return "OP_NOP8"; |
|||
case OP_NOP9 : return "OP_NOP9"; |
|||
case OP_NOP10 : return "OP_NOP10"; |
|||
|
|||
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; |
|||
|
|||
default: |
|||
return "OP_UNKNOWN"; |
|||
} |
|||
} |
|||
|
|||
unsigned int CScript::GetSigOpCount(bool fAccurate) const |
|||
{ |
|||
unsigned int n = 0; |
|||
const_iterator pc = begin(); |
|||
opcodetype lastOpcode = OP_INVALIDOPCODE; |
|||
while (pc < end()) |
|||
{ |
|||
opcodetype opcode; |
|||
if (!GetOp(pc, opcode)) |
|||
break; |
|||
if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) |
|||
n++; |
|||
else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) |
|||
{ |
|||
if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) |
|||
n += DecodeOP_N(lastOpcode); |
|||
else |
|||
n += MAX_PUBKEYS_PER_MULTISIG; |
|||
} |
|||
lastOpcode = opcode; |
|||
} |
|||
return n; |
|||
} |
|||
|
|||
unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const |
|||
{ |
|||
if (!IsPayToScriptHash()) |
|||
return GetSigOpCount(true); |
|||
|
|||
// This is a pay-to-script-hash scriptPubKey;
|
|||
// get the last item that the scriptSig
|
|||
// pushes onto the stack:
|
|||
const_iterator pc = scriptSig.begin(); |
|||
std::vector<unsigned char> vData; |
|||
while (pc < scriptSig.end()) |
|||
{ |
|||
opcodetype opcode; |
|||
if (!scriptSig.GetOp(pc, opcode, vData)) |
|||
return 0; |
|||
if (opcode > OP_16) |
|||
return 0; |
|||
} |
|||
|
|||
/// ... and return its opcount:
|
|||
CScript subscript(vData.begin(), vData.end()); |
|||
return subscript.GetSigOpCount(true); |
|||
} |
|||
|
|||
bool CScript::IsPayToScriptHash() const |
|||
{ |
|||
// Extra-fast test for pay-to-script-hash CScripts:
|
|||
return (this->size() == 23 && |
|||
(*this)[0] == OP_HASH160 && |
|||
(*this)[1] == 0x14 && |
|||
(*this)[22] == OP_EQUAL); |
|||
} |
|||
|
|||
bool CScript::IsPayToWitnessScriptHash() const |
|||
{ |
|||
// Extra-fast test for pay-to-witness-script-hash CScripts:
|
|||
return (this->size() == 34 && |
|||
(*this)[0] == OP_0 && |
|||
(*this)[1] == 0x20); |
|||
} |
|||
|
|||
// A witness program is any valid CScript that consists of a 1-byte push opcode
|
|||
// followed by a data push between 2 and 40 bytes.
|
|||
bool CScript::IsWitnessProgram(int& version, std::vector<unsigned char>& program) const |
|||
{ |
|||
if (this->size() < 4 || this->size() > 42) { |
|||
return false; |
|||
} |
|||
if ((*this)[0] != OP_0 && ((*this)[0] < OP_1 || (*this)[0] > OP_16)) { |
|||
return false; |
|||
} |
|||
if ((size_t)((*this)[1] + 2) == this->size()) { |
|||
version = DecodeOP_N((opcodetype)(*this)[0]); |
|||
program = std::vector<unsigned char>(this->begin() + 2, this->end()); |
|||
return true; |
|||
} |
|||
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 |
|||
{ |
|||
return this->IsPushOnly(begin()); |
|||
} |
|||
|
|||
std::string CScriptWitness::ToString() const |
|||
{ |
|||
std::string ret = "CScriptWitness("; |
|||
for (unsigned int i = 0; i < stack.size(); i++) { |
|||
if (i) { |
|||
ret += ", "; |
|||
} |
|||
ret += HexStr(stack[i]); |
|||
} |
|||
return ret + ")"; |
|||
} |
|||
|
|||
bool CScript::HasValidOps() const |
|||
{ |
|||
CScript::const_iterator it = begin(); |
|||
while (it < end()) { |
|||
opcodetype opcode; |
|||
std::vector<unsigned char> item; |
|||
if (!GetOp(it, opcode, item) || opcode > MAX_OPCODE || item.size() > MAX_SCRIPT_ELEMENT_SIZE) { |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) |
|||
{ |
|||
opcodeRet = OP_INVALIDOPCODE; |
|||
if (pvchRet) |
|||
pvchRet->clear(); |
|||
if (pc >= end) |
|||
return false; |
|||
|
|||
// Read instruction
|
|||
if (end - pc < 1) |
|||
return false; |
|||
unsigned int opcode = *pc++; |
|||
|
|||
// Immediate operand
|
|||
if (opcode <= OP_PUSHDATA4) |
|||
{ |
|||
unsigned int nSize = 0; |
|||
if (opcode < OP_PUSHDATA1) |
|||
{ |
|||
nSize = opcode; |
|||
} |
|||
else if (opcode == OP_PUSHDATA1) |
|||
{ |
|||
if (end - pc < 1) |
|||
return false; |
|||
nSize = *pc++; |
|||
} |
|||
else if (opcode == OP_PUSHDATA2) |
|||
{ |
|||
if (end - pc < 2) |
|||
return false; |
|||
nSize = ReadLE16(&pc[0]); |
|||
pc += 2; |
|||
} |
|||
else if (opcode == OP_PUSHDATA4) |
|||
{ |
|||
if (end - pc < 4) |
|||
return false; |
|||
nSize = ReadLE32(&pc[0]); |
|||
pc += 4; |
|||
} |
|||
if (end - pc < 0 || (unsigned int)(end - pc) < nSize) |
|||
return false; |
|||
if (pvchRet) |
|||
pvchRet->assign(pc, pc + nSize); |
|||
pc += nSize; |
|||
} |
|||
|
|||
opcodeRet = static_cast<opcodetype>(opcode); |
|||
return true; |
|||
} |
@ -0,0 +1,588 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_SCRIPT_SCRIPT_H |
|||
#define BITCOIN_SCRIPT_SCRIPT_H |
|||
|
|||
#include <crypto/common.h> |
|||
#include <prevector.h> |
|||
#include <serialize.h> |
|||
|
|||
#include <assert.h> |
|||
#include <climits> |
|||
#include <limits> |
|||
#include <stdexcept> |
|||
#include <stdint.h> |
|||
#include <string.h> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
// Maximum number of bytes pushable to the stack
|
|||
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; |
|||
|
|||
// Maximum number of non-push operations per script
|
|||
static const int MAX_OPS_PER_SCRIPT = 201; |
|||
|
|||
// Maximum number of public keys per multisig
|
|||
static const int MAX_PUBKEYS_PER_MULTISIG = 20; |
|||
|
|||
// Maximum script length in bytes
|
|||
static const int MAX_SCRIPT_SIZE = 10000; |
|||
|
|||
// Maximum number of values on script interpreter stack
|
|||
static const int MAX_STACK_SIZE = 1000; |
|||
|
|||
// Threshold for nLockTime: below this value it is interpreted as block number,
|
|||
// otherwise as UNIX timestamp.
|
|||
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
|
|||
|
|||
// Maximum nLockTime. Since a lock time indicates the last invalid timestamp, a
|
|||
// transaction with this lock time will never be valid unless lock time
|
|||
// checking is disabled (by setting all input sequence numbers to
|
|||
// SEQUENCE_FINAL).
|
|||
static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU; |
|||
|
|||
template <typename T> |
|||
std::vector<unsigned char> ToByteVector(const T& in) |
|||
{ |
|||
return std::vector<unsigned char>(in.begin(), in.end()); |
|||
} |
|||
|
|||
/** Script opcodes */ |
|||
enum opcodetype |
|||
{ |
|||
// push value
|
|||
OP_0 = 0x00, |
|||
OP_FALSE = OP_0, |
|||
OP_PUSHDATA1 = 0x4c, |
|||
OP_PUSHDATA2 = 0x4d, |
|||
OP_PUSHDATA4 = 0x4e, |
|||
OP_1NEGATE = 0x4f, |
|||
OP_RESERVED = 0x50, |
|||
OP_1 = 0x51, |
|||
OP_TRUE=OP_1, |
|||
OP_2 = 0x52, |
|||
OP_3 = 0x53, |
|||
OP_4 = 0x54, |
|||
OP_5 = 0x55, |
|||
OP_6 = 0x56, |
|||
OP_7 = 0x57, |
|||
OP_8 = 0x58, |
|||
OP_9 = 0x59, |
|||
OP_10 = 0x5a, |
|||
OP_11 = 0x5b, |
|||
OP_12 = 0x5c, |
|||
OP_13 = 0x5d, |
|||
OP_14 = 0x5e, |
|||
OP_15 = 0x5f, |
|||
OP_16 = 0x60, |
|||
|
|||
// control
|
|||
OP_NOP = 0x61, |
|||
OP_VER = 0x62, |
|||
OP_IF = 0x63, |
|||
OP_NOTIF = 0x64, |
|||
OP_VERIF = 0x65, |
|||
OP_VERNOTIF = 0x66, |
|||
OP_ELSE = 0x67, |
|||
OP_ENDIF = 0x68, |
|||
OP_VERIFY = 0x69, |
|||
OP_RETURN = 0x6a, |
|||
|
|||
// stack ops
|
|||
OP_TOALTSTACK = 0x6b, |
|||
OP_FROMALTSTACK = 0x6c, |
|||
OP_2DROP = 0x6d, |
|||
OP_2DUP = 0x6e, |
|||
OP_3DUP = 0x6f, |
|||
OP_2OVER = 0x70, |
|||
OP_2ROT = 0x71, |
|||
OP_2SWAP = 0x72, |
|||
OP_IFDUP = 0x73, |
|||
OP_DEPTH = 0x74, |
|||
OP_DROP = 0x75, |
|||
OP_DUP = 0x76, |
|||
OP_NIP = 0x77, |
|||
OP_OVER = 0x78, |
|||
OP_PICK = 0x79, |
|||
OP_ROLL = 0x7a, |
|||
OP_ROT = 0x7b, |
|||
OP_SWAP = 0x7c, |
|||
OP_TUCK = 0x7d, |
|||
|
|||
// splice ops
|
|||
OP_CAT = 0x7e, |
|||
OP_SUBSTR = 0x7f, |
|||
OP_LEFT = 0x80, |
|||
OP_RIGHT = 0x81, |
|||
OP_SIZE = 0x82, |
|||
|
|||
// bit logic
|
|||
OP_INVERT = 0x83, |
|||
OP_AND = 0x84, |
|||
OP_OR = 0x85, |
|||
OP_XOR = 0x86, |
|||
OP_EQUAL = 0x87, |
|||
OP_EQUALVERIFY = 0x88, |
|||
OP_RESERVED1 = 0x89, |
|||
OP_RESERVED2 = 0x8a, |
|||
|
|||
// numeric
|
|||
OP_1ADD = 0x8b, |
|||
OP_1SUB = 0x8c, |
|||
OP_2MUL = 0x8d, |
|||
OP_2DIV = 0x8e, |
|||
OP_NEGATE = 0x8f, |
|||
OP_ABS = 0x90, |
|||
OP_NOT = 0x91, |
|||
OP_0NOTEQUAL = 0x92, |
|||
|
|||
OP_ADD = 0x93, |
|||
OP_SUB = 0x94, |
|||
OP_MUL = 0x95, |
|||
OP_DIV = 0x96, |
|||
OP_MOD = 0x97, |
|||
OP_LSHIFT = 0x98, |
|||
OP_RSHIFT = 0x99, |
|||
|
|||
OP_BOOLAND = 0x9a, |
|||
OP_BOOLOR = 0x9b, |
|||
OP_NUMEQUAL = 0x9c, |
|||
OP_NUMEQUALVERIFY = 0x9d, |
|||
OP_NUMNOTEQUAL = 0x9e, |
|||
OP_LESSTHAN = 0x9f, |
|||
OP_GREATERTHAN = 0xa0, |
|||
OP_LESSTHANOREQUAL = 0xa1, |
|||
OP_GREATERTHANOREQUAL = 0xa2, |
|||
OP_MIN = 0xa3, |
|||
OP_MAX = 0xa4, |
|||
|
|||
OP_WITHIN = 0xa5, |
|||
|
|||
// crypto
|
|||
OP_RIPEMD160 = 0xa6, |
|||
OP_SHA1 = 0xa7, |
|||
OP_SHA256 = 0xa8, |
|||
OP_HASH160 = 0xa9, |
|||
OP_HASH256 = 0xaa, |
|||
OP_CODESEPARATOR = 0xab, |
|||
OP_CHECKSIG = 0xac, |
|||
OP_CHECKSIGVERIFY = 0xad, |
|||
OP_CHECKMULTISIG = 0xae, |
|||
OP_CHECKMULTISIGVERIFY = 0xaf, |
|||
|
|||
// expansion
|
|||
OP_NOP1 = 0xb0, |
|||
OP_CHECKLOCKTIMEVERIFY = 0xb1, |
|||
OP_NOP2 = OP_CHECKLOCKTIMEVERIFY, |
|||
OP_CHECKSEQUENCEVERIFY = 0xb2, |
|||
OP_NOP3 = OP_CHECKSEQUENCEVERIFY, |
|||
OP_NOP4 = 0xb3, |
|||
OP_NOP5 = 0xb4, |
|||
OP_NOP6 = 0xb5, |
|||
OP_NOP7 = 0xb6, |
|||
OP_NOP8 = 0xb7, |
|||
OP_NOP9 = 0xb8, |
|||
OP_NOP10 = 0xb9, |
|||
|
|||
OP_INVALIDOPCODE = 0xff, |
|||
}; |
|||
|
|||
// Maximum value that an opcode can be
|
|||
static const unsigned int MAX_OPCODE = OP_NOP10; |
|||
|
|||
const char* GetOpName(opcodetype opcode); |
|||
|
|||
class scriptnum_error : public std::runtime_error |
|||
{ |
|||
public: |
|||
explicit scriptnum_error(const std::string& str) : std::runtime_error(str) {} |
|||
}; |
|||
|
|||
class CScriptNum |
|||
{ |
|||
/**
|
|||
* Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers. |
|||
* The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1], |
|||
* but results may overflow (and are valid as long as they are not used in a subsequent |
|||
* numeric operation). CScriptNum enforces those semantics by storing results as |
|||
* an int64 and allowing out-of-range values to be returned as a vector of bytes but |
|||
* throwing an exception if arithmetic is done or the result is interpreted as an integer. |
|||
*/ |
|||
public: |
|||
|
|||
explicit CScriptNum(const int64_t& n) |
|||
{ |
|||
m_value = n; |
|||
} |
|||
|
|||
static const size_t nDefaultMaxNumSize = 4; |
|||
|
|||
explicit CScriptNum(const std::vector<unsigned char>& vch, bool fRequireMinimal, |
|||
const size_t nMaxNumSize = nDefaultMaxNumSize) |
|||
{ |
|||
if (vch.size() > nMaxNumSize) { |
|||
throw scriptnum_error("script number overflow"); |
|||
} |
|||
if (fRequireMinimal && vch.size() > 0) { |
|||
// Check that the number is encoded with the minimum possible
|
|||
// number of bytes.
|
|||
//
|
|||
// If the most-significant-byte - excluding the sign bit - is zero
|
|||
// then we're not minimal. Note how this test also rejects the
|
|||
// negative-zero encoding, 0x80.
|
|||
if ((vch.back() & 0x7f) == 0) { |
|||
// One exception: if there's more than one byte and the most
|
|||
// significant bit of the second-most-significant-byte is set
|
|||
// it would conflict with the sign bit. An example of this case
|
|||
// is +-255, which encode to 0xff00 and 0xff80 respectively.
|
|||
// (big-endian).
|
|||
if (vch.size() <= 1 || (vch[vch.size() - 2] & 0x80) == 0) { |
|||
throw scriptnum_error("non-minimally encoded script number"); |
|||
} |
|||
} |
|||
} |
|||
m_value = set_vch(vch); |
|||
} |
|||
|
|||
inline bool operator==(const int64_t& rhs) const { return m_value == rhs; } |
|||
inline bool operator!=(const int64_t& rhs) const { return m_value != rhs; } |
|||
inline bool operator<=(const int64_t& rhs) const { return m_value <= rhs; } |
|||
inline bool operator< (const int64_t& rhs) const { return m_value < rhs; } |
|||
inline bool operator>=(const int64_t& rhs) const { return m_value >= rhs; } |
|||
inline bool operator> (const int64_t& rhs) const { return m_value > rhs; } |
|||
|
|||
inline bool operator==(const CScriptNum& rhs) const { return operator==(rhs.m_value); } |
|||
inline bool operator!=(const CScriptNum& rhs) const { return operator!=(rhs.m_value); } |
|||
inline bool operator<=(const CScriptNum& rhs) const { return operator<=(rhs.m_value); } |
|||
inline bool operator< (const CScriptNum& rhs) const { return operator< (rhs.m_value); } |
|||
inline bool operator>=(const CScriptNum& rhs) const { return operator>=(rhs.m_value); } |
|||
inline bool operator> (const CScriptNum& rhs) const { return operator> (rhs.m_value); } |
|||
|
|||
inline CScriptNum operator+( const int64_t& rhs) const { return CScriptNum(m_value + rhs);} |
|||
inline CScriptNum operator-( const int64_t& rhs) const { return CScriptNum(m_value - rhs);} |
|||
inline CScriptNum operator+( const CScriptNum& rhs) const { return operator+(rhs.m_value); } |
|||
inline CScriptNum operator-( const CScriptNum& rhs) const { return operator-(rhs.m_value); } |
|||
|
|||
inline CScriptNum& operator+=( const CScriptNum& rhs) { return operator+=(rhs.m_value); } |
|||
inline CScriptNum& operator-=( const CScriptNum& rhs) { return operator-=(rhs.m_value); } |
|||
|
|||
inline CScriptNum operator&( const int64_t& rhs) const { return CScriptNum(m_value & rhs);} |
|||
inline CScriptNum operator&( const CScriptNum& rhs) const { return operator&(rhs.m_value); } |
|||
|
|||
inline CScriptNum& operator&=( const CScriptNum& rhs) { return operator&=(rhs.m_value); } |
|||
|
|||
inline CScriptNum operator-() const |
|||
{ |
|||
assert(m_value != std::numeric_limits<int64_t>::min()); |
|||
return CScriptNum(-m_value); |
|||
} |
|||
|
|||
inline CScriptNum& operator=( const int64_t& rhs) |
|||
{ |
|||
m_value = rhs; |
|||
return *this; |
|||
} |
|||
|
|||
inline CScriptNum& operator+=( const int64_t& rhs) |
|||
{ |
|||
assert(rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || |
|||
(rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)); |
|||
m_value += rhs; |
|||
return *this; |
|||
} |
|||
|
|||
inline CScriptNum& operator-=( const int64_t& rhs) |
|||
{ |
|||
assert(rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || |
|||
(rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)); |
|||
m_value -= rhs; |
|||
return *this; |
|||
} |
|||
|
|||
inline CScriptNum& operator&=( const int64_t& rhs) |
|||
{ |
|||
m_value &= rhs; |
|||
return *this; |
|||
} |
|||
|
|||
int getint() const |
|||
{ |
|||
if (m_value > std::numeric_limits<int>::max()) |
|||
return std::numeric_limits<int>::max(); |
|||
else if (m_value < std::numeric_limits<int>::min()) |
|||
return std::numeric_limits<int>::min(); |
|||
return m_value; |
|||
} |
|||
|
|||
int64_t GetInt64() const { return m_value; } |
|||
|
|||
std::vector<unsigned char> getvch() const |
|||
{ |
|||
return serialize(m_value); |
|||
} |
|||
|
|||
static std::vector<unsigned char> serialize(const int64_t& value) |
|||
{ |
|||
if(value == 0) |
|||
return std::vector<unsigned char>(); |
|||
|
|||
std::vector<unsigned char> result; |
|||
const bool neg = value < 0; |
|||
uint64_t absvalue = neg ? -value : value; |
|||
|
|||
while(absvalue) |
|||
{ |
|||
result.push_back(absvalue & 0xff); |
|||
absvalue >>= 8; |
|||
} |
|||
|
|||
// - If the most significant byte is >= 0x80 and the value is positive, push a
|
|||
// new zero-byte to make the significant byte < 0x80 again.
|
|||
|
|||
// - If the most significant byte is >= 0x80 and the value is negative, push a
|
|||
// new 0x80 byte that will be popped off when converting to an integral.
|
|||
|
|||
// - If the most significant byte is < 0x80 and the value is negative, add
|
|||
// 0x80 to it, since it will be subtracted and interpreted as a negative when
|
|||
// converting to an integral.
|
|||
|
|||
if (result.back() & 0x80) |
|||
result.push_back(neg ? 0x80 : 0); |
|||
else if (neg) |
|||
result.back() |= 0x80; |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private: |
|||
static int64_t set_vch(const std::vector<unsigned char>& vch) |
|||
{ |
|||
if (vch.empty()) |
|||
return 0; |
|||
|
|||
int64_t result = 0; |
|||
for (size_t i = 0; i != vch.size(); ++i) |
|||
result |= static_cast<int64_t>(vch[i]) << 8*i; |
|||
|
|||
// If the input vector's most significant byte is 0x80, remove it from
|
|||
// the result's msb and return a negative.
|
|||
if (vch.back() & 0x80) |
|||
return -((int64_t)(result & ~(0x80ULL << (8 * (vch.size() - 1))))); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
int64_t m_value; |
|||
}; |
|||
|
|||
/**
|
|||
* We use a prevector for the script to reduce the considerable memory overhead |
|||
* of vectors in cases where they normally contain a small number of small elements. |
|||
* Tests in October 2015 showed use of this reduced dbcache memory usage by 23% |
|||
* and made an initial sync 13% faster. |
|||
*/ |
|||
typedef prevector<28, unsigned char> CScriptBase; |
|||
|
|||
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet); |
|||
|
|||
/** Serialized script, used inside transaction inputs and outputs */ |
|||
class CScript : public CScriptBase |
|||
{ |
|||
protected: |
|||
CScript& push_int64(int64_t n) |
|||
{ |
|||
if (n == -1 || (n >= 1 && n <= 16)) |
|||
{ |
|||
push_back(n + (OP_1 - 1)); |
|||
} |
|||
else if (n == 0) |
|||
{ |
|||
push_back(OP_0); |
|||
} |
|||
else |
|||
{ |
|||
*this << CScriptNum::serialize(n); |
|||
} |
|||
return *this; |
|||
} |
|||
public: |
|||
CScript() { } |
|||
CScript(const_iterator pbegin, const_iterator pend) : CScriptBase(pbegin, pend) { } |
|||
CScript(std::vector<unsigned char>::const_iterator pbegin, std::vector<unsigned char>::const_iterator pend) : CScriptBase(pbegin, pend) { } |
|||
CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } |
|||
|
|||
ADD_SERIALIZE_METHODS; |
|||
|
|||
template <typename Stream, typename Operation> |
|||
inline void SerializationOp(Stream& s, Operation ser_action) { |
|||
READWRITEAS(CScriptBase, *this); |
|||
} |
|||
|
|||
CScript& operator+=(const CScript& b) |
|||
{ |
|||
reserve(size() + b.size()); |
|||
insert(end(), b.begin(), b.end()); |
|||
return *this; |
|||
} |
|||
|
|||
friend CScript operator+(const CScript& a, const CScript& b) |
|||
{ |
|||
CScript ret = a; |
|||
ret += b; |
|||
return ret; |
|||
} |
|||
|
|||
CScript(int64_t b) { operator<<(b); } |
|||
|
|||
explicit CScript(opcodetype b) { operator<<(b); } |
|||
explicit CScript(const CScriptNum& b) { operator<<(b); } |
|||
// delete non-existent constructor to defend against future introduction
|
|||
// e.g. via prevector
|
|||
explicit CScript(const std::vector<unsigned char>& b) = delete; |
|||
|
|||
|
|||
CScript& operator<<(int64_t b) { return push_int64(b); } |
|||
|
|||
CScript& operator<<(opcodetype opcode) |
|||
{ |
|||
if (opcode < 0 || opcode > 0xff) |
|||
throw std::runtime_error("CScript::operator<<(): invalid opcode"); |
|||
insert(end(), (unsigned char)opcode); |
|||
return *this; |
|||
} |
|||
|
|||
CScript& operator<<(const CScriptNum& b) |
|||
{ |
|||
*this << b.getvch(); |
|||
return *this; |
|||
} |
|||
|
|||
CScript& operator<<(const std::vector<unsigned char>& b) |
|||
{ |
|||
if (b.size() < OP_PUSHDATA1) |
|||
{ |
|||
insert(end(), (unsigned char)b.size()); |
|||
} |
|||
else if (b.size() <= 0xff) |
|||
{ |
|||
insert(end(), OP_PUSHDATA1); |
|||
insert(end(), (unsigned char)b.size()); |
|||
} |
|||
else if (b.size() <= 0xffff) |
|||
{ |
|||
insert(end(), OP_PUSHDATA2); |
|||
uint8_t _data[2]; |
|||
WriteLE16(_data, b.size()); |
|||
insert(end(), _data, _data + sizeof(_data)); |
|||
} |
|||
else |
|||
{ |
|||
insert(end(), OP_PUSHDATA4); |
|||
uint8_t _data[4]; |
|||
WriteLE32(_data, b.size()); |
|||
insert(end(), _data, _data + sizeof(_data)); |
|||
} |
|||
insert(end(), b.begin(), b.end()); |
|||
return *this; |
|||
} |
|||
|
|||
CScript& operator<<(const CScript& b) |
|||
{ |
|||
// I'm not sure if this should push the script or concatenate scripts.
|
|||
// If there's ever a use for pushing a script onto a script, delete this member fn
|
|||
assert(!"Warning: Pushing a CScript onto a CScript with << is probably not intended, use + to concatenate!"); |
|||
return *this; |
|||
} |
|||
|
|||
|
|||
bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const |
|||
{ |
|||
return GetScriptOp(pc, end(), opcodeRet, &vchRet); |
|||
} |
|||
|
|||
bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const |
|||
{ |
|||
return GetScriptOp(pc, end(), opcodeRet, nullptr); |
|||
} |
|||
|
|||
|
|||
/** Encode/decode small integers: */ |
|||
static int DecodeOP_N(opcodetype opcode) |
|||
{ |
|||
if (opcode == OP_0) |
|||
return 0; |
|||
assert(opcode >= OP_1 && opcode <= OP_16); |
|||
return (int)opcode - (int)(OP_1 - 1); |
|||
} |
|||
static opcodetype EncodeOP_N(int n) |
|||
{ |
|||
assert(n >= 0 && n <= 16); |
|||
if (n == 0) |
|||
return OP_0; |
|||
return (opcodetype)(OP_1+n-1); |
|||
} |
|||
|
|||
/**
|
|||
* Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs |
|||
* as 20 sigops. With pay-to-script-hash, that changed: |
|||
* CHECKMULTISIGs serialized in scriptSigs are |
|||
* counted more accurately, assuming they are of the form |
|||
* ... OP_N CHECKMULTISIG ... |
|||
*/ |
|||
unsigned int GetSigOpCount(bool fAccurate) const; |
|||
|
|||
/**
|
|||
* Accurately count sigOps, including sigOps in |
|||
* pay-to-script-hash transactions: |
|||
*/ |
|||
unsigned int GetSigOpCount(const CScript& scriptSig) const; |
|||
|
|||
bool IsPayToScriptHash() const; |
|||
bool IsPayToWitnessScriptHash() const; |
|||
bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const; |
|||
|
|||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ |
|||
bool IsPushOnly(const_iterator pc) const; |
|||
bool IsPushOnly() const; |
|||
|
|||
/** Check if the script contains valid OP_CODES */ |
|||
bool HasValidOps() const; |
|||
|
|||
/**
|
|||
* Returns whether the script is guaranteed to fail at execution, |
|||
* regardless of the initial stack. This allows outputs to be pruned |
|||
* instantly when entering the UTXO set. |
|||
*/ |
|||
bool IsUnspendable() const |
|||
{ |
|||
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE); |
|||
} |
|||
|
|||
void clear() |
|||
{ |
|||
// The default prevector::clear() does not release memory
|
|||
CScriptBase::clear(); |
|||
shrink_to_fit(); |
|||
} |
|||
}; |
|||
|
|||
struct CScriptWitness |
|||
{ |
|||
// Note that this encodes the data elements being pushed, rather than
|
|||
// encoding them as a CScript that pushes them.
|
|||
std::vector<std::vector<unsigned char> > stack; |
|||
|
|||
// Some compilers complain without a default constructor
|
|||
CScriptWitness() { } |
|||
|
|||
bool IsNull() const { return stack.empty(); } |
|||
|
|||
void SetNull() { stack.clear(); stack.shrink_to_fit(); } |
|||
|
|||
std::string ToString() const; |
|||
}; |
|||
|
|||
#endif // BITCOIN_SCRIPT_SCRIPT_H
|
File diff suppressed because it is too large
@ -0,0 +1,60 @@ |
|||
// Copyright (c) 2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef BITCOIN_SPAN_H |
|||
#define BITCOIN_SPAN_H |
|||
|
|||
#include <type_traits> |
|||
#include <cstddef> |
|||
#include <algorithm> |
|||
|
|||
/** A Span is an object that can refer to a contiguous sequence of objects.
|
|||
* |
|||
* It implements a subset of C++20's std::span. |
|||
*/ |
|||
template<typename C> |
|||
class Span |
|||
{ |
|||
C* m_data; |
|||
std::ptrdiff_t m_size; |
|||
|
|||
public: |
|||
constexpr Span() noexcept : m_data(nullptr), m_size(0) {} |
|||
constexpr Span(C* data, std::ptrdiff_t size) noexcept : m_data(data), m_size(size) {} |
|||
constexpr Span(C* data, C* end) noexcept : m_data(data), m_size(end - data) {} |
|||
|
|||
constexpr C* data() const noexcept { return m_data; } |
|||
constexpr C* begin() const noexcept { return m_data; } |
|||
constexpr C* end() const noexcept { return m_data + m_size; } |
|||
constexpr std::ptrdiff_t size() const noexcept { return m_size; } |
|||
constexpr C& operator[](std::ptrdiff_t pos) const noexcept { return m_data[pos]; } |
|||
|
|||
constexpr Span<C> subspan(std::ptrdiff_t offset) const noexcept { return Span<C>(m_data + offset, m_size - offset); } |
|||
constexpr Span<C> subspan(std::ptrdiff_t offset, std::ptrdiff_t count) const noexcept { return Span<C>(m_data + offset, count); } |
|||
constexpr Span<C> first(std::ptrdiff_t count) const noexcept { return Span<C>(m_data, count); } |
|||
constexpr Span<C> last(std::ptrdiff_t count) const noexcept { return Span<C>(m_data + m_size - count, count); } |
|||
|
|||
friend constexpr bool operator==(const Span& a, const Span& b) noexcept { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } |
|||
friend constexpr bool operator!=(const Span& a, const Span& b) noexcept { return !(a == b); } |
|||
friend constexpr bool operator<(const Span& a, const Span& b) noexcept { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } |
|||
friend constexpr bool operator<=(const Span& a, const Span& b) noexcept { return !(b < a); } |
|||
friend constexpr bool operator>(const Span& a, const Span& b) noexcept { return (b < a); } |
|||
friend constexpr bool operator>=(const Span& a, const Span& b) noexcept { return !(a < b); } |
|||
}; |
|||
|
|||
/** Create a span to a container exposing data() and size().
|
|||
* |
|||
* This correctly deals with constness: the returned Span's element type will be |
|||
* whatever data() returns a pointer to. If either the passed container is const, |
|||
* or its element type is const, the resulting span will have a const element type. |
|||
* |
|||
* std::span will have a constructor that implements this functionality directly. |
|||
*/ |
|||
template<typename A, int N> |
|||
constexpr Span<A> MakeSpan(A (&a)[N]) { return Span<A>(a, N); } |
|||
|
|||
template<typename V> |
|||
constexpr Span<typename std::remove_pointer<decltype(std::declval<V>().data())>::type> MakeSpan(V& v) { return Span<typename std::remove_pointer<decltype(std::declval<V>().data())>::type>(v.data(), v.size()); } |
|||
|
|||
#endif |
File diff suppressed because it is too large
@ -0,0 +1,45 @@ |
|||
// Copyright (c) 2019 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 <util/spanparsing.h> |
|||
|
|||
#include <string> |
|||
#include <span.h> |
|||
|
|||
bool Const(const std::string& str, Span<const char>& sp) |
|||
{ |
|||
if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) { |
|||
sp = sp.subspan(str.size()); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
bool Func(const std::string& str, Span<const char>& sp) |
|||
{ |
|||
if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) { |
|||
sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
Span<const char> Expr(Span<const char>& sp) |
|||
{ |
|||
int level = 0; |
|||
auto it = sp.begin(); |
|||
while (it != sp.end()) { |
|||
if (*it == '(') { |
|||
++level; |
|||
} else if (level && *it == ')') { |
|||
--level; |
|||
} else if (level == 0 && (*it == ')' || *it == ',')) { |
|||
break; |
|||
} |
|||
++it; |
|||
} |
|||
Span<const char> ret = sp.first(it - sp.begin()); |
|||
sp = sp.subspan(it - sp.begin()); |
|||
return ret; |
|||
} |
@ -0,0 +1,20 @@ |
|||
// Copyright (c) 2019 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef _BITCOIN_UTIL_SPANPARSING_H_ |
|||
#define _BITCOIN_UTIL_SPANPARSING_H_ 1 |
|||
|
|||
#include <string> |
|||
#include <span.h> |
|||
|
|||
/** Parse a constant. If successful, sp is updated to skip the constant and return true. */ |
|||
bool Const(const std::string& str, Span<const char>& sp); |
|||
|
|||
/** Parse a function call. If successful, sp is updated to be the function's argument(s). */ |
|||
bool Func(const std::string& str, Span<const char>& sp); |
|||
|
|||
/** Return the expression that sp begins with, and update sp to skip it. */ |
|||
Span<const char> Expr(Span<const char>& sp); |
|||
|
|||
#endif |
@ -0,0 +1,559 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2018 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 <util/strencodings.h> |
|||
|
|||
#include <tinyformat.h> |
|||
|
|||
#include <algorithm> |
|||
#include <cstdlib> |
|||
#include <cstring> |
|||
#include <errno.h> |
|||
#include <limits> |
|||
|
|||
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
|||
|
|||
static const std::string SAFE_CHARS[] = |
|||
{ |
|||
CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
|
|||
CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
|
|||
CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
|
|||
CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
|
|||
}; |
|||
|
|||
std::string SanitizeString(const std::string& str, int rule) |
|||
{ |
|||
std::string strResult; |
|||
for (std::string::size_type i = 0; i < str.size(); i++) |
|||
{ |
|||
if (SAFE_CHARS[rule].find(str[i]) != std::string::npos) |
|||
strResult.push_back(str[i]); |
|||
} |
|||
return strResult; |
|||
} |
|||
|
|||
const signed char p_util_hexdigit[256] = |
|||
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, |
|||
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, }; |
|||
|
|||
signed char HexDigit(char c) |
|||
{ |
|||
return p_util_hexdigit[(unsigned char)c]; |
|||
} |
|||
|
|||
bool IsHex(const std::string& str) |
|||
{ |
|||
for(std::string::const_iterator it(str.begin()); it != str.end(); ++it) |
|||
{ |
|||
if (HexDigit(*it) < 0) |
|||
return false; |
|||
} |
|||
return (str.size() > 0) && (str.size()%2 == 0); |
|||
} |
|||
|
|||
bool IsHexNumber(const std::string& str) |
|||
{ |
|||
size_t starting_location = 0; |
|||
if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') { |
|||
starting_location = 2; |
|||
} |
|||
for (const char c : str.substr(starting_location)) { |
|||
if (HexDigit(c) < 0) return false; |
|||
} |
|||
// Return false for empty string or "0x".
|
|||
return (str.size() > starting_location); |
|||
} |
|||
|
|||
std::vector<unsigned char> ParseHex(const char* psz) |
|||
{ |
|||
// convert hex dump to vector
|
|||
std::vector<unsigned char> vch; |
|||
while (true) |
|||
{ |
|||
while (IsSpace(*psz)) |
|||
psz++; |
|||
signed char c = HexDigit(*psz++); |
|||
if (c == (signed char)-1) |
|||
break; |
|||
unsigned char n = (c << 4); |
|||
c = HexDigit(*psz++); |
|||
if (c == (signed char)-1) |
|||
break; |
|||
n |= c; |
|||
vch.push_back(n); |
|||
} |
|||
return vch; |
|||
} |
|||
|
|||
std::vector<unsigned char> ParseHex(const std::string& str) |
|||
{ |
|||
return ParseHex(str.c_str()); |
|||
} |
|||
|
|||
void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { |
|||
size_t colon = in.find_last_of(':'); |
|||
// if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
|
|||
bool fHaveColon = colon != in.npos; |
|||
bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
|
|||
bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); |
|||
if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { |
|||
int32_t n; |
|||
if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { |
|||
in = in.substr(0, colon); |
|||
portOut = n; |
|||
} |
|||
} |
|||
if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') |
|||
hostOut = in.substr(1, in.size()-2); |
|||
else |
|||
hostOut = in; |
|||
} |
|||
|
|||
std::string EncodeBase64(const unsigned char* pch, size_t len) |
|||
{ |
|||
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|||
|
|||
std::string str; |
|||
str.reserve(((len + 2) / 3) * 4); |
|||
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len); |
|||
while (str.size() % 4) str += '='; |
|||
return str; |
|||
} |
|||
|
|||
std::string EncodeBase64(const std::string& str) |
|||
{ |
|||
return EncodeBase64((const unsigned char*)str.c_str(), str.size()); |
|||
} |
|||
|
|||
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid) |
|||
{ |
|||
static const int decode64_table[256] = |
|||
{ |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, |
|||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
|||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, |
|||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, |
|||
49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 |
|||
}; |
|||
|
|||
const char* e = p; |
|||
std::vector<uint8_t> val; |
|||
val.reserve(strlen(p)); |
|||
while (*p != 0) { |
|||
int x = decode64_table[(unsigned char)*p]; |
|||
if (x == -1) break; |
|||
val.push_back(x); |
|||
++p; |
|||
} |
|||
|
|||
std::vector<unsigned char> ret; |
|||
ret.reserve((val.size() * 3) / 4); |
|||
bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end()); |
|||
|
|||
const char* q = p; |
|||
while (valid && *p != 0) { |
|||
if (*p != '=') { |
|||
valid = false; |
|||
break; |
|||
} |
|||
++p; |
|||
} |
|||
valid = valid && (p - e) % 4 == 0 && p - q < 4; |
|||
if (pf_invalid) *pf_invalid = !valid; |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
std::string DecodeBase64(const std::string& str, bool* pf_invalid) |
|||
{ |
|||
std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid); |
|||
return std::string((const char*)vchRet.data(), vchRet.size()); |
|||
} |
|||
|
|||
std::string EncodeBase32(const unsigned char* pch, size_t len) |
|||
{ |
|||
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; |
|||
|
|||
std::string str; |
|||
str.reserve(((len + 4) / 5) * 8); |
|||
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len); |
|||
while (str.size() % 8) str += '='; |
|||
return str; |
|||
} |
|||
|
|||
std::string EncodeBase32(const std::string& str) |
|||
{ |
|||
return EncodeBase32((const unsigned char*)str.c_str(), str.size()); |
|||
} |
|||
|
|||
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid) |
|||
{ |
|||
static const int decode32_table[256] = |
|||
{ |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
|||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, |
|||
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, |
|||
23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 |
|||
}; |
|||
|
|||
const char* e = p; |
|||
std::vector<uint8_t> val; |
|||
val.reserve(strlen(p)); |
|||
while (*p != 0) { |
|||
int x = decode32_table[(unsigned char)*p]; |
|||
if (x == -1) break; |
|||
val.push_back(x); |
|||
++p; |
|||
} |
|||
|
|||
std::vector<unsigned char> ret; |
|||
ret.reserve((val.size() * 5) / 8); |
|||
bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end()); |
|||
|
|||
const char* q = p; |
|||
while (valid && *p != 0) { |
|||
if (*p != '=') { |
|||
valid = false; |
|||
break; |
|||
} |
|||
++p; |
|||
} |
|||
valid = valid && (p - e) % 8 == 0 && p - q < 8; |
|||
if (pf_invalid) *pf_invalid = !valid; |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
std::string DecodeBase32(const std::string& str, bool* pf_invalid) |
|||
{ |
|||
std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid); |
|||
return std::string((const char*)vchRet.data(), vchRet.size()); |
|||
} |
|||
|
|||
NODISCARD static bool ParsePrechecks(const std::string& str) |
|||
{ |
|||
if (str.empty()) // No empty string allowed
|
|||
return false; |
|||
if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
|
|||
return false; |
|||
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
|
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
bool ParseInt32(const std::string& str, int32_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtol will not set errno if valid
|
|||
long int n = strtol(str.c_str(), &endp, 10); |
|||
if(out) *out = (int32_t)n; |
|||
// Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
|
|||
// platforms the size of these types may be different.
|
|||
return endp && *endp == 0 && !errno && |
|||
n >= std::numeric_limits<int32_t>::min() && |
|||
n <= std::numeric_limits<int32_t>::max(); |
|||
} |
|||
|
|||
bool ParseInt64(const std::string& str, int64_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtoll will not set errno if valid
|
|||
long long int n = strtoll(str.c_str(), &endp, 10); |
|||
if(out) *out = (int64_t)n; |
|||
// Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *int64_t*.
|
|||
return endp && *endp == 0 && !errno && |
|||
n >= std::numeric_limits<int64_t>::min() && |
|||
n <= std::numeric_limits<int64_t>::max(); |
|||
} |
|||
|
|||
bool ParseUInt32(const std::string& str, uint32_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range
|
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtoul will not set errno if valid
|
|||
unsigned long int n = strtoul(str.c_str(), &endp, 10); |
|||
if(out) *out = (uint32_t)n; |
|||
// Note that strtoul returns a *unsigned long int*, so even if it doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit
|
|||
// platforms the size of these types may be different.
|
|||
return endp && *endp == 0 && !errno && |
|||
n <= std::numeric_limits<uint32_t>::max(); |
|||
} |
|||
|
|||
bool ParseUInt64(const std::string& str, uint64_t *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range
|
|||
return false; |
|||
char *endp = nullptr; |
|||
errno = 0; // strtoull will not set errno if valid
|
|||
unsigned long long int n = strtoull(str.c_str(), &endp, 10); |
|||
if(out) *out = (uint64_t)n; |
|||
// Note that strtoull returns a *unsigned long long int*, so even if it doesn't report an over/underflow
|
|||
// we still have to check that the returned value is within the range of an *uint64_t*.
|
|||
return endp && *endp == 0 && !errno && |
|||
n <= std::numeric_limits<uint64_t>::max(); |
|||
} |
|||
|
|||
|
|||
bool ParseDouble(const std::string& str, double *out) |
|||
{ |
|||
if (!ParsePrechecks(str)) |
|||
return false; |
|||
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
|
|||
return false; |
|||
std::istringstream text(str); |
|||
text.imbue(std::locale::classic()); |
|||
double result; |
|||
text >> result; |
|||
if(out) *out = result; |
|||
return text.eof() && !text.fail(); |
|||
} |
|||
|
|||
std::string FormatParagraph(const std::string& in, size_t width, size_t indent) |
|||
{ |
|||
std::stringstream out; |
|||
size_t ptr = 0; |
|||
size_t indented = 0; |
|||
while (ptr < in.size()) |
|||
{ |
|||
size_t lineend = in.find_first_of('\n', ptr); |
|||
if (lineend == std::string::npos) { |
|||
lineend = in.size(); |
|||
} |
|||
const size_t linelen = lineend - ptr; |
|||
const size_t rem_width = width - indented; |
|||
if (linelen <= rem_width) { |
|||
out << in.substr(ptr, linelen + 1); |
|||
ptr = lineend + 1; |
|||
indented = 0; |
|||
} else { |
|||
size_t finalspace = in.find_last_of(" \n", ptr + rem_width); |
|||
if (finalspace == std::string::npos || finalspace < ptr) { |
|||
// No place to break; just include the entire word and move on
|
|||
finalspace = in.find_first_of("\n ", ptr); |
|||
if (finalspace == std::string::npos) { |
|||
// End of the string, just add it and break
|
|||
out << in.substr(ptr); |
|||
break; |
|||
} |
|||
} |
|||
out << in.substr(ptr, finalspace - ptr) << "\n"; |
|||
if (in[finalspace] == '\n') { |
|||
indented = 0; |
|||
} else if (indent) { |
|||
out << std::string(indent, ' '); |
|||
indented = indent; |
|||
} |
|||
ptr = finalspace + 1; |
|||
} |
|||
} |
|||
return out.str(); |
|||
} |
|||
|
|||
std::string i64tostr(int64_t n) |
|||
{ |
|||
return strprintf("%d", n); |
|||
} |
|||
|
|||
std::string itostr(int n) |
|||
{ |
|||
return strprintf("%d", n); |
|||
} |
|||
|
|||
int64_t atoi64(const char* psz) |
|||
{ |
|||
#ifdef _MSC_VER |
|||
return _atoi64(psz); |
|||
#else |
|||
return strtoll(psz, nullptr, 10); |
|||
#endif |
|||
} |
|||
|
|||
int64_t atoi64(const std::string& str) |
|||
{ |
|||
#ifdef _MSC_VER |
|||
return _atoi64(str.c_str()); |
|||
#else |
|||
return strtoll(str.c_str(), nullptr, 10); |
|||
#endif |
|||
} |
|||
|
|||
int atoi(const std::string& str) |
|||
{ |
|||
return atoi(str.c_str()); |
|||
} |
|||
|
|||
/** Upper bound for mantissa.
|
|||
* 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer. |
|||
* Larger integers cannot consist of arbitrary combinations of 0-9: |
|||
* |
|||
* 999999999999999999 1^18-1 |
|||
* 9223372036854775807 (1<<63)-1 (max int64_t) |
|||
* 9999999999999999999 1^19-1 (would overflow) |
|||
*/ |
|||
static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL; |
|||
|
|||
/** Helper function for ParseFixedPoint */ |
|||
static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros) |
|||
{ |
|||
if(ch == '0') |
|||
++mantissa_tzeros; |
|||
else { |
|||
for (int i=0; i<=mantissa_tzeros; ++i) { |
|||
if (mantissa > (UPPER_BOUND / 10LL)) |
|||
return false; /* overflow */ |
|||
mantissa *= 10; |
|||
} |
|||
mantissa += ch - '0'; |
|||
mantissa_tzeros = 0; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) |
|||
{ |
|||
int64_t mantissa = 0; |
|||
int64_t exponent = 0; |
|||
int mantissa_tzeros = 0; |
|||
bool mantissa_sign = false; |
|||
bool exponent_sign = false; |
|||
int ptr = 0; |
|||
int end = val.size(); |
|||
int point_ofs = 0; |
|||
|
|||
if (ptr < end && val[ptr] == '-') { |
|||
mantissa_sign = true; |
|||
++ptr; |
|||
} |
|||
if (ptr < end) |
|||
{ |
|||
if (val[ptr] == '0') { |
|||
/* pass single 0 */ |
|||
++ptr; |
|||
} else if (val[ptr] >= '1' && val[ptr] <= '9') { |
|||
while (ptr < end && IsDigit(val[ptr])) { |
|||
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) |
|||
return false; /* overflow */ |
|||
++ptr; |
|||
} |
|||
} else return false; /* missing expected digit */ |
|||
} else return false; /* empty string or loose '-' */ |
|||
if (ptr < end && val[ptr] == '.') |
|||
{ |
|||
++ptr; |
|||
if (ptr < end && IsDigit(val[ptr])) |
|||
{ |
|||
while (ptr < end && IsDigit(val[ptr])) { |
|||
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) |
|||
return false; /* overflow */ |
|||
++ptr; |
|||
++point_ofs; |
|||
} |
|||
} else return false; /* missing expected digit */ |
|||
} |
|||
if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E')) |
|||
{ |
|||
++ptr; |
|||
if (ptr < end && val[ptr] == '+') |
|||
++ptr; |
|||
else if (ptr < end && val[ptr] == '-') { |
|||
exponent_sign = true; |
|||
++ptr; |
|||
} |
|||
if (ptr < end && IsDigit(val[ptr])) { |
|||
while (ptr < end && IsDigit(val[ptr])) { |
|||
if (exponent > (UPPER_BOUND / 10LL)) |
|||
return false; /* overflow */ |
|||
exponent = exponent * 10 + val[ptr] - '0'; |
|||
++ptr; |
|||
} |
|||
} else return false; /* missing expected digit */ |
|||
} |
|||
if (ptr != end) |
|||
return false; /* trailing garbage */ |
|||
|
|||
/* finalize exponent */ |
|||
if (exponent_sign) |
|||
exponent = -exponent; |
|||
exponent = exponent - point_ofs + mantissa_tzeros; |
|||
|
|||
/* finalize mantissa */ |
|||
if (mantissa_sign) |
|||
mantissa = -mantissa; |
|||
|
|||
/* convert to one 64-bit fixed-point value */ |
|||
exponent += decimals; |
|||
if (exponent < 0) |
|||
return false; /* cannot represent values smaller than 10^-decimals */ |
|||
if (exponent >= 18) |
|||
return false; /* cannot represent values larger than or equal to 10^(18-decimals) */ |
|||
|
|||
for (int i=0; i < exponent; ++i) { |
|||
if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL)) |
|||
return false; /* overflow */ |
|||
mantissa *= 10; |
|||
} |
|||
if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND) |
|||
return false; /* overflow */ |
|||
|
|||
if (amount_out) |
|||
*amount_out = mantissa; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void Downcase(std::string& str) |
|||
{ |
|||
std::transform(str.begin(), str.end(), str.begin(), [](char c){return ToLower(c);}); |
|||
} |
|||
|
|||
std::string Capitalize(std::string str) |
|||
{ |
|||
if (str.empty()) return str; |
|||
str[0] = ToUpper(str.front()); |
|||
return str; |
|||
} |
@ -0,0 +1,242 @@ |
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
/**
|
|||
* Utilities for converting data from/to strings. |
|||
*/ |
|||
#ifndef BITCOIN_UTIL_STRENCODINGS_H |
|||
#define BITCOIN_UTIL_STRENCODINGS_H |
|||
|
|||
#include <attributes.h> |
|||
|
|||
#include <cstdint> |
|||
#include <iterator> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
#define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) |
|||
|
|||
/** Used by SanitizeString() */ |
|||
enum SafeChars |
|||
{ |
|||
SAFE_CHARS_DEFAULT, //!< The full set of allowed chars
|
|||
SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset
|
|||
SAFE_CHARS_FILENAME, //!< Chars allowed in filenames
|
|||
SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
|
|||
}; |
|||
|
|||
/**
|
|||
* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email |
|||
* addresses, but avoid anything even possibly remotely dangerous like & or > |
|||
* @param[in] str The string to sanitize |
|||
* @param[in] rule The set of safe chars to choose (default: least restrictive) |
|||
* @return A new string without unsafe chars |
|||
*/ |
|||
std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT); |
|||
std::vector<unsigned char> ParseHex(const char* psz); |
|||
std::vector<unsigned char> ParseHex(const std::string& str); |
|||
signed char HexDigit(char c); |
|||
/* Returns true if each character in str is a hex character, and has an even
|
|||
* number of hex digits.*/ |
|||
bool IsHex(const std::string& str); |
|||
/**
|
|||
* Return true if the string is a hex number, optionally prefixed with "0x" |
|||
*/ |
|||
bool IsHexNumber(const std::string& str); |
|||
std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr); |
|||
std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr); |
|||
std::string EncodeBase64(const unsigned char* pch, size_t len); |
|||
std::string EncodeBase64(const std::string& str); |
|||
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr); |
|||
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr); |
|||
std::string EncodeBase32(const unsigned char* pch, size_t len); |
|||
std::string EncodeBase32(const std::string& str); |
|||
|
|||
void SplitHostPort(std::string in, int &portOut, std::string &hostOut); |
|||
std::string i64tostr(int64_t n); |
|||
std::string itostr(int n); |
|||
int64_t atoi64(const char* psz); |
|||
int64_t atoi64(const std::string& str); |
|||
int atoi(const std::string& str); |
|||
|
|||
/**
|
|||
* Tests if the given character is a decimal digit. |
|||
* @param[in] c character to test |
|||
* @return true if the argument is a decimal digit; otherwise false. |
|||
*/ |
|||
constexpr bool IsDigit(char c) |
|||
{ |
|||
return c >= '0' && c <= '9'; |
|||
} |
|||
|
|||
/**
|
|||
* Tests if the given character is a whitespace character. The whitespace characters |
|||
* are: space, form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal |
|||
* tab ('\t'), and vertical tab ('\v'). |
|||
* |
|||
* This function is locale independent. Under the C locale this function gives the |
|||
* same result as std::isspace. |
|||
* |
|||
* @param[in] c character to test |
|||
* @return true if the argument is a whitespace character; otherwise false |
|||
*/ |
|||
constexpr inline bool IsSpace(char c) noexcept { |
|||
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; |
|||
} |
|||
|
|||
/**
|
|||
* Convert string to signed 32-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
NODISCARD bool ParseInt32(const std::string& str, int32_t *out); |
|||
|
|||
/**
|
|||
* Convert string to signed 64-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
NODISCARD bool ParseInt64(const std::string& str, int64_t *out); |
|||
|
|||
/**
|
|||
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
NODISCARD bool ParseUInt32(const std::string& str, uint32_t *out); |
|||
|
|||
/**
|
|||
* Convert decimal string to unsigned 64-bit integer with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid integer, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
NODISCARD bool ParseUInt64(const std::string& str, uint64_t *out); |
|||
|
|||
/**
|
|||
* Convert string to double with strict parse error feedback. |
|||
* @returns true if the entire string could be parsed as valid double, |
|||
* false if not the entire string could be parsed or when overflow or underflow occurred. |
|||
*/ |
|||
NODISCARD bool ParseDouble(const std::string& str, double *out); |
|||
|
|||
template<typename T> |
|||
std::string HexStr(const T itbegin, const T itend) |
|||
{ |
|||
std::string rv; |
|||
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', |
|||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
|||
rv.reserve(std::distance(itbegin, itend) * 2); |
|||
for(T it = itbegin; it < itend; ++it) |
|||
{ |
|||
unsigned char val = (unsigned char)(*it); |
|||
rv.push_back(hexmap[val>>4]); |
|||
rv.push_back(hexmap[val&15]); |
|||
} |
|||
return rv; |
|||
} |
|||
|
|||
template<typename T> |
|||
inline std::string HexStr(const T& vch) |
|||
{ |
|||
return HexStr(vch.begin(), vch.end()); |
|||
} |
|||
|
|||
/**
|
|||
* Format a paragraph of text to a fixed width, adding spaces for |
|||
* indentation to any added line. |
|||
*/ |
|||
std::string FormatParagraph(const std::string& in, size_t width = 79, size_t indent = 0); |
|||
|
|||
/**
|
|||
* Timing-attack-resistant comparison. |
|||
* Takes time proportional to length |
|||
* of first argument. |
|||
*/ |
|||
template <typename T> |
|||
bool TimingResistantEqual(const T& a, const T& b) |
|||
{ |
|||
if (b.size() == 0) return a.size() == 0; |
|||
size_t accumulator = a.size() ^ b.size(); |
|||
for (size_t i = 0; i < a.size(); i++) |
|||
accumulator |= a[i] ^ b[i%b.size()]; |
|||
return accumulator == 0; |
|||
} |
|||
|
|||
/** Parse number as fixed point according to JSON number syntax.
|
|||
* See http://json.org/number.gif
|
|||
* @returns true on success, false on error. |
|||
* @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger. |
|||
*/ |
|||
NODISCARD bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); |
|||
|
|||
/** Convert from one power-of-2 number base to another. */ |
|||
template<int frombits, int tobits, bool pad, typename O, typename I> |
|||
bool ConvertBits(const O& outfn, I it, I end) { |
|||
size_t acc = 0; |
|||
size_t bits = 0; |
|||
constexpr size_t maxv = (1 << tobits) - 1; |
|||
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; |
|||
while (it != end) { |
|||
acc = ((acc << frombits) | *it) & max_acc; |
|||
bits += frombits; |
|||
while (bits >= tobits) { |
|||
bits -= tobits; |
|||
outfn((acc >> bits) & maxv); |
|||
} |
|||
++it; |
|||
} |
|||
if (pad) { |
|||
if (bits) outfn((acc << (tobits - bits)) & maxv); |
|||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
/**
|
|||
* Converts the given character to its lowercase equivalent. |
|||
* This function is locale independent. It only converts uppercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* @param[in] c the character to convert to lowercase. |
|||
* @return the lowercase equivalent of c; or the argument |
|||
* if no conversion is possible. |
|||
*/ |
|||
constexpr char ToLower(char c) |
|||
{ |
|||
return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c); |
|||
} |
|||
|
|||
/**
|
|||
* Converts the given string to its lowercase equivalent. |
|||
* This function is locale independent. It only converts uppercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* @param[in,out] str the string to convert to lowercase. |
|||
*/ |
|||
void Downcase(std::string& str); |
|||
|
|||
/**
|
|||
* Converts the given character to its uppercase equivalent. |
|||
* This function is locale independent. It only converts lowercase |
|||
* characters in the standard 7-bit ASCII range. |
|||
* @param[in] c the character to convert to uppercase. |
|||
* @return the uppercase equivalent of c; or the argument |
|||
* if no conversion is possible. |
|||
*/ |
|||
constexpr char ToUpper(char c) |
|||
{ |
|||
return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c); |
|||
} |
|||
|
|||
/**
|
|||
* Capitalizes the first character of the given string. |
|||
* This function is locale independent. It only capitalizes the |
|||
* first character of the argument if it has an uppercase equivalent |
|||
* in the standard 7-bit ASCII range. |
|||
* @param[in] str the string to capitalize. |
|||
* @return string with the first letter capitalized. |
|||
*/ |
|||
std::string Capitalize(std::string str); |
|||
|
|||
#endif // BITCOIN_UTIL_STRENCODINGS_H
|
@ -0,0 +1,77 @@ |
|||
// Copyright (c) 2019 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef _BITCOIN_UTIL_VECTOR_H_ |
|||
#define _BITCOIN_UTIL_VECTOR_H_ 1 |
|||
|
|||
#include <vector> |
|||
#include <utility> |
|||
|
|||
/** Compute the number of arguments passed. */ |
|||
inline constexpr unsigned NumArgs() { return 0; } |
|||
template<typename Arg, typename... Args> |
|||
inline constexpr unsigned NumArgs(Arg&&, Args&&... args) { return 1 + NumArgs(std::forward<Args>(args)...); } |
|||
|
|||
/** Push back multiple elements to a container. */ |
|||
template<typename V> |
|||
inline void PushBackMany(V& vec) {} |
|||
template<typename V, typename Arg, typename... Args> |
|||
inline void PushBackMany(V& vec, Arg&& arg, Args&&... args) |
|||
{ |
|||
vec.push_back(std::forward<Arg>(arg)); |
|||
PushBackMany(vec, std::forward<Args>(args)...); |
|||
} |
|||
|
|||
/** Construct a vector with the specified elements.
|
|||
* |
|||
* This is preferable over the list initializing constructor of std::vector: |
|||
* - It automatically infers the element type of the vector. |
|||
* - If arguments are rvalue references, they will be moved into the vector |
|||
* (list initialization always copies). |
|||
* |
|||
* The first argument is used to determine the vector's element type. The |
|||
* other arguments must be convertible to that type. |
|||
*/ |
|||
template<typename Arg, typename... Args> |
|||
inline std::vector<typename std::remove_cv<typename std::remove_reference<Arg>::type>::type> Vector(Arg&& arg, Args&&... args) |
|||
{ |
|||
std::vector<typename std::remove_cv<typename std::remove_reference<Arg>::type>::type> ret; |
|||
ret.reserve(1 + NumArgs(std::forward<Args>(args)...)); |
|||
ret.push_back(std::forward<Arg>(arg)); |
|||
PushBackMany(ret, std::forward<Args>(args)...); |
|||
return ret; |
|||
} |
|||
|
|||
template<typename V> |
|||
inline V Cat(V&& v1, V&& v2) |
|||
{ |
|||
v1.reserve(v1.size() + v2.size()); |
|||
for (auto& arg : v2) { |
|||
v1.push_back(std::move(arg)); |
|||
} |
|||
return std::move(v1); // Explicit move is necessary
|
|||
} |
|||
|
|||
template<typename V> |
|||
inline V Cat(V&& v1, const V& v2) |
|||
{ |
|||
v1.reserve(v1.size() + v2.size()); |
|||
for (auto& arg : v2) { |
|||
v1.push_back(arg); |
|||
} |
|||
return std::move(v1); |
|||
} |
|||
|
|||
template<typename V> |
|||
inline V Cat(const V& v1, const V& v2) |
|||
{ |
|||
V ret = v1; |
|||
ret.reserve(v1.size() + v2.size()); |
|||
for (auto& arg : v2) { |
|||
ret.push_back(arg); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,902 @@ |
|||
// Copyright (c) 2019 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 <string> |
|||
#include <vector> |
|||
#include <unordered_map> |
|||
#include <script/script.h> |
|||
#include <script/miniscript.h> |
|||
#include <span.h> |
|||
#include <util/strencodings.h> |
|||
|
|||
#include "compiler.h" |
|||
|
|||
#include <assert.h> |
|||
|
|||
const CompilerContext COMPILER_CTX; |
|||
|
|||
namespace { |
|||
|
|||
using Node = miniscript::NodeRef<CompilerKey>; |
|||
using NodeType = miniscript::NodeType; |
|||
using miniscript::operator""_mst; |
|||
|
|||
template<typename... Args> |
|||
Node MakeNode(Args&&... args) { return miniscript::MakeNodeRef<CompilerKey>(std::forward<Args>(args)...); } |
|||
|
|||
struct Policy { |
|||
enum class Type { |
|||
NONE, |
|||
|
|||
PK, |
|||
OLDER, |
|||
AFTER, |
|||
HASH160, |
|||
HASH256, |
|||
RIPEMD160, |
|||
SHA256, |
|||
AND, |
|||
OR, |
|||
THRESH |
|||
}; |
|||
|
|||
Type node_type = Type::NONE; |
|||
std::vector<Policy> sub; |
|||
std::vector<unsigned char> data; |
|||
std::vector<CompilerKey> keys; |
|||
std::vector<uint32_t> prob; |
|||
uint32_t k = 0; |
|||
|
|||
~Policy() = default; |
|||
Policy(const Policy& x) = delete; |
|||
Policy& operator=(const Policy& x) = delete; |
|||
Policy& operator=(Policy&& x) = default; |
|||
Policy(Policy&& x) = default; |
|||
|
|||
explicit Policy(Type nt) : node_type(nt) {} |
|||
explicit Policy(Type nt, uint32_t kv) : node_type(nt), k(kv) {} |
|||
explicit Policy(Type nt, std::vector<unsigned char>&& dat) : node_type(nt), data(std::move(dat)) {} |
|||
explicit Policy(Type nt, std::vector<unsigned char>&& dat, uint32_t kv) : node_type(nt), data(std::move(dat)), k(kv) {} |
|||
explicit Policy(Type nt, std::vector<Policy>&& subs) : node_type(nt), sub(std::move(subs)) {} |
|||
explicit Policy(Type nt, std::vector<CompilerKey>&& key) : node_type(nt), keys(std::move(key)) {} |
|||
explicit Policy(Type nt, std::vector<Policy>&& subs, std::vector<uint32_t>&& probs) : node_type(nt), sub(std::move(subs)), prob(std::move(probs)) {} |
|||
explicit Policy(Type nt, std::vector<Policy>&& subs, uint32_t kv) : node_type(nt), sub(std::move(subs)), k(kv) {} |
|||
explicit Policy(Type nt, std::vector<CompilerKey>&& key, uint32_t kv) : node_type(nt), keys(std::move(key)), k(kv) {} |
|||
|
|||
bool operator()() const { return node_type != Type::NONE; } |
|||
}; |
|||
|
|||
bool operator==(const Span<const char>& a, const std::string& b) { return (size_t)a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } |
|||
|
|||
std::vector<unsigned char> Hash(const Span<const char>& in, size_t len) |
|||
{ |
|||
auto unhex = ParseHex(std::string(in.begin(), in.end())); |
|||
if (unhex.size() == len) return unhex; |
|||
return {}; |
|||
} |
|||
|
|||
Policy Parse(Span<const char>& in); |
|||
|
|||
Policy ParseProb(Span<const char>& in, uint32_t& prob) { |
|||
prob = 0; |
|||
while (in.size() && in[0] >= ('0' + (prob == 0)) && in[0] <= '9') { |
|||
prob = prob * 10 + (in[0] - '0'); |
|||
in = in.subspan(1); |
|||
} |
|||
if (prob) { |
|||
if (in.size() == 0 || in[0] != '@') return Policy(Policy::Type::NONE); |
|||
in = in.subspan(1); |
|||
} else { |
|||
prob = 1; |
|||
} |
|||
return Parse(in); |
|||
} |
|||
|
|||
Policy Parse(Span<const char>& in) { |
|||
auto expr = Expr(in); |
|||
if (Func("pk", expr)) { |
|||
CompilerKey key; |
|||
if (COMPILER_CTX.FromString(expr.begin(), expr.end(), key)) { |
|||
return Policy(Policy::Type::PK, Vector(std::move(key))); |
|||
} |
|||
return Policy(Policy::Type::NONE); |
|||
} else if (Func("after", expr)) { |
|||
unsigned long num = std::stoul(std::string(expr.begin(), expr.end())); |
|||
return Policy(Policy::Type::AFTER, num); |
|||
} else if (Func("older", expr)) { |
|||
unsigned long num = std::stoul(std::string(expr.begin(), expr.end())); |
|||
return Policy(Policy::Type::OLDER, num); |
|||
} else if (Func("sha256", expr)) { |
|||
auto hash = Hash(expr, 32); |
|||
if (hash.size()) return Policy(Policy::Type::SHA256, std::move(hash)); |
|||
return Policy(Policy::Type::NONE); |
|||
} else if (Func("ripemd160", expr)) { |
|||
auto hash = Hash(expr, 20); |
|||
if (hash.size()) return Policy(Policy::Type::RIPEMD160, std::move(hash)); |
|||
return Policy(Policy::Type::NONE); |
|||
} else if (Func("hash256", expr)) { |
|||
auto hash = Hash(expr, 32); |
|||
if (hash.size()) return Policy(Policy::Type::HASH256, std::move(hash)); |
|||
return Policy(Policy::Type::NONE); |
|||
} else if (Func("hash160", expr)) { |
|||
auto hash = Hash(expr, 20); |
|||
if (hash.size()) return Policy(Policy::Type::HASH160, std::move(hash)); |
|||
return Policy(Policy::Type::NONE); |
|||
} else if (Func("or", expr)) { |
|||
std::vector<Policy> sub; |
|||
std::vector<uint32_t> prob; |
|||
uint32_t p; |
|||
sub.emplace_back(ParseProb(expr, p)); |
|||
if (!sub.back()()) return Policy(Policy::Type::NONE); |
|||
prob.push_back(p); |
|||
while (expr.size()) { |
|||
if (!Const(",", expr)) return Policy(Policy::Type::NONE); |
|||
sub.emplace_back(ParseProb(expr, p)); |
|||
if (!sub.back()()) return Policy(Policy::Type::NONE); |
|||
prob.push_back(p); |
|||
} |
|||
return Policy(Policy::Type::OR, std::move(sub), std::move(prob)); |
|||
} else if (Func("and", expr)) { |
|||
std::vector<Policy> sub; |
|||
sub.emplace_back(Parse(expr)); |
|||
if (!sub.back()()) return Policy(Policy::Type::NONE); |
|||
while (expr.size()) { |
|||
if (!Const(",", expr)) return Policy(Policy::Type::NONE); |
|||
sub.emplace_back(Parse(expr)); |
|||
if (!sub.back()()) return Policy(Policy::Type::NONE); |
|||
} |
|||
return Policy(Policy::Type::AND, std::move(sub)); |
|||
} else if (Func("thresh", expr) || Func("thres", expr)) { |
|||
auto arg = Expr(expr); |
|||
uint32_t count = std::stoul(std::string(arg.begin(), arg.end())); |
|||
if (count < 1 || count > 16) return Policy(Policy::Type::NONE); |
|||
std::vector<Policy> sub; |
|||
while (expr.size()) { |
|||
if (!Const(",", expr)) return Policy(Policy::Type::NONE); |
|||
sub.emplace_back(Parse(expr)); |
|||
if (!sub.back()()) return Policy(Policy::Type::NONE); |
|||
} |
|||
return Policy(Policy::Type::THRESH, std::move(sub), count); |
|||
} |
|||
|
|||
return Policy(Policy::Type::NONE); |
|||
} |
|||
|
|||
Policy Parse(const std::string& in) { |
|||
try { |
|||
Span<const char> sp(in.data(), in.size()); |
|||
Policy ret = Parse(sp); |
|||
if (sp.size()) return Policy(Policy::Type::NONE); |
|||
return ret; |
|||
} catch (const std::logic_error&) { |
|||
return Policy(Policy::Type::NONE); |
|||
} |
|||
} |
|||
|
|||
struct Strat { |
|||
enum class Type { |
|||
FALSE, TRUE, |
|||
PK, THRESH_M, |
|||
OLDER, AFTER, |
|||
HASH160, HASH256, SHA256, RIPEMD160, |
|||
AND, OR, ANDOR, THRESH, |
|||
WRAP_AS, WRAP_C, WRAP_D, WRAP_V, WRAP_J, WRAP_N, // Several kinds of wrappers that don't change semantics
|
|||
MULTI, // Every subgraph is a separate compilation strategy; try each once
|
|||
CACHE, // sub[0] is the dependency; sub[1] and higher are (possibly self-referential) improvements to try repeatedly until all of them stop improving
|
|||
}; |
|||
|
|||
Type node_type; |
|||
std::vector<const Strat*> sub; |
|||
std::vector<CompilerKey> keys; |
|||
std::vector<unsigned char> data; |
|||
int64_t k = 0; |
|||
double prob; |
|||
|
|||
explicit Strat(Type nt) : node_type(nt) {} |
|||
explicit Strat(Type nt, int64_t kv) : node_type(nt), k(kv) {} |
|||
explicit Strat(Type nt, std::vector<unsigned char> dat) : node_type(nt), data(std::move(dat)) {} |
|||
explicit Strat(Type nt, std::vector<unsigned char> dat, int64_t kv) : node_type(nt), data(std::move(dat)), k(kv) {} |
|||
explicit Strat(Type nt, std::vector<const Strat*> subs) : node_type(nt), sub(std::move(subs)) {} |
|||
explicit Strat(Type nt, std::vector<CompilerKey> key) : node_type(nt), keys(std::move(key)) {} |
|||
explicit Strat(Type nt, std::vector<const Strat*> subs, double probs) : node_type(nt), sub(std::move(subs)), prob(probs) {} |
|||
explicit Strat(Type nt, std::vector<const Strat*> subs, int64_t kv, double probs) : node_type(nt), sub(std::move(subs)), k(kv), prob(probs) {} |
|||
explicit Strat(Type nt, std::vector<const Strat*> subs, int64_t kv) : node_type(nt), sub(std::move(subs)), k(kv) {} |
|||
explicit Strat(Type nt, std::vector<CompilerKey> key, int64_t kv) : node_type(nt), keys(std::move(key)), k(kv) {} |
|||
}; |
|||
|
|||
typedef std::vector<std::unique_ptr<Strat>> StratStore; |
|||
|
|||
template <typename... X> |
|||
const Strat* MakeStrat(StratStore& store, X&&... args) { |
|||
Strat* ret = new Strat(std::forward<X>(args)...); |
|||
store.emplace_back(ret); |
|||
return ret; |
|||
} |
|||
|
|||
template <typename... X> |
|||
Strat* MakeMutStrat(StratStore& store, X&&... args) { |
|||
Strat* ret = new Strat(std::forward<X>(args)...); |
|||
store.emplace_back(ret); |
|||
return ret; |
|||
} |
|||
|
|||
const Strat* ComputeStrategy(const Policy& node, std::unordered_map<const Policy*, const Strat*>& cache, StratStore& store); |
|||
|
|||
const Strat* GetStrategy(const Policy& node, std::unordered_map<const Policy*, const Strat*>& cache, StratStore& store) { |
|||
auto it = cache.find(&node); |
|||
if (it != cache.end()) return it->second; |
|||
auto ret = ComputeStrategy(node, cache, store); |
|||
if (ret) cache.emplace(&node, ret); |
|||
return ret; |
|||
} |
|||
|
|||
static StratStore STRAT_GLOBAL; |
|||
static const Strat* STRAT_FALSE = MakeStrat(STRAT_GLOBAL, Strat::Type::CACHE, Vector(MakeStrat(STRAT_GLOBAL, Strat::Type::FALSE))); |
|||
static const Strat* STRAT_TRUE = MakeStrat(STRAT_GLOBAL, Strat::Type::CACHE, Vector(MakeStrat(STRAT_GLOBAL, Strat::Type::TRUE))); |
|||
|
|||
const Strat* ComputeStrategy(const Policy& node, std::unordered_map<const Policy*, const Strat*>& cache, StratStore& store) { |
|||
std::vector<const Strat*> strats; |
|||
switch (node.node_type) { |
|||
case Policy::Type::NONE: |
|||
return {}; |
|||
case Policy::Type::PK: |
|||
strats.push_back(MakeStrat(store, Strat::Type::PK, node.keys)); |
|||
break; |
|||
case Policy::Type::OLDER: |
|||
strats.push_back(MakeStrat(store, Strat::Type::OLDER, node.k)); |
|||
break; |
|||
case Policy::Type::AFTER: |
|||
strats.push_back(MakeStrat(store, Strat::Type::AFTER, node.k)); |
|||
break; |
|||
case Policy::Type::HASH256: |
|||
strats.push_back(MakeStrat(store, Strat::Type::HASH256, node.data)); |
|||
break; |
|||
case Policy::Type::HASH160: |
|||
strats.push_back(MakeStrat(store, Strat::Type::HASH160, node.data)); |
|||
break; |
|||
case Policy::Type::SHA256: |
|||
strats.push_back(MakeStrat(store, Strat::Type::SHA256, node.data)); |
|||
break; |
|||
case Policy::Type::RIPEMD160: |
|||
strats.push_back(MakeStrat(store, Strat::Type::RIPEMD160, node.data)); |
|||
break; |
|||
case Policy::Type::AND: { |
|||
if (node.sub.size() != 2) return {}; |
|||
const auto left = GetStrategy(node.sub[0], cache, store); |
|||
const auto right = GetStrategy(node.sub[1], cache, store); |
|||
if (!left || !right) return {}; |
|||
strats.push_back(MakeStrat(store, Strat::Type::AND, Vector(left, right))); // and(X,Y)
|
|||
strats.push_back(MakeStrat(store, Strat::Type::ANDOR, Vector(std::move(left), std::move(right), STRAT_FALSE), 1.0)); // or(and(X,Y),0)
|
|||
break; |
|||
} |
|||
case Policy::Type::OR: { |
|||
if (node.sub.size() != 2) return {}; |
|||
double prob = ((double)node.prob[0]) / (node.prob[0] + node.prob[1]); |
|||
const auto left = GetStrategy(node.sub[0], cache, store); |
|||
const auto right = GetStrategy(node.sub[1], cache, store); |
|||
if (!left || !right) return {}; |
|||
if (node.sub[0].node_type == Policy::Type::AND && node.sub[0].sub.size() == 2) { |
|||
const auto leftleft = GetStrategy(node.sub[0].sub[0], cache, store); |
|||
const auto leftright = GetStrategy(node.sub[0].sub[1], cache, store); |
|||
if (!leftleft || !leftright) return {}; |
|||
strats.push_back(MakeStrat(store, Strat::Type::ANDOR, Vector(std::move(leftleft), std::move(leftright), right), prob)); |
|||
} |
|||
if (node.sub[1].node_type == Policy::Type::AND && node.sub[1].sub.size() == 2) { |
|||
const auto rightleft = GetStrategy(node.sub[1].sub[0], cache, store); |
|||
const auto rightright = GetStrategy(node.sub[1].sub[1], cache, store); |
|||
if (!rightleft || !rightright) return {}; |
|||
strats.push_back(MakeStrat(store, Strat::Type::ANDOR, Vector(std::move(rightleft), std::move(rightright), left), 1.0 - prob)); |
|||
} |
|||
strats.push_back(MakeStrat(store, Strat::Type::ANDOR, Vector(left, STRAT_TRUE, right), prob)); |
|||
strats.push_back(MakeStrat(store, Strat::Type::OR, Vector(std::move(left), std::move(right)), prob)); |
|||
break; |
|||
} |
|||
case Policy::Type::THRESH: { |
|||
std::vector<const Strat*> subs; |
|||
std::transform(node.sub.begin(), node.sub.end(), std::back_inserter(subs), [&](const Policy& x){ return GetStrategy(x, cache, store); }); |
|||
for (const auto& s : subs) { |
|||
if (!s) return {}; |
|||
} |
|||
if (std::all_of(node.sub.begin(), node.sub.end(), [&](const Policy& x){ return x.node_type == Policy::Type::PK; })) { |
|||
std::vector<CompilerKey> keys; |
|||
for (const Policy& x : node.sub) { |
|||
keys.push_back(x.keys[0]); |
|||
} |
|||
strats.push_back(MakeStrat(store, Strat::Type::THRESH_M, std::move(keys), node.k)); |
|||
} |
|||
strats.push_back(MakeStrat(store, Strat::Type::THRESH, std::move(subs), node.k, (double)node.k / subs.size())); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (strats.size() != 1) { |
|||
auto sub = std::move(strats); |
|||
strats.push_back(MakeStrat(store, Strat::Type::MULTI, std::move(sub))); |
|||
} |
|||
|
|||
auto ret = MakeMutStrat(store, Strat::Type::CACHE, std::move(strats)); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::WRAP_C, std::vector<const Strat*>{ret})); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::WRAP_V, std::vector<const Strat*>{ret})); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::AND, std::vector<const Strat*>{ret, STRAT_TRUE})); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::WRAP_N, std::vector<const Strat*>{ret})); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::WRAP_D, std::vector<const Strat*>{ret})); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::WRAP_J, std::vector<const Strat*>{ret})); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::OR, std::vector<const Strat*>{ret, STRAT_FALSE}, 1.0)); |
|||
ret->sub.push_back(MakeStrat(store, Strat::Type::WRAP_AS, std::vector<const Strat*>{ret})); |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
struct CostPair { |
|||
double sat; |
|||
double nsat; |
|||
|
|||
constexpr CostPair(double s, double n) : sat(s), nsat(n) {} |
|||
}; |
|||
|
|||
struct Result { |
|||
Node node; |
|||
CostPair pair; |
|||
double cost; |
|||
|
|||
int Compare(double other_cost, const Node& other_node) const { |
|||
if (cost < other_cost) return -1; |
|||
if (cost > other_cost) return 1; |
|||
if (node->ScriptSize() > other_node->ScriptSize()) return -1; |
|||
if (node->ScriptSize() < other_node->ScriptSize()) return 1; |
|||
return 0; |
|||
} |
|||
|
|||
Result(Node&& in_node, const CostPair& in_pair, double in_cost) : node(std::move(in_node)), pair(in_pair), cost(in_cost) {} |
|||
}; |
|||
|
|||
double inline Mul(double coef, double val) { |
|||
if (coef == 0) return 0; |
|||
return coef * val; |
|||
} |
|||
|
|||
typedef std::pair<miniscript::Type, miniscript::Type> TypeFilter; // First element is required type properties; second one is which one we care about
|
|||
|
|||
constexpr TypeFilter ParseFilter(const char *c, size_t len, size_t split) { |
|||
return c[split] == '/' ? TypeFilter{operator"" _mst(c, split), operator"" _mst(c + split + 1, len - split - 1)} : |
|||
split == len ? TypeFilter{operator"" _mst(c, len), ""_mst} : ParseFilter(c, len, split + 1); |
|||
} |
|||
|
|||
constexpr TypeFilter operator"" _mstf(const char* c, size_t len) { return ParseFilter(c, len, 0); } |
|||
|
|||
struct Compilation { |
|||
std::vector<Result> results; |
|||
double p, q; |
|||
int seq = 0; |
|||
|
|||
Compilation(double p_, double q_) : p(p_), q(q_) {} |
|||
Compilation(const Compilation&) = default; |
|||
Compilation(Compilation&&) = default; |
|||
Compilation& operator=(const Compilation&) = default; |
|||
Compilation& operator=(Compilation&&) = default; |
|||
~Compilation() = default; |
|||
|
|||
double Cost(const CostPair& pair, const Node& node) { |
|||
return node->ScriptSize() + Mul(p, pair.sat) + Mul(q, pair.nsat); |
|||
} |
|||
|
|||
void Add(const CostPair& pair, Node node) { |
|||
auto new_typ = node->GetType(); |
|||
double cost = Cost(pair, node); |
|||
if (!(new_typ << "m"_mst)) return; |
|||
if (cost > 10000) return; |
|||
for (const Result& x : results) { |
|||
auto old_typ = x.node->GetType(); |
|||
if (old_typ << new_typ && (x.Compare(cost, node) <= 0)) return; // There is an existing element that's a subtype and better. New item is not useful.
|
|||
} |
|||
// We're at least better in some conditions.
|
|||
results.erase(std::remove_if(results.begin(), results.end(), [&](const Result& x){ |
|||
auto old_typ = x.node->GetType(); |
|||
return (new_typ << old_typ && (x.Compare(cost, node) >= 0)); // Delete existing types which are supertypes of the new type and worse.
|
|||
}), results.end()); |
|||
// Add the new item.
|
|||
results.emplace_back(std::move(node), pair, cost); |
|||
++seq; |
|||
} |
|||
|
|||
void Add(const Result& x) { Add(x.pair, x.node); } |
|||
|
|||
template<typename... X> |
|||
void Add(const CostPair& pair, X&&... args) { Add(pair, MakeNode(std::forward<X>(args)...)); } |
|||
|
|||
std::vector<Result> Query(const TypeFilter typ) const { |
|||
std::map<miniscript::Type, Result> rm; |
|||
for (const Result& x : results) { |
|||
if (x.node->GetType() << typ.first) { |
|||
auto masked = x.node->GetType() & typ.second; |
|||
auto r = rm.emplace(masked, x); |
|||
if (!r.second && x.Compare(r.first->second.cost, r.first->second.node) < 0) r.first->second = x; |
|||
} |
|||
} |
|||
std::vector<Result> ret; |
|||
for (const auto& elem : rm) ret.push_back(std::move(elem.second)); |
|||
return ret; |
|||
} |
|||
}; |
|||
|
|||
|
|||
struct CompilationKey { |
|||
const Strat* strat; |
|||
double p, q; |
|||
|
|||
bool operator<(const CompilationKey& other) const { |
|||
if (strat < other.strat) return true; |
|||
if (strat > other.strat) return false; |
|||
if (p < other.p) return true; |
|||
if (p > other.p) return false; |
|||
return q < other.q; |
|||
} |
|||
|
|||
bool operator==(const CompilationKey& other) const { |
|||
if (strat != other.strat) return false; |
|||
if (p != other.p) return false; |
|||
return q == other.q; |
|||
} |
|||
}; |
|||
|
|||
const Compilation& GetCompilation(const Strat* strat, double p, double q, std::map<CompilationKey, Compilation>& cache); |
|||
void Compile(const Strat* strat, Compilation& compilation, std::map<CompilationKey, Compilation>& cache); |
|||
|
|||
constexpr double INF = std::numeric_limits<double>::infinity(); |
|||
|
|||
CostPair CalcCostPair(NodeType nt, const std::vector<const Result*>& s, double l) { |
|||
double r = 1.0 - l; |
|||
if (nt != NodeType::OR_B && nt != NodeType::OR_D && nt != NodeType::OR_C && nt != NodeType::OR_I && nt != NodeType::THRESH && nt != NodeType::ANDOR && nt != NodeType::THRESH_M) { |
|||
assert(l == 0); |
|||
} |
|||
switch (nt) { |
|||
case NodeType::PK: return {73, 1}; |
|||
case NodeType::PK_H: return {107, 35}; |
|||
case NodeType::OLDER: |
|||
case NodeType::AFTER: |
|||
return {0, INF}; |
|||
case NodeType::HASH256: |
|||
case NodeType::HASH160: |
|||
case NodeType::SHA256: |
|||
case NodeType::RIPEMD160: |
|||
return {33, 33}; |
|||
case NodeType::WRAP_A: |
|||
case NodeType::WRAP_S: |
|||
case NodeType::WRAP_C: |
|||
case NodeType::WRAP_N: |
|||
return s[0]->pair; |
|||
case NodeType::WRAP_D: return {2 + s[0]->pair.sat, 1}; |
|||
case NodeType::WRAP_V: return {s[0]->pair.sat, INF}; |
|||
case NodeType::WRAP_J: return {s[0]->pair.sat, 1}; |
|||
case NodeType::TRUE: return {0, INF}; |
|||
case NodeType::FALSE: return {INF, 0}; |
|||
case NodeType::AND_V: return {s[0]->pair.sat + s[1]->pair.sat, INF}; |
|||
case NodeType::AND_B: return {s[0]->pair.sat + s[1]->pair.sat, s[0]->pair.nsat + s[1]->pair.nsat}; |
|||
case NodeType::OR_B: |
|||
return {Mul(l, s[0]->pair.sat + s[1]->pair.nsat) + Mul(r, s[0]->pair.nsat + s[1]->pair.sat), s[0]->pair.nsat + s[1]->pair.nsat}; |
|||
case NodeType::OR_D: |
|||
case NodeType::OR_C: |
|||
return {Mul(l, s[0]->pair.sat) + Mul(r, s[0]->pair.nsat + s[1]->pair.sat), s[0]->pair.nsat + s[1]->pair.nsat}; |
|||
case NodeType::OR_I: |
|||
return {Mul(l, s[0]->pair.sat + 2) + Mul(r, s[1]->pair.sat + 1), std::min(2 + s[0]->pair.nsat, 1 + s[1]->pair.nsat)}; |
|||
case NodeType::ANDOR: |
|||
return {Mul(l, s[0]->pair.sat + s[1]->pair.sat) + Mul(r, s[0]->pair.nsat + s[2]->pair.sat), s[0]->pair.nsat + s[2]->pair.nsat}; |
|||
case NodeType::THRESH_M: return CostPair{1.0 + l * 73.0, 1.0 + l}; |
|||
case NodeType::THRESH: { |
|||
double sat = 0.0, nsat = 0.0; |
|||
for (const auto& sub : s) { |
|||
sat += sub->pair.sat; |
|||
nsat += sub->pair.nsat; |
|||
} |
|||
return CostPair{Mul(l, sat) + Mul(r, nsat), nsat}; |
|||
} |
|||
} |
|||
throw std::runtime_error("Computing CostPair of unknown nodetype"); |
|||
} |
|||
|
|||
std::pair<std::vector<double>, std::vector<double>> GetPQs(NodeType nt, double p, double q, double l, int m) { |
|||
static const std::pair<std::vector<double>, std::vector<double>> NONE; |
|||
double r = 1.0 - l; |
|||
switch (nt) { |
|||
case NodeType::TRUE: |
|||
case NodeType::FALSE: |
|||
case NodeType::PK: |
|||
case NodeType::PK_H: |
|||
case NodeType::THRESH_M: |
|||
case NodeType::OLDER: |
|||
case NodeType::AFTER: |
|||
case NodeType::HASH256: |
|||
case NodeType::HASH160: |
|||
case NodeType::SHA256: |
|||
case NodeType::RIPEMD160: |
|||
return NONE; |
|||
case NodeType::WRAP_A: |
|||
case NodeType::WRAP_S: |
|||
case NodeType::WRAP_C: |
|||
case NodeType::WRAP_N: |
|||
return {{p}, {q}}; |
|||
case NodeType::WRAP_D: |
|||
case NodeType::WRAP_V: |
|||
case NodeType::WRAP_J: |
|||
return {{p}, {0}}; |
|||
case NodeType::AND_V: |
|||
case NodeType::AND_B: |
|||
return {{p, p}, {q, q}}; |
|||
case NodeType::OR_B: return {{l*p, r*p}, {r*p + q, l*p + q}}; |
|||
case NodeType::OR_D: return {{l*p, r*p}, {r*p + q, q}}; |
|||
case NodeType::OR_C: return {{l*p, r*p}, {r*p, 0}}; |
|||
case NodeType::OR_I: return {{l*p, r*p}, {m == 0 ? q : 0, m == 1 ? q : 0}}; |
|||
case NodeType::ANDOR: return {{l*p, l*p, r*p}, {q + r*p, 0, q}}; |
|||
case NodeType::THRESH: return {std::vector<double>(m, p * l), std::vector<double>(m, q + p * r)}; |
|||
} |
|||
assert(false); |
|||
return NONE; |
|||
} |
|||
|
|||
typedef std::vector<std::vector<TypeFilter>> TypeFilters; |
|||
|
|||
const TypeFilters& GetTypeFilter(NodeType nt) { |
|||
static const TypeFilters FILTER_NO{{}}; |
|||
static const TypeFilters FILTER_WRAP_A{{"B/udfems"_mstf}}; |
|||
static const TypeFilters FILTER_WRAP_S{{"Bo/udfemsx"_mstf}}; |
|||
static const TypeFilters FILTER_WRAP_C{{"K/onde"_mstf}}; |
|||
static const TypeFilters FILTER_WRAP_D{{"V/zfms"_mstf}}; |
|||
static const TypeFilters FILTER_WRAP_V{{"B/zonmsx"_mstf}}; |
|||
static const TypeFilters FILTER_WRAP_J{{"Bn/oufms"_mstf}}; |
|||
static const TypeFilters FILTER_WRAP_N{{"B/zondfems"_mstf}}; |
|||
static const TypeFilters FILTER_AND_V{ |
|||
{"V/nzoms"_mstf, "B/unzofmsx"_mstf}, |
|||
{"V/nsoms"_mstf, "K/unzofmsx"_mstf}, |
|||
{"V/nzoms"_mstf, "V/unzofmsx"_mstf} |
|||
}; |
|||
static const TypeFilters FILTER_AND_B{{"B/zondfems"_mstf, "W/zondfems"_mstf}}; |
|||
static const TypeFilters FILTER_OR_B{{"Bde/zoms"_mstf, "Wde/zoms"_mstf}}; |
|||
static const TypeFilters FILTER_OR_D{{"Bdue/zoms"_mstf, "B/zoudfems"_mstf}}; |
|||
static const TypeFilters FILTER_OR_C{{"Bdue/zoms"_mstf, "V/zoms"_mstf}}; |
|||
static const TypeFilters FILTER_OR_I{ |
|||
{"V/zudfems"_mstf, "V/zudfems"_mstf}, |
|||
{"B/zudfems"_mstf, "B/zudfems"_mstf}, |
|||
{"K/zudfems"_mstf, "K/zudfems"_mstf} |
|||
}; |
|||
static const TypeFilters FILTER_ANDOR{ |
|||
{"Bdue/zoms"_mstf, "B/zoufms"_mstf, "B/zoudfems"_mstf}, |
|||
{"Bdue/zoms"_mstf, "K/zoufms"_mstf, "K/zoudfems"_mstf}, |
|||
{"Bdue/zoms"_mstf, "V/zoufms"_mstf, "V/zoudfems"_mstf} |
|||
}; |
|||
|
|||
switch (nt) { |
|||
case NodeType::TRUE: |
|||
case NodeType::FALSE: |
|||
case NodeType::PK: |
|||
case NodeType::PK_H: |
|||
case NodeType::THRESH_M: |
|||
case NodeType::OLDER: |
|||
case NodeType::AFTER: |
|||
case NodeType::HASH256: |
|||
case NodeType::HASH160: |
|||
case NodeType::SHA256: |
|||
case NodeType::RIPEMD160: |
|||
return FILTER_NO; |
|||
case NodeType::WRAP_A: return FILTER_WRAP_A; |
|||
case NodeType::WRAP_S: return FILTER_WRAP_S; |
|||
case NodeType::WRAP_C: return FILTER_WRAP_C; |
|||
case NodeType::WRAP_D: return FILTER_WRAP_D; |
|||
case NodeType::WRAP_V: return FILTER_WRAP_V; |
|||
case NodeType::WRAP_J: return FILTER_WRAP_J; |
|||
case NodeType::WRAP_N: return FILTER_WRAP_N; |
|||
case NodeType::AND_V: return FILTER_AND_V; |
|||
case NodeType::AND_B: return FILTER_AND_B; |
|||
case NodeType::OR_B: return FILTER_OR_B; |
|||
case NodeType::OR_C: return FILTER_OR_C; |
|||
case NodeType::OR_D: return FILTER_OR_D; |
|||
case NodeType::OR_I: return FILTER_OR_I; |
|||
case NodeType::ANDOR: return FILTER_ANDOR; |
|||
case NodeType::THRESH: break; |
|||
} |
|||
assert(false); |
|||
return FILTER_NO; |
|||
} |
|||
|
|||
template<typename... Args> |
|||
void AddInner(Compilation& compilation, std::map<CompilationKey, Compilation>& cache, NodeType nt, const std::vector<const Result*>& resp, double prob, Args&&... args) { |
|||
std::vector<Node> subs; |
|||
for (const Result* res : resp) subs.push_back(res->node); |
|||
compilation.Add(CalcCostPair(nt, resp, prob), nt, std::move(subs), std::forward<Args>(args)...); |
|||
} |
|||
|
|||
template<typename... Args> |
|||
void Add(Compilation& compilation, std::map<CompilationKey, Compilation>& cache, NodeType nt, const std::vector<const Strat*>& s, double prob, int m, Args&&... args) { |
|||
auto pqs = GetPQs(nt, compilation.p, compilation.q, prob, m); |
|||
auto filter = GetTypeFilter(nt); |
|||
std::vector<const Result*> resp; |
|||
resp.resize(s.size()); |
|||
for (size_t j = 0; j < filter.size(); ++j) { |
|||
std::vector<std::vector<Result>> res; |
|||
uint32_t num_comb = 1; |
|||
assert(s.size() == filter[j].size()); |
|||
for (size_t i = 0; i < s.size(); ++i) { |
|||
const Compilation& subcomp = GetCompilation(s[i], pqs.first[i], pqs.second[i], cache); |
|||
res.push_back(subcomp.Query(filter[j][i])); |
|||
num_comb *= res.back().size(); |
|||
} |
|||
for (uint32_t comb = 0; comb < num_comb; ++comb) { |
|||
uint32_t c = comb; |
|||
for (size_t i = 0; i < s.size(); ++i) { |
|||
resp[i] = &res[i][c % res[i].size()]; |
|||
c /= res[i].size(); |
|||
} |
|||
if (comb + 1 == num_comb) { |
|||
AddInner(compilation, cache, nt, resp, prob, std::forward<Args>(args)...); |
|||
} else { |
|||
AddInner(compilation, cache, nt, resp, prob, args...); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
const Compilation& GetCompilation(const Strat* strat, double p, double q, std::map<CompilationKey, Compilation>& cache) { |
|||
assert(strat->node_type == Strat::Type::CACHE); |
|||
CompilationKey key{strat, p, q}; |
|||
auto it = cache.find(key); |
|||
if (it != cache.end()) { |
|||
assert(it->second.p == p); |
|||
assert(it->second.q == q); |
|||
return it->second; |
|||
} |
|||
Compilation new_entry(p, q); |
|||
assert(strat->sub.size() > 0); |
|||
Compile(strat->sub[0], new_entry, cache); |
|||
auto it2 = cache.emplace(key, std::move(new_entry)); |
|||
assert(it2.second); |
|||
Compilation &result = it2.first->second; |
|||
if (strat->sub.size() > 1) { |
|||
size_t last = 1, pos = 1; |
|||
do { |
|||
int prevseq = result.seq; |
|||
Compile(strat->sub[pos], result, cache); |
|||
if (result.seq != prevseq) last = pos; |
|||
++pos; |
|||
if (pos == strat->sub.size()) pos = 1; |
|||
} while (pos != last); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
void Compile(const Strat* strat, Compilation& compilation, std::map<CompilationKey, Compilation>& cache) { |
|||
double p = compilation.p, q = compilation.q; |
|||
switch (strat->node_type) { |
|||
case Strat::Type::MULTI: |
|||
for (const auto& x : strat->sub) { |
|||
Compile(x, compilation, cache); |
|||
} |
|||
return; |
|||
case Strat::Type::CACHE: { |
|||
const Compilation& sub = GetCompilation(strat, p, q, cache); |
|||
for (const Result& x : sub.results) { |
|||
compilation.Add(x); |
|||
} |
|||
return; |
|||
} |
|||
case Strat::Type::FALSE: |
|||
Add(compilation, cache, NodeType::FALSE, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::TRUE: |
|||
Add(compilation, cache, NodeType::TRUE, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::AFTER: |
|||
case Strat::Type::OLDER: { |
|||
Add(compilation, cache, strat->node_type == Strat::Type::OLDER ? NodeType::OLDER : NodeType::AFTER, strat->sub, 0, 0, strat->k); |
|||
return; |
|||
} |
|||
case Strat::Type::HASH160: { |
|||
Add(compilation, cache, NodeType::HASH160, strat->sub, 0, 0, strat->data); |
|||
return; |
|||
} |
|||
case Strat::Type::HASH256: { |
|||
Add(compilation, cache, NodeType::HASH256, strat->sub, 0, 0, strat->data); |
|||
return; |
|||
} |
|||
case Strat::Type::RIPEMD160: { |
|||
Add(compilation, cache, NodeType::RIPEMD160, strat->sub, 0, 0, strat->data); |
|||
return; |
|||
} |
|||
case Strat::Type::SHA256: { |
|||
Add(compilation, cache, NodeType::SHA256, strat->sub, 0, 0, strat->data); |
|||
return; |
|||
} |
|||
case Strat::Type::PK: { |
|||
Add(compilation, cache, NodeType::PK, strat->sub, 0, 0, strat->keys); |
|||
Add(compilation, cache, NodeType::PK_H, strat->sub, 0, 0, strat->keys); |
|||
return; |
|||
} |
|||
case Strat::Type::THRESH_M: |
|||
Add(compilation, cache, NodeType::THRESH_M, strat->sub, strat->k, 0, strat->keys, strat->k); |
|||
return; |
|||
case Strat::Type::WRAP_AS: |
|||
Add(compilation, cache, NodeType::WRAP_A, strat->sub, 0, 0); |
|||
Add(compilation, cache, NodeType::WRAP_S, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::WRAP_C: |
|||
Add(compilation, cache, NodeType::WRAP_C, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::WRAP_D: |
|||
Add(compilation, cache, NodeType::WRAP_D, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::WRAP_N: |
|||
Add(compilation, cache, NodeType::WRAP_N, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::WRAP_J: |
|||
Add(compilation, cache, NodeType::WRAP_J, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::WRAP_V: |
|||
Add(compilation, cache, NodeType::WRAP_V, strat->sub, 0, 0); |
|||
return; |
|||
case Strat::Type::AND: { |
|||
const auto& sub = strat->sub; |
|||
const std::vector<const Strat*> rev{sub[1], sub[0]}; |
|||
if (q == 0) { |
|||
Add(compilation, cache, NodeType::AND_V, sub, 0, 0); |
|||
Add(compilation, cache, NodeType::AND_V, rev, 0, 0); |
|||
} |
|||
Add(compilation, cache, NodeType::AND_B, sub, 0, 0); |
|||
Add(compilation, cache, NodeType::AND_B, rev, 0, 0); |
|||
return; |
|||
} |
|||
case Strat::Type::OR: { |
|||
const auto& sub = strat->sub; |
|||
const std::vector<const Strat*> rev{sub[1], sub[0]}; |
|||
double l = strat->prob, r = 1.0 - l; |
|||
if (q == 0) { |
|||
Add(compilation, cache, NodeType::OR_C, sub, l, 0); |
|||
Add(compilation, cache, NodeType::OR_C, rev, r, 0); |
|||
} |
|||
Add(compilation, cache, NodeType::OR_B, sub, l, 0); |
|||
Add(compilation, cache, NodeType::OR_B, rev, r, 0); |
|||
Add(compilation, cache, NodeType::OR_D, sub, l, 0); |
|||
Add(compilation, cache, NodeType::OR_D, rev, r, 0); |
|||
Add(compilation, cache, NodeType::OR_I, sub, l, 0); |
|||
Add(compilation, cache, NodeType::OR_I, rev, r, 0); |
|||
Add(compilation, cache, NodeType::OR_I, sub, l, 1); |
|||
Add(compilation, cache, NodeType::OR_I, rev, r, 1); |
|||
return; |
|||
} |
|||
case Strat::Type::ANDOR: { |
|||
const auto& sub = strat->sub; |
|||
const std::vector<const Strat*> rev{sub[1], sub[0], sub[2]}; |
|||
double l = strat->prob; |
|||
Add(compilation, cache, NodeType::ANDOR, sub, l, 0); |
|||
Add(compilation, cache, NodeType::ANDOR, rev, l, 0); |
|||
return; |
|||
} |
|||
case Strat::Type::THRESH: { |
|||
auto pqs = GetPQs(NodeType::THRESH, p, q, strat->prob, (int)strat->sub.size()); |
|||
std::vector<Result> Bs, Ws; |
|||
int B_pos = -1; |
|||
double cost_diff = -1.0; |
|||
for (size_t i = 0; i < strat->sub.size(); ++i) { |
|||
const Compilation& comp = GetCompilation(strat->sub[i], pqs.first[i], pqs.second[i], cache); |
|||
auto res_B = comp.Query("Bemdu"_mstf); |
|||
if (res_B.size() == 0) {fprintf(stderr, "Cannot compile arg=%i as B\n", (int)i); return; } |
|||
assert(res_B.size() == 1); |
|||
Bs.push_back(std::move(res_B[0])); |
|||
auto res_W = comp.Query("Wemdu"_mstf); |
|||
if (res_W.size() == 0) {fprintf(stderr, "Cannot compile arg=%i as W\n", (int)i); return; } |
|||
assert(res_W.size() == 1); |
|||
Ws.push_back(std::move(res_W[0])); |
|||
if (Ws.back().cost - Bs.back().cost > cost_diff) { |
|||
cost_diff = Ws.back().cost - Bs.back().cost; |
|||
B_pos = i; |
|||
} |
|||
} |
|||
std::vector<const Result*> resp; |
|||
resp.push_back(&Bs[B_pos]); |
|||
for (size_t i = 0; i < strat->sub.size(); ++i) { |
|||
if ((int)i != B_pos) resp.push_back(&Ws[i]); |
|||
} |
|||
AddInner(compilation, cache, NodeType::THRESH, resp, strat->prob, strat->k); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/*
|
|||
std::string DebugNode(const Node& node) { |
|||
switch (node->nodetype) { |
|||
case NodeType::PK: return "pk"; |
|||
case NodeType::PK_H: return "pk_h"; |
|||
case NodeType::THRESH_M: return "thresh_m(" + std::to_string(node->k) + " of " + std::to_string(node->keys.size()) + ")"; |
|||
case NodeType::AFTER: return "after"; |
|||
case NodeType::OLDER: return "older"; |
|||
case NodeType::SHA256: return "sha256"; |
|||
case NodeType::HASH256: return "hash256"; |
|||
case NodeType::RIPEMD160: return "ripemd160"; |
|||
case NodeType::HASH160: return "hash160"; |
|||
case NodeType::TRUE: return "1"; |
|||
case NodeType::FALSE: return "0"; |
|||
case NodeType::WRAP_C: return "c:"; |
|||
case NodeType::WRAP_A: return "a:"; |
|||
case NodeType::WRAP_S: return "s:"; |
|||
case NodeType::WRAP_V: return "v:"; |
|||
case NodeType::WRAP_D: return "d:"; |
|||
case NodeType::WRAP_J: return "j:"; |
|||
case NodeType::WRAP_N: return "n:"; |
|||
case NodeType::AND_V: return "and_v"; |
|||
case NodeType::AND_B: return "and_b"; |
|||
case NodeType::OR_B: return "or_b"; |
|||
case NodeType::OR_C: return "or_c"; |
|||
case NodeType::OR_D: return "or_d"; |
|||
case NodeType::OR_I: return "or_i"; |
|||
case NodeType::ANDOR: return "andor"; |
|||
case NodeType::THRESH: return "thresh(" + std::to_string(node->k) + " of " + std::to_string(node->subs.size()) + ")"; |
|||
} |
|||
assert(false); |
|||
return ""; |
|||
} |
|||
|
|||
void PrintCompilationResult(int level, const Result& res) { |
|||
for (int i = 0; i < level; ++i) fprintf(stderr, " "); |
|||
fprintf(stderr, "* %s p=%f q=%f scriptlen=%i sat=%f nsat=%f cost=%f\n", DebugNode(res.node).c_str(), res.p, res.q, (int)res.node->ScriptSize(), res.pair.sat, res.pair.nsat, res.cost); |
|||
assert(res.subs.size() == res.node->subs.size()); |
|||
for (size_t j = 0; j < res.subs.size(); ++j) { |
|||
PrintCompilationResult(level + 1, *(res.subs[j])); |
|||
} |
|||
} |
|||
*/ |
|||
|
|||
} // namespace
|
|||
|
|||
bool Compile(const std::string& policy, miniscript::NodeRef<CompilerKey>& ret, double& avgcost) { |
|||
Policy pol = Parse(policy); |
|||
if (!pol()) return false; |
|||
|
|||
const Strat* strat; |
|||
StratStore store; |
|||
{ |
|||
std::unordered_map<const Policy*, const Strat*> cache; |
|||
strat = ComputeStrategy(pol, cache, store); |
|||
} |
|||
if (!strat) return false; |
|||
|
|||
std::map<CompilationKey, Compilation> cache; |
|||
const Compilation& compilation = GetCompilation(strat, 1.0, 0.0, cache); |
|||
|
|||
auto res = compilation.Query("Bms"_mstf); |
|||
bool ok = false; |
|||
if (res.size() == 1) { |
|||
ret = std::move(res[0].node); |
|||
avgcost = res[0].pair.sat; |
|||
ok = true; |
|||
} |
|||
|
|||
return ok; |
|||
} |
|||
|
|||
std::string Expand(std::string str) { |
|||
while (true) { |
|||
auto pos = str.find("(H)"); |
|||
if (pos == std::string::npos) break; |
|||
str.replace(pos, 3, "(8888888888888888888888888888888888888888888888888888888888888888)"); |
|||
} |
|||
while (true) { |
|||
auto pos = str.find("(h)"); |
|||
if (pos == std::string::npos) break; |
|||
str.replace(pos, 3, "(9999999999999999999999999999999999999999)"); |
|||
} |
|||
return str; |
|||
} |
|||
|
|||
std::string Abbreviate(std::string str) { |
|||
while (true) { |
|||
auto pos = str.find("(8888888888888888888888888888888888888888888888888888888888888888)"); |
|||
if (pos == std::string::npos) break; |
|||
str.replace(pos, 66, "(H)"); |
|||
} |
|||
while (true) { |
|||
auto pos = str.find("(9999999999999999999999999999999999999999)"); |
|||
if (pos == std::string::npos) break; |
|||
str.replace(pos, 42, "(h)"); |
|||
} |
|||
return str; |
|||
} |
@ -0,0 +1,33 @@ |
|||
// Copyright (c) 2019 The Bitcoin Core developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef _BITCOIN_SCRIPT_MINISCRIPT_COMPILER_H_ |
|||
#define _BITCOIN_SCRIPT_MINISCRIPT_COMPILER_H_ |
|||
|
|||
#include <script/miniscript.h> |
|||
|
|||
#include <string> |
|||
|
|||
struct CompilerKey { |
|||
std::string name; |
|||
|
|||
}; |
|||
|
|||
struct CompilerContext { |
|||
typedef CompilerKey Key; |
|||
|
|||
std::string ToString(const Key& key) const { return key.name; } |
|||
|
|||
template<typename I> |
|||
bool FromString(I first, I last, Key& key) const { key.name = std::string(first, last); return true; } |
|||
}; |
|||
|
|||
extern const CompilerContext COMPILER_CTX; |
|||
|
|||
bool Compile(const std::string& policy, miniscript::NodeRef<CompilerKey>& ret, double& avgcost); |
|||
|
|||
std::string Expand(std::string str); |
|||
std::string Abbreviate(std::string str); |
|||
|
|||
#endif |
@ -0,0 +1,580 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<!-- Required meta tags --> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
|||
<title>Miniscript</title> |
|||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous"> |
|||
<!-- Optional theme --> |
|||
<link rel="stylesheet" href="style.css"> |
|||
<style> |
|||
.monospace { |
|||
font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace; |
|||
} |
|||
a.demo_link { |
|||
text-decoration-style: dashed; |
|||
text-underline-position: under; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<script src="miniscript.js"></script> |
|||
<script> |
|||
em_miniscript_compile = Module.cwrap('miniscript_compile', 'none', ['string', 'number', 'number', 'number', 'number']); |
|||
em_miniscript_analyze = Module.cwrap('miniscript_analyze', 'none', ['string', 'number', 'number']); |
|||
|
|||
function miniscript_compile() { |
|||
document.getElementById("descout").value = "Compiling..."; |
|||
document.getElementById("spendout").innerHTML = "Compiling..."; |
|||
window.setTimeout(function() { |
|||
src = document.getElementById("source").value; |
|||
var descout = Module._malloc(1000); |
|||
var spendout = Module._malloc(50000); |
|||
em_miniscript_compile(src, descout, 1000, spendout, 50000); |
|||
document.getElementById("descout").value = Module.UTF8ToString(descout); |
|||
document.getElementById("spendout").innerHTML = Module.UTF8ToString(spendout); |
|||
Module._free(descout) |
|||
Module._free(spendout) |
|||
}); |
|||
} |
|||
|
|||
function miniscript_analyze() { |
|||
document.getElementById("spendout").innerHTML = "Analyzing..."; |
|||
window.setTimeout(function() { |
|||
src = document.getElementById("descout").value; |
|||
var spendout = Module._malloc(50000); |
|||
em_miniscript_analyze(src, spendout, 50000); |
|||
document.getElementById("spendout").innerHTML = Module.UTF8ToString(spendout); |
|||
Module._free(spendout) |
|||
}); |
|||
} |
|||
|
|||
</script> |
|||
|
|||
<div class="container" style="margin-top:50px"> |
|||
|
|||
<div class="card mb-3 text-left"> |
|||
<h3 class="card-header">Miniscript introduction</h3> |
|||
<div class="card-block"> |
|||
<p> |
|||
Some explanation goes here. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card mb-3 text-left"> |
|||
<h3 class="card-header">Compiler demo</h3> |
|||
<div class="card-block"> |
|||
|
|||
<textarea rows="1" cols="100" id="source"> |
|||
and(pk(C),or(pk(C),or(9@pk(C),older(1000)))) |
|||
</textarea><br/> |
|||
<button type="button" onclick="miniscript_compile();">Compile</button> |
|||
Supported elements: |
|||
<ul> |
|||
<li><b>pk(HEX)</b>: Require public key HEX to sign.</li> |
|||
<li><b>time(NUMBER)</b>: Require that a relative time NUMBER has passed since creating the output.</li> |
|||
<li><b>hash(HEX)</b>: Require that the SHA256 preimage of HEX is revealed.</li> |
|||
<li><b>and(EXPR,EXPR)</b>: Require that both subexpressions are satisfied.</li> |
|||
<li><b>or([N@]EXPR,[N@]EXPR)</b>: Require that one of the subexpressions is satisfied. The numbers N indicate the relative probability of each of the subexpressions.</li> |
|||
<li><b>thresh(NUM,EXPR,EXPR,...)</b>: Require that NUM out of the following subexpressions are satisfied.</li> |
|||
</ul> |
|||
Shorthands: |
|||
<ul> |
|||
<li><b>C</b>: A dummy compressed public key (usable as argument to pk or multi)</li> |
|||
<li><b>U</b>: A dummy uncompressed public key (usable as argument to pk or multi)</li> |
|||
<li><b>H</b>: A dummy hash (usable as argument to hash)</li> |
|||
</ul> |
|||
|
|||
<hr/> |
|||
<b>Miniscript output</b><br/> |
|||
<textarea rows="4" cols="100" id="descout"></textarea> |
|||
<button type="button" onclick="miniscript_analyze();">Analyze</button> |
|||
|
|||
<hr/> |
|||
<b>Analysis</b><br/> |
|||
<a id="spendout"></a> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<div class="card text-left"> |
|||
<h3 class="card-header">Miniscript reference</h3> |
|||
<div class="card-block"> |
|||
|
|||
<table border="1"> |
|||
<tr><th>Type</th><th>Shorthand</th><th>Behavior under honest inputs</th><th>Behavior under adverserial inputs</th></tr> |
|||
<tr><td>Base</td><td>B</td><td>Takes its inputs from the top of the stack. Pushes a nonzero value of up to 4 bytes if the condition is satisfied, exactly zero otherwise.</td><td>Pushes zero onto the stack, or aborts.</td></tr> |
|||
<tr><td>Key</td><td>K</td><td>Takes its inputs from the top of the stack. Pushes a public key with which a checksig is to be done onto the stack.</td><td>Aborts.</td></tr> |
|||
<tr><td>Verify</td><td>V</td><td>Takes its inputs from the top of the stack, which must satisfy the condition. Does not push anything onto the stack.</td><td>Aborts.</td></tr> |
|||
<tr><td>Wrapped</td><td>W</td><td>Takes from the stack its inputs + element X at the top. If the inputs satisfy the condition, [t X] or [X t] is pushed, where t is a nonzero value of up to 4 bytes. If not, [0 X] or [X 0] is pushed.</td><td>Pushes [0 X] or [X 0] onto the stack, or aborts.</td></tr> |
|||
</table> |
|||
|
|||
In addition, every expression can have zero or more of the following properties used for reasoning about correctness:<ul> |
|||
<li><b>z</b> "zero": Known to consume exactly zero stack elements. Conflicts with "o".</li> |
|||
<li><b>o</b> "one": Known to consume exactly one stack element. Conflicts with "z".</li> |
|||
<li><b>n</b> "nonzero": Known to consume at least one stack element, and the top element is never required to be 0 to satisfy the condition. Conflicts with "z". Impossible for W.</li> |
|||
<li><b>d</b> "dissatisfiable": There is a guaranteed way to this dissatisfy the expression. Impossible for V.</li> |
|||
<li><b>u</b> "unit": Satisfaction always results in an exact 1 on the stack (as opposed to arbitrary nonzero value). Impossible for V. Implied by K</li> |
|||
</ul> |
|||
|
|||
There are also four more properties that let us reason about nonmalleability:<ul> |
|||
<li><b>e</b> "expr": A unique way to dissatisfy this expression exists which cannot be modified into another dissatisfaction by an attacker. Conflicts with "f". Implies "d". Impossible for V.</li> |
|||
<li><b>f</b> "forced": There is no way to dissatisfy the expression, either under honest or malicious input. Conflicts with "d" and "e". Implied by V.</li> |
|||
<li><b>s</b> "safe": Satisfying this expression requires at least one signature. This means a dissatisfaction cannot be converted into a satisfaction for this expression. Implied by K.</li> |
|||
<li><b>m</b> "nonmalleable": For every way the condition can be met, a unique satisfaction exists which cannot be modified into another satisfaction by an attacker. Implied by Z.</li> |
|||
</ul> |
|||
|
|||
<h3>Legend</h3> |
|||
|
|||
<table border="1"> |
|||
<tr> |
|||
<th>Semantics</th> |
|||
<th>Node</th> |
|||
<th>Script</th> |
|||
<th>nsat</th> |
|||
<th>sat (X,Y)</th> |
|||
<th>sat Z</th> |
|||
<th>Types</th> |
|||
<th>Req.</th> |
|||
<th>Corr. prop.</th> |
|||
<th>Mall. prop.</th> |
|||
</tr> |
|||
<tr> |
|||
<td rowspan="2">check(key)</td> |
|||
<td>pk(key)</td> |
|||
<td>key</td> |
|||
<td>0</td> |
|||
<td>sig</td> |
|||
<td>-</td> |
|||
<td>K</td> |
|||
<td></td> |
|||
<td>o, n, u, d</td> |
|||
<td>e, m, s</td> |
|||
</tr> |
|||
<tr> |
|||
<td>pk_h(keyhash)</td> |
|||
<td>DUP HASH160 keyhash EQUALVERFIFY</td> |
|||
<td>0 key</td> |
|||
<td>sig key</td> |
|||
<td>-</td> |
|||
<td>K</td> |
|||
<td></td> |
|||
<td>n, u, d</td> |
|||
<td>e, m, s</td> |
|||
</tr> |
|||
<tr> |
|||
<td>nSequence ≥ n (and compatible)</td> |
|||
<td>older(n)</td> |
|||
<td>n CHECKSEQUENCEVERIFY</td> |
|||
<td>-</td> |
|||
<td>[]</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td>n ≥ 1</td> |
|||
<td>z</td> |
|||
<td>f, m</td> |
|||
</tr> |
|||
<tr> |
|||
<td>nLockTime ≥ n (and compaitlbe)</td> |
|||
<td>after(n)</td> |
|||
<td>n CHECKLOCKTIMEVERIFY</td> |
|||
<td>-</td> |
|||
<td>[]</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td>2<sup>31</sup> > n ≥ 1</td> |
|||
<td>z</td> |
|||
<td>f, m</td> |
|||
</tr> |
|||
<tr> |
|||
<td>len(x) = 32 and SHA256(x) = h</td> |
|||
<td>sha256(h)</td> |
|||
<td>SIZE 32 EQUALVERIFY SHA256 h EQUAL</td> |
|||
<td>000...</td> |
|||
<td>x</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td></td> |
|||
<td>o, n, u, d</td> |
|||
<td>m</td> |
|||
</tr> |
|||
<tr> |
|||
<td>len(x) = 32 and HASH256(x) = h</td> |
|||
<td>hash256(h)</td> |
|||
<td>SIZE 32 EQUALVERIFY HASH256 h EQUAL</td> |
|||
<td>000...</td> |
|||
<td>x</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td></td> |
|||
<td>o, n, u, d</td> |
|||
<td>m</td> |
|||
</tr> |
|||
<tr> |
|||
<td>len(x) = 32 and RIPEMD160(x) = h</td> |
|||
<td>ripemd160(h)</td> |
|||
<td>SIZE 32 EQUALVERIFY RIPEMD160 h EQUAL</td> |
|||
<td>000...</td> |
|||
<td>x</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td></td> |
|||
<td>o, n, u, d</td> |
|||
<td>m</td> |
|||
</tr> |
|||
<tr> |
|||
<td>len(x) = 32 and HASH160(x) = h</td> |
|||
<td>hash160(h)</td> |
|||
<td>SIZE 32 EQUALVERIFY HASH160 h EQUAL</td> |
|||
<td>000...</td> |
|||
<td>x</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td></td> |
|||
<td>o, n, u, d</td> |
|||
<td>m</td> |
|||
</tr> |
|||
<tr> |
|||
<td rowspan="3">X and Y</td> |
|||
<td>and_v(X,Y)</td> |
|||
<td>[X] [Y]</td> |
|||
<td>-</td> |
|||
<td>sat<sub>Y</sub> sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>B=V<sub>X</sub>B<sub>Y</sub><br />K=V<sub>X</sub>K<sub>Y</sub><br />V=V<sub>X</sub>V<sub>Y</sub></td> |
|||
<td></td> |
|||
<td> |
|||
u=u<sub>Y</sub><br /> |
|||
n=n<sub>X</sub>+z<sub>X</sub>n<sub>Y</sub><br /> |
|||
z=z<sub>X</sub>z<sub>Y</sub><br /> |
|||
o=z<sub>X</sub>o<sub>Y</sub>+o<sub>X</sub>z<sub>Y</sub> |
|||
</td> |
|||
<td> |
|||
f=f<sub>X</sub>f<sub>Y</sub>[=f<sub>Y</sub>]<br /> |
|||
m=m<sub>X</sub>m<sub>Y</sub><br /> |
|||
s=s<sub>X</sub>+s<sub>Y</sub> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>and_b(X,Y)</td> |
|||
<td>[X] [Y] BOOLAND</td> |
|||
<td>nsat<sub>Y</sub> nsat<sub>X</sub></td> |
|||
<td>sat<sub>Y</sub> sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>B=B<sub>X</sub>W<sub>Y</sub></td> |
|||
<td></td> |
|||
<td> |
|||
z=z<sub>X</sub>z<sub>Y</sub><br /> |
|||
o=z<sub>X</sub>o<sub>Y</sub>+o<sub>X</sub>z<sub>Y</sub><br /> |
|||
n=n<sub>X</sub>+z<sub>X</sub>n<sub>Y</sub><br /> |
|||
d=d<sub>X</sub>d<sub>Y</sub><br /> |
|||
u |
|||
</td> |
|||
<td> |
|||
f=f<sub>X</sub>f<sub>Y</sub><br /> |
|||
e=e<sub>X</sub>e<sub>Y</sub>s<sub>X</sub>s<sub>Y</sub><br /> |
|||
m=m<sub>X</sub>m<sub>Y</sub><br /> |
|||
s=s<sub>X</sub>+s<sub>Y</sub> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>and_n(X,Y)</td> |
|||
<td>[X] NOTIF 0 ELSE [Y] ENDIF (=andor(X,Y,0))</td> |
|||
<td>nsat<sub>X</sub></td> |
|||
<td>sat<sub>Y</sub> sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>B=B<sub>X</sub>B<sub>Y</sub></td> |
|||
<td>d<sub>X</sub>u<sub>X</sub></td> |
|||
<td> |
|||
z=z<sub>X</sub>z<sub>Y</sub><br /> |
|||
o=o<sub>X</sub>z<sub>Y</sub><br /> |
|||
u=u<sub>Y</sub><br /> |
|||
d=d<sub>X</sub>[=1] |
|||
</td> |
|||
<td> |
|||
f=f<sub>Y</sub>f<sub>X</sub>[=0]<br /> |
|||
e=e<sub>X</sub>(s<sub>X</sub>+f<sub>Y</sub>)<br /> |
|||
m=m<sub>X</sub>m<sub>Y</sub>e<sub>X</sub><br /> |
|||
s=s<sub>X</sub>+s<sub>Y</sub> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td rowspan="4">X or Z</td> |
|||
<td>or_b(X,Z)</td> |
|||
<td>[X] [Z] BOOLOR</td> |
|||
<td>nsat<sub>Z</sub> nsat<sub>X</sub></td> |
|||
<td>nsat<sub>Z</sub> sat<sub>X</sub></td> |
|||
<td>sat<sub>Z</sub> nsat<sub>X</sub></td> |
|||
<td>B=B<sub>X</sub>W<sub>Z</sub></td> |
|||
<td>d<sub>X</sub>d<sub>Z</sub></td> |
|||
<td> |
|||
z=z<sub>X</sub>z<sub>Z</sub><br /> |
|||
o=z<sub>X</sub>o<sub>Z</sub>+o<sub>X</sub>z<sub>Z</sub><br /> |
|||
d=d<sub>X</sub>d<sub>Z</sub>[=1]<br /> |
|||
u |
|||
</td> |
|||
<td> |
|||
f=f<sub>X</sub>+f<sub>Z</sub>[=0]<br /> |
|||
e=e<sub>X</sub>e<sub>Z</sub><br /> |
|||
m=m<sub>X</sub>m<sub>Z</sub>e<sub>X</sub>e<sub>Z</sub>(s<sub>X</sub>+s<sub>Z</sub>)<br /> |
|||
s=s<sub>X</sub>s<sub>Z</sub> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>or_d(X,Z)</td> |
|||
<td>[X] IFDUP NOTIF [Z] ENDIF</td> |
|||
<td>nsat<sub>Z</sub> nsat<sub>X</sub></td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>sat<sub>Z</sub> nsat<sub>X</sub></td> |
|||
<td>B=B<sub>X</sub>B<sub>Z</sub></td> |
|||
<td>d<sub>X</sub>u<sub>X</sub></td> |
|||
<td> |
|||
z=z<sub>X</sub>z<sub>Z</sub><br /> |
|||
o=o<sub>X</sub>z<sub>Z</sub><br /> |
|||
u=u<sub>X</sub>(f<sub>X</sub>+u<sub>Z</sub>)[=u<sub>Z</sub>]<br /> |
|||
d=d<sub>X</sub>d<sub>Z</sub>[=d<sub>Z</sub>] |
|||
</td> |
|||
<td> |
|||
f=f<sub>X</sub>+f<sub>Z</sub>[=f<sub>Z</sub>]<br /> |
|||
e=e<sub>X</sub>e<sub>Z</sub><br /> |
|||
m=m<sub>X</sub>m<sub>Z</sub>e<sub>X</sub>(s<sub>X</sub>+s<sub>Z</sub>)<br /> |
|||
s=s<sub>X</sub>s<sub>Z</sub> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>or_c(X,Z)</td> |
|||
<td>[X] NOTIF [Z] ENDIF</td> |
|||
<td>-</td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>sat<sub>Z</sub> nsat<sub>X</sub></td> |
|||
<td>V=B<sub>X</sub>V<sub>Z</sub></td> |
|||
<td>d<sub>X</sub>u<sub>X</sub></td> |
|||
<td> |
|||
z=z<sub>X</sub>z<sub>Z</sub><br /> |
|||
o=o<sub>X</sub>z<sub>Z</sub> |
|||
</td> |
|||
<td> |
|||
f=f<sub>X</sub>+f<sub>Z</sub>[=1]<br /> |
|||
m=m<sub>X</sub>m<sub>Z</sub>e<sub>X</sub>(s<sub>X</sub>+s<sub>Z</sub>)<br /> |
|||
s=s<sub>X</sub>s<sub>Z</sub> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>or_i(X,Z)</td> |
|||
<td>IF [X] ELSE [Z] ENDIF</td> |
|||
<td>nsat<sub>X</sub> 1<br />nsat<sub>Z</sub> 0</td> |
|||
<td>sat<sub>X</sub> 1</td> |
|||
<td>sat<sub>Z</sub> 0</td> |
|||
<td>V=V<sub>X</sub>V<sub>Z</sub><br />B=B<sub>X</sub>B<sub>Z</sub><br />K=K<sub>X</sub>K<sub>Z</sub></td> |
|||
<td></td> |
|||
<td> |
|||
o=z<sub>X</sub>z<sub>Z</sub><br /> |
|||
u=u<sub>X</sub>u<sub>Z</sub><br /> |
|||
d=d<sub>X</sub>+d<sub>Z</sub> |
|||
</td> |
|||
<td> |
|||
f=f<sub>X</sub>f<sub>Z</sub><br /> |
|||
e=e<sub>X</sub>f<sub>Z</sub>+e<sub>Z</sub>f<sub>X</sub><br /> |
|||
m=m<sub>X</sub>m<sub>Z</sub>(s<sub>X</sub>+s<sub>Z</sub>)<br /> |
|||
s=s<sub>X</sub>s<sub>Z</sub> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>(X and Y) or Z</td> |
|||
<td>andor(X,Y,Z)</td> |
|||
<td>[X] NOTIF [Z] ELSE [Y] ENDIF</td> |
|||
<td>nsat<sub>Z</sub> nsat<sub>X</sub></td> |
|||
<td>sat<sub>Y</sub> sat<sub>X</sub></td> |
|||
<td>sat<sub>Z</sub> nsat<sub>X</sub></td> |
|||
<td>B=B<sub>X</sub>B<sub>Y</sub>B<sub>Z</sub><br />K=B<sub>X</sub>K<sub>Y</sub>K<sub>Z</sub><br />V=B<sub>X</sub>V<sub>Y</sub>V<sub>Z</sub></td> |
|||
<td>d<sub>X</sub>u<sub>X</sub></td> |
|||
<td> |
|||
z=z<sub>X</sub>z<sub>Y</sub>z<sub>Z</sub><br /> |
|||
o=z<sub>X</sub>o<sub>Y</sub>o<sub>Z</sub>+o<sub>X</sub>z<sub>Y</sub>z<sub>Z</sub><br /> |
|||
u=u<sub>Y</sub>u<sub>Z</sub><br /> |
|||
d=d<sub>X</sub>d<sub>Z</sub>[=d<sub>Z</sub>] |
|||
</td> |
|||
<td> |
|||
f=f<sub>Y</sub>(f<sub>X</sub>+f<sub>Z</sub>)[=f<sub>Y</sub>f<sub>Z</sub>]<br /> |
|||
e=e<sub>X</sub>e<sub>Z</sub>(s<sub>X</sub>+f<sub>Y</sub>)<br /> |
|||
m=m<sub>X</sub>m<sub>Y</sub>m<sub>Z</sub>e<sub>X</sub>(s<sub>X</sub>+s<sub>Y</sub>+s<sub>Z</sub>)<br /> |
|||
s=s<sub>Z</sub>(s<sub>X</sub>+s<sub>Y</sub>) |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>X<sub>1</sub> + ... + X<sub>n</sub> = k</td> |
|||
<td>thresh(k,...)</td> |
|||
<td>[X<sub>1</sub>] ([X<sub>i</sub> ADD)*(n-1) k EQUAL</td> |
|||
<td>nsat...</td> |
|||
<td>(sat|nsat)...</td> |
|||
<td>-</td> |
|||
<td>B=X<sub>1</sub> is B<br />others are W</td> |
|||
<td>n > k > 1<br />all are d and u</td> |
|||
<td> |
|||
z=all are z<br /> |
|||
o=all are z, except one is o<br /> |
|||
d<br /> |
|||
u |
|||
</td> |
|||
<td> |
|||
e=all are e and s<br /> |
|||
m=all are e and m, ≥(n-k) are s<br /> |
|||
s=≥(n-k+1) are s |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>check(key_1) + ... = k</td> |
|||
<td>thresh_m(k,...)</td> |
|||
<td>k key_1 ... key_n n CHECKMULTISIG</td> |
|||
<td>0 0 ... 0</td> |
|||
<td>0 sig...</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td>n ≥ k ≥ 1</td> |
|||
<td>n, u, d</td> |
|||
<td>e, m, s</td> |
|||
</tr> |
|||
<tr> |
|||
<td rowspan="10">X</td> |
|||
<td>a:X</td> |
|||
<td>TOALTSTACK [X] FROMALTSTACK</td> |
|||
<td>nsat<sub>X</sub></td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>W=B<sub>X</sub></td> |
|||
<td></td> |
|||
<td>u=u<sub>X</sub>, d=d<sub>X</sub></td> |
|||
<td>f=f<sub>X</sub>, e=e<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>s:X</td> |
|||
<td>SWAP [X]</td> |
|||
<td>nsat<sub>X</sub></td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>o<sub>X</sub></td> |
|||
<td>W=B<sub>X</sub></td> |
|||
<td></td> |
|||
<td>u=u<sub>x</sub>, d=d<sub>X</sub></td> |
|||
<td>f=f<sub>X</sub>, e=e<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>c:X</td> |
|||
<td>[X] CHECKSIG</td> |
|||
<td>nsat<sub>X</sub></td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>B=K<sub>X</sub></td> |
|||
<td></td> |
|||
<td>o=o<sub>X</sub>, n=n<sub>X</sub>, u, d=d<sub>X</sub></td> |
|||
<td>e=e<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub>[=1]</td> |
|||
</tr> |
|||
<tr> |
|||
<td>t:X</td> |
|||
<td>[X] 1 (=and_v(X,1))</td> |
|||
<td>-</td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>B=V<sub>X</sub></td> |
|||
<td></td> |
|||
<td>u, n=n<sub>X</sub>, z=z<sub>X</sub>, o=o<sub>X</sub></td> |
|||
<td>f, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
<tr> |
|||
<td>d:X</td> |
|||
<td>DUP IF [X] ENDIF</td> |
|||
<td>0</td> |
|||
<td>sat<sub>X</sub> 1</td> |
|||
<td>-</td> |
|||
<td>B=V<sub>X</sub></td> |
|||
<td>z<sub>X</sub></td> |
|||
<td>o=z<sub>X</sub>, n, u, d</td> |
|||
<td>e=f<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>v:X</td> |
|||
<td>[X] VERIFY</td> |
|||
<td>-</td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>V=B<sub>X</sub></td> |
|||
<td></td> |
|||
<td>z=z<sub>X</sub>, o=o<sub>X</sub>, n=n<sub>X</sub></td> |
|||
<td>f, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>j:X</td> |
|||
<td>SIZE 0NOTEQUAL IF [X] ENDIF</td> |
|||
<td>0</td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>B=B<sub>X</sub></td> |
|||
<td>n<sub>X</sub></td> |
|||
<td>o=o<sub>X</sub>, n, u=u<sub>X</sub>, d</td> |
|||
<td>e=f<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>n:X</td> |
|||
<td>[X] 0NOTEQUAL</td> |
|||
<td>nsat<sub>X</sub></td> |
|||
<td>sat<sub>X</sub></td> |
|||
<td>-</td> |
|||
<td>B=B<sub>X</sub></td> |
|||
<td></td> |
|||
<td>z=z<sub>X</sub>, o=o<sub>X</sub>, n=n<sub>X</sub>, u, d=d<sub>X</sub></td> |
|||
<td>f=f<sub>X</sub>, e=e<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>l:X</td> |
|||
<td>IF 0 ELSE [X] ENDIF (=or_i(0,X))</td> |
|||
<td>1</td> |
|||
<td>sat<sub>X</sub> 0</td> |
|||
<td>-</td> |
|||
<td>B=B<sub>X</sub></td> |
|||
<td></td> |
|||
<td>o=z<sub>X</sub>, u=u<sub>X</sub>, d</td> |
|||
<td>e=f<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>u:X</td> |
|||
<td>IF [X] ELSE 0 ENDIF (=or_i(X,0))</td> |
|||
<td>0</td> |
|||
<td>sat<sub>X</sub> 1</td> |
|||
<td>-</td> |
|||
<td>B=B<sub>X</sub></td> |
|||
<td></td> |
|||
<td>o=z<sub>X</sub>, u=u<sub>X</sub>, d</td> |
|||
<td>e=f<sub>X</sub>, m=m<sub>X</sub>, s=s<sub>X</sub></td> |
|||
</tr> |
|||
<tr> |
|||
<td>true</td> |
|||
<td>1</td> |
|||
<td>1</td> |
|||
<td>-</td> |
|||
<td>[]</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td></td> |
|||
<td>z, u</td> |
|||
<td>f, m</td> |
|||
</tr> |
|||
<tr> |
|||
<td>false</td> |
|||
<td>0</td> |
|||
<td>0</td> |
|||
<td>[]</td> |
|||
<td>-</td> |
|||
<td>-</td> |
|||
<td>B</td> |
|||
<td></td> |
|||
<td>z, u, d</td> |
|||
<td>e, m, s</td> |
|||
</tr> |
|||
</table> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</body> |
|||
</html> |
@ -0,0 +1,118 @@ |
|||
#include <string> |
|||
|
|||
#include <script/miniscript.h> |
|||
|
|||
#include "compiler.h" |
|||
|
|||
namespace { |
|||
|
|||
using miniscript::operator""_mst; |
|||
|
|||
void Output(const std::string& str, char* out, int outlen) { |
|||
int maxlen = std::min<int>(outlen - 1, str.size()); |
|||
memcpy(out, str.c_str(), maxlen); |
|||
out[maxlen] = 0; |
|||
} |
|||
|
|||
std::string Props(const miniscript::NodeRef<CompilerKey>& node, std::string in) { |
|||
std::string ret = "<span title=\"type: "; |
|||
if (node->GetType() == ""_mst) { |
|||
ret += "[invalid]"; |
|||
} else { |
|||
if (node->GetType() << "B"_mst) ret += 'B'; |
|||
if (node->GetType() << "V"_mst) ret += 'V'; |
|||
if (node->GetType() << "W"_mst) ret += 'W'; |
|||
if (node->GetType() << "K"_mst) ret += 'K'; |
|||
if (node->GetType() << "z"_mst) ret += 'z'; |
|||
if (node->GetType() << "o"_mst) ret += 'o'; |
|||
if (node->GetType() << "n"_mst) ret += 'n'; |
|||
if (node->GetType() << "d"_mst) ret += 'd'; |
|||
if (node->GetType() << "f"_mst) ret += 'f'; |
|||
if (node->GetType() << "e"_mst) ret += 'e'; |
|||
if (node->GetType() << "m"_mst) ret += 'm'; |
|||
if (node->GetType() << "u"_mst) ret += 'u'; |
|||
if (node->GetType() << "s"_mst) ret += 's'; |
|||
} |
|||
ret += " scriptlen: " + std::to_string(node->ScriptSize()); |
|||
ret += " maxop: " + std::to_string(node->GetOps()); |
|||
return std::move(ret) + "\">" + std::move(in) + "</span>"; |
|||
} |
|||
|
|||
std::string Analyze(const miniscript::NodeRef<CompilerKey>& node) { |
|||
switch (node->nodetype) { |
|||
case miniscript::NodeType::PK: return Props(node, "pk(" + COMPILER_CTX.ToString(node->keys[0]) + ")"); |
|||
case miniscript::NodeType::PK_H: return Props(node, "pk_h(" + COMPILER_CTX.ToString(node->keys[0]) + ")"); |
|||
case miniscript::NodeType::THRESH_M: return Props(node, "thresh_m(" + std::to_string(node->k) + " of " + std::to_string(node->keys.size()) + ")"); |
|||
case miniscript::NodeType::AFTER: return Props(node, "after(" + std::to_string(node->k) + ")"); |
|||
case miniscript::NodeType::OLDER: return Props(node, "older(" + std::to_string(node->k) + ")"); |
|||
case miniscript::NodeType::SHA256: return Props(node, "sha256()"); |
|||
case miniscript::NodeType::RIPEMD160: return Props(node, "ripemd160()"); |
|||
case miniscript::NodeType::HASH256: return Props(node, "hash256()"); |
|||
case miniscript::NodeType::HASH160: return Props(node, "hash160()"); |
|||
case miniscript::NodeType::FALSE: return Props(node, "false"); |
|||
case miniscript::NodeType::TRUE: return Props(node, "true"); |
|||
case miniscript::NodeType::WRAP_A: return Props(node, "a:") + " " + Analyze(node->subs[0]); |
|||
case miniscript::NodeType::WRAP_S: return Props(node, "s:") + " " + Analyze(node->subs[0]); |
|||
case miniscript::NodeType::WRAP_C: return Props(node, "c:") + " " + Analyze(node->subs[0]); |
|||
case miniscript::NodeType::WRAP_D: return Props(node, "d:") + " " + Analyze(node->subs[0]); |
|||
case miniscript::NodeType::WRAP_V: return Props(node, "v:") + " " + Analyze(node->subs[0]); |
|||
case miniscript::NodeType::WRAP_N: return Props(node, "n:") + " " + Analyze(node->subs[0]); |
|||
case miniscript::NodeType::WRAP_J: return Props(node, "j:") + " " + Analyze(node->subs[0]); |
|||
case miniscript::NodeType::AND_V: return Props(node, "and_v") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>"; |
|||
case miniscript::NodeType::AND_B: return Props(node, "and_b") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>"; |
|||
case miniscript::NodeType::OR_B: return Props(node, "or_b") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>"; |
|||
case miniscript::NodeType::OR_C: return Props(node, "or_c") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>"; |
|||
case miniscript::NodeType::OR_D: return Props(node, "or_d") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>"; |
|||
case miniscript::NodeType::OR_I: return Props(node, "or_i") + "<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul>"; |
|||
case miniscript::NodeType::ANDOR: return Props(node, "andor [or]") + "<ul style=\"list-style-type: disc;\"><li>andor [and]<ul style=\"list-style-type: disc;\"><li>" + Analyze(node->subs[0]) + "</li><li>" + Analyze(node->subs[1]) + "</li></ul></li><li>" + Analyze(node->subs[2]) + "</li></ul>"; |
|||
case miniscript::NodeType::THRESH: { |
|||
auto ret = Props(node, "thresh(" + std::to_string(node->k) + " of " + std::to_string(node->subs.size()) + ")") + "<ul style=\"list-style-type: disc;\">"; |
|||
for (const auto& sub : node->subs) { |
|||
ret += "<li>" + Analyze(sub) + "</li>"; |
|||
} |
|||
return std::move(ret) + "</ul>"; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
extern "C" { |
|||
|
|||
void miniscript_compile(const char* desc, char* descout, int descoutlen, char* costout, int costoutlen) { |
|||
try { |
|||
std::string str(desc); |
|||
str.erase(str.find_last_not_of(" \n\r\t") + 1); |
|||
miniscript::NodeRef<CompilerKey> ret; |
|||
double avgcost; |
|||
if (!Compile(Expand(str), ret, avgcost)) { |
|||
Output("[compile error]", descout, descoutlen); |
|||
Output("[compile error]", costout, costoutlen); |
|||
return; |
|||
} |
|||
Output(Abbreviate(ret->ToString(COMPILER_CTX)), descout, descoutlen); |
|||
std::string coststr = "Size: " + std::to_string(ret->ScriptSize()) + " bytes script + " + std::to_string(avgcost) + " bytes input = " + std::to_string(ret->ScriptSize() + avgcost) + " bytes<br/><ul><li>" + Analyze(ret) + "</li></lu>"; |
|||
Output(coststr, costout, costoutlen); |
|||
} catch (const std::exception& e) { |
|||
Output("[exception: " + std::string(e.what()) + "]", descout, descoutlen); |
|||
} |
|||
} |
|||
|
|||
void miniscript_analyze(const char* ms, char* costout, int costoutlen) { |
|||
try { |
|||
std::string str(ms); |
|||
str.erase(str.find_last_not_of(" \n\r\t") + 1); |
|||
miniscript::NodeRef<CompilerKey> ret; |
|||
ret = miniscript::FromString(Expand(str), COMPILER_CTX); |
|||
if (!ret) { |
|||
Output("[analysis error]", costout, costoutlen); |
|||
return; |
|||
} |
|||
std::string coststr = "Size: " + std::to_string(ret->ScriptSize()) + " bytes script<ul><li>" + Analyze(ret) + "</li>"; |
|||
Output(coststr, costout, costoutlen); |
|||
} catch (const std::exception& e) { |
|||
Output("[exception: " + std::string(e.what()) + "]", costout, costoutlen); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,36 @@ |
|||
#include <iostream> |
|||
#include <stdio.h> |
|||
#include <string> |
|||
#include <ctype.h> |
|||
#include <assert.h> |
|||
|
|||
#include <script/miniscript.h> |
|||
|
|||
#include "compiler.h" |
|||
|
|||
using miniscript::operator""_mst; |
|||
|
|||
static bool run(std::string&& line, int64_t count) { |
|||
if (line.size() && line.back() == '\n') line.pop_back(); |
|||
if (line.size() == 0) return false; |
|||
|
|||
miniscript::NodeRef<CompilerKey> ret; |
|||
double avgcost = 0; |
|||
if (Compile(Expand(line), ret, avgcost)) { |
|||
printf("X %17.10f %5i %s %s\n", ret->ScriptSize() + avgcost, (int)ret->ScriptSize(), Abbreviate(ret->ToString(COMPILER_CTX)).c_str(), line.c_str()); |
|||
} else if ((ret = miniscript::FromString(Expand(line), COMPILER_CTX))) { |
|||
printf("%7li scriptlen=%i maxops=%i type=%s safe=%s nonmal=%s dissat=%s input=%s output=%s miniscript=%s\n", (long)count, (int)ret->ScriptSize(), (int)ret->GetOps(), ret->GetType() << "B"_mst ? "B" : ret->GetType() << "V"_mst ? "V" : ret->GetType() << "W"_mst ? "W" : ret->GetType() << "K"_mst ? "K" : "(invalid)", ret->GetType() << "s"_mst ? "yes" : "no", ret->GetType() << "m"_mst ? "yes" : "no", ret->GetType() << "f"_mst ? "no" : ret->GetType() << "e"_mst ? "unique" : ret->GetType() << "d"_mst ? "yes" : "unknown", ret->GetType() << "z"_mst ? "0" : ret->GetType() << "o"_mst ? (ret->GetType() << "n"_mst ? "1n" : "1") : ret->GetType() << "n"_mst ? "n" : "-", ret->GetType() << "u"_mst ? "1" : "nonzero", line.c_str()); |
|||
} else { |
|||
printf("Failed to parse as policy or miniscript '%s'\n", line.c_str()); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
int main(void) { |
|||
int64_t count = 0; |
|||
do { |
|||
std::string line; |
|||
std::getline(std::cin, line); |
|||
if (!run(std::move(line), count++)) break; |
|||
} while(true); |
|||
} |
@ -0,0 +1,8 @@ |
|||
|
|||
.footer { |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
padding: 4rem; |
|||
text-align: center; |
|||
} |
@ -0,0 +1,52 @@ |
|||
prob = "" | ("3" | "99") "@"; |
|||
|
|||
s1 = s; |
|||
s2 = s "," s1; |
|||
s3 = s "," s2; |
|||
s4 = s "," s3; |
|||
s5 = s "," s4; |
|||
|
|||
w1 = w; |
|||
w2 = w "," w1; |
|||
w3 = w "," w2; |
|||
w4 = w "," w3; |
|||
w5 = w "," w4; |
|||
|
|||
t11 = s1; |
|||
t12 = w1 "," s1; |
|||
t22 = s2; |
|||
t13 = w2 "," s1; |
|||
t23 = w1 "," s2; |
|||
t33 = s3; |
|||
t14 = w3 "," s1; |
|||
t24 = w2 "," s2; |
|||
t34 = w1 "," s3; |
|||
t44 = s4; |
|||
t15 = w4 "," s1; |
|||
t25 = w3 "," s2; |
|||
t35 = w2 "," s3; |
|||
t45 = w1 "," s4; |
|||
t55 = s5; |
|||
|
|||
s = "pk(C)" | |
|||
"and(" (t12 | t22) ")" | |
|||
"or(" prob s "," prob s ")" | |
|||
"thresh(2," (t23 | t33) ")" | |
|||
"thresh(2," (t34 | t44) ")" | |
|||
"thresh(3," (t24 | t34 | t44) ")" | |
|||
"thresh(2," (t45 | t55) ")" | |
|||
"thresh(3," (t35 | t45 | t55) ")" | |
|||
"thresh(4," (t25 | t35 | t45 | t55) ")"; |
|||
|
|||
w = "after(9)" | |
|||
"sha256(H)" | |
|||
"or(" prob w "," prob s ")" | |
|||
"and(" w "," w ")" | |
|||
"thresh(2," t13 ")" | |
|||
"thresh(2," t24 ")" | |
|||
"thresh(3," t14 ")" | |
|||
"thresh(2," t35 ")" | |
|||
"thresh(3," t25 ")" | |
|||
"thresh(4," t15 ")"; |
|||
|
|||
main = s; |
@ -0,0 +1,14 @@ |
|||
digraph wrapper { |
|||
|
|||
c -> "a/s"; |
|||
c -> v; |
|||
d -> "a/s"; |
|||
j -> "a/s"; |
|||
n -> "a/s"; |
|||
n -> "l/u"; |
|||
t -> j; |
|||
t -> "l/u"; |
|||
"l/u" -> "a/s"; |
|||
v -> d; |
|||
v -> t; |
|||
} |
Binary file not shown.
@ -0,0 +1,43 @@ |
|||
6522173 a |
|||
8439774 ac |
|||
658287 aj |
|||
19 ajtv |
|||
94072 ajtvc |
|||
154419 al |
|||
1 aln |
|||
65829 alt |
|||
63575 altv |
|||
726071 altvc |
|||
3718 an |
|||
16477 au |
|||
36 aun |
|||
29997 aut |
|||
6787 autv |
|||
168980 autvc |
|||
158884276 c |
|||
8040 d |
|||
464369 dv |
|||
664591 j |
|||
178791 jtv |
|||
75588 jtvc |
|||
268910 l |
|||
19 ln |
|||
319564 lt |
|||
93261 ltv |
|||
1074205 ltvc |
|||
2783824 n |
|||
18736088 sc |
|||
39913 sd |
|||
2758580 sdv |
|||
291516 sj |
|||
2015672 sjtv |
|||
9763257 t |
|||
680157 tv |
|||
8141788 tvc |
|||
27124 u |
|||
294 un |
|||
50609 ut |
|||
8639 utv |
|||
247160 utvc |
|||
38789595 v |
|||
83559552 vc |
Loading…
Reference in new issue