Browse Source

Initial commit

analysis
Pieter Wuille 5 years ago
commit
40767b27a9
  1. 14
      Makefile
  2. 22
      bitcoin/attributes.h
  3. 66
      bitcoin/compat/byteswap.h
  4. 241
      bitcoin/compat/endian.h
  5. 103
      bitcoin/crypto/common.h
  6. 528
      bitcoin/prevector.h
  7. 184
      bitcoin/script/miniscript.cpp
  8. 1257
      bitcoin/script/miniscript.h
  9. 328
      bitcoin/script/script.cpp
  10. 588
      bitcoin/script/script.h
  11. 1005
      bitcoin/serialize.h
  12. 60
      bitcoin/span.h
  13. 1069
      bitcoin/tinyformat.h
  14. 45
      bitcoin/util/spanparsing.cpp
  15. 20
      bitcoin/util/spanparsing.h
  16. 559
      bitcoin/util/strencodings.cpp
  17. 242
      bitcoin/util/strencodings.h
  18. 77
      bitcoin/util/vector.h
  19. 902
      compiler.cpp
  20. 33
      compiler.h
  21. 580
      index.html
  22. 118
      js_bindings.cpp
  23. 36
      main.cpp
  24. 8
      style.css
  25. 52
      test.gram
  26. 14
      wrapper.dot
  27. BIN
      wrapper.pdf
  28. 43
      wrapper.txt

14
Makefile

@ -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

22
bitcoin/attributes.h

@ -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

66
bitcoin/compat/byteswap.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

241
bitcoin/compat/endian.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

103
bitcoin/crypto/common.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

528
bitcoin/prevector.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

184
bitcoin/script/miniscript.cpp

@ -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

1257
bitcoin/script/miniscript.h

File diff suppressed because it is too large

328
bitcoin/script/script.cpp

@ -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;
}

588
bitcoin/script/script.h

@ -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

1005
bitcoin/serialize.h

File diff suppressed because it is too large

60
bitcoin/span.h

@ -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

1069
bitcoin/tinyformat.h

File diff suppressed because it is too large

45
bitcoin/util/spanparsing.cpp

@ -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;
}

20
bitcoin/util/spanparsing.h

@ -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

559
bitcoin/util/strencodings.cpp

@ -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;
}

242
bitcoin/util/strencodings.h

@ -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

77
bitcoin/util/vector.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

902
compiler.cpp

@ -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;
}

33
compiler.h

@ -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

580
index.html

@ -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 &ge; n (and compatible)</td>
<td>older(n)</td>
<td>n CHECKSEQUENCEVERIFY</td>
<td>-</td>
<td>[]</td>
<td>-</td>
<td>B</td>
<td>n &ge; 1</td>
<td>z</td>
<td>f, m</td>
</tr>
<tr>
<td>nLockTime &ge; 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 &ge; 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, &ge;(n-k) are s<br />
s=&ge;(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 &ge; k &ge; 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>

118
js_bindings.cpp

@ -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 += "&#13;scriptlen: " + std::to_string(node->ScriptSize());
ret += "&#13;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);
}
}
}

36
main.cpp

@ -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);
}

8
style.css

@ -0,0 +1,8 @@
.footer {
right: 0;
bottom: 0;
left: 0;
padding: 4rem;
text-align: center;
}

52
test.gram

@ -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;

14
wrapper.dot

@ -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;
}

BIN
wrapper.pdf

Binary file not shown.

43
wrapper.txt

@ -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…
Cancel
Save