diff --git a/.travis.yml b/.travis.yml index 17a3c93a..97ab2478 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ language: cpp compiler: gcc + os: linux + sudo: required dist: trusty + install: - sudo apt-get -qq update - sudo apt-get install -y build-essential libtool autotools-dev autoconf pkg-config libssl-dev libcrypto++-dev diff --git a/Makefile.am b/Makefile.am index 81f87e43..055686dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -190,6 +190,8 @@ check-local: @qa/pull-tester/run-silkd-for-test.sh $(JAVA) -jar $(JAVA_COMPARISON_TOOL) qa/tmp/compTool $(COMPARISON_TOOL_REORG_TESTS) 2>&1 endif +dist_noinst_SCRIPTS = autogen.sh + EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.sh qa/pull-tester/run-silk-cli qa/rpc-tests $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) CLEANFILES = $(OSX_DMG) $(SILK_WIN_INSTALLER) diff --git a/README.md b/README.md index 72d301f0..bc8e3328 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# **Silk-Core (SLK) v1.0.1.2** +# **Silk-Core (SLK) v1.1.0.0** [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) diff --git a/configure.ac b/configure.ac index bc306208..b4bbe0c0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 1) -define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 2) +define(_CLIENT_VERSION_MINOR, 1) +define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2017) AC_INIT([Silk Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[silk]) @@ -399,8 +399,8 @@ if test x$use_lcov = xyes; then [AC_MSG_ERROR("lcov testing requested but --coverage flag does not work")]) fi -dnl Check for endianness -AC_C_BIGENDIAN +dnl Require little endian +### ??? AC_C_BIGENDIAN([AC_MSG_ERROR("Big Endian not supported")]) dnl Check for pthread compile/link requirements AX_PTHREAD diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index a6e9f341..b60fc7d7 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,11 +1,12 @@ --- -name: "silk-linux-0.5" +name: "silk-linux-1.1" enable_cache: true suites: - "trusty" architectures: - "amd64" packages: +- "curl" - "g++-multilib" - "git-core" - "pkg-config" @@ -15,9 +16,9 @@ packages: - "faketime" - "bsdmainutils" - "binutils-gold" -reference_datetime: "2016-01-01 00:00:00" +reference_datetime: "2017-01-01 00:00:00" remotes: -- "url": "https://github.com/Silk/silk.git" +- "url": "https://github.com/silknetwork/silk-core.git" "dir": "silk" files: [] script: | diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 51d4dcd4..3e96fb04 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "silk-osx-0.5" +name: "silk-osx-1.1" enable_cache: true suites: - "trusty" @@ -7,6 +7,7 @@ architectures: - "amd64" packages: - "g++-multilib" +- "curl" - "git-core" - "pkg-config" - "autoconf" @@ -20,7 +21,7 @@ packages: - "libbz2-dev" reference_datetime: "2017-01-01 00:00:00" remotes: -- "url": "https://github.com/Silk/silk.git" +- "url": "https://github.com/silknetwork/silk-core.git" "dir": "silk" files: - "MacOSX10.7.sdk.tar.gz" diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 3e53ae3a..cf79b34e 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -1,11 +1,12 @@ --- -name: "silk-win-0.5" +name: "silk-win-1.1" enable_cache: true suites: - "trusty" architectures: - "amd64" packages: +- "curl" - "g++" - "git-core" - "pkg-config" diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index b366460e..200d6ed2 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -7,7 +7,7 @@ build_darwin_OTOOL: = $(shell xcrun -f otool) build_darwin_NM: = $(shell xcrun -f nm) build_darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) build_darwin_SHA256SUM = shasum -a 256 -build_darwin_DOWNLOAD = curl --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -o +build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o #darwin host on darwin builder. overrides darwin host preferences. darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) diff --git a/depends/builders/linux.mk b/depends/builders/linux.mk index 98d0e9de..b03f4240 100644 --- a/depends/builders/linux.mk +++ b/depends/builders/linux.mk @@ -1,2 +1,2 @@ build_linux_SHA256SUM = sha256sum -build_linux_DOWNLOAD = wget --timeout=$(DOWNLOAD_CONNECT_TIMEOUT) --tries=$(DOWNLOAD_RETRIES) -nv -O +build_linux_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o diff --git a/depends/config.guess b/depends/config.guess index 1f5c50c0..55373193 100755 --- a/depends/config.guess +++ b/depends/config.guess @@ -1099,7 +1099,7 @@ EOF # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; diff --git a/depends/packages/miniupnpc.mk b/depends/packages/miniupnpc.mk index 00101f1b..77bae10c 100644 --- a/depends/packages/miniupnpc.mk +++ b/depends/packages/miniupnpc.mk @@ -1,8 +1,8 @@ package=miniupnpc -$(package)_version=1.9.20140701 +$(package)_version=1.9.20151008 $(package)_download_path=http://miniupnp.free.fr/files $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=26f3985bad7768b8483b793448ae49414cdc4451d0ec83e7c1944367e15f9f07 +$(package)_sha256_hash=e444ac3b587ce82709c4d0cfca1fe71f44f9fc433e9f946b12b9e1bfe667a633 define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" diff --git a/depends/packages/native_protobuf.mk b/depends/packages/native_protobuf.mk index ed1a771f..ce50b366 100644 --- a/depends/packages/native_protobuf.mk +++ b/depends/packages/native_protobuf.mk @@ -1,8 +1,8 @@ package=native_protobuf -$(package)_version=2.5.0 -$(package)_download_path=https://protobuf.googlecode.com/files +$(package)_version=2.6.1 +$(package)_download_path=https://github.com/google/protobuf/releases/download/v$($(package)_version) $(package)_file_name=protobuf-$($(package)_version).tar.bz2 -$(package)_sha256_hash=13bfc5ae543cf3aa180ac2485c0bc89495e3ae711fc6fab4f8ffe90dfb4bb677 +$(package)_sha256_hash=ee445612d544d885ae240ffbcbf9267faa9f593b7b101f21d58beceb92661910 define $(package)_set_vars $(package)_config_opts=--disable-shared diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk index eb477941..492994b7 100644 --- a/depends/packages/openssl.mk +++ b/depends/packages/openssl.mk @@ -10,7 +10,7 @@ $(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/o $(package)_config_opts+=no-krb5 no-camellia no-capieng no-cast no-cms no-dtls1 no-gost no-gmp no-heartbeats no-idea no-jpake no-md2 $(package)_config_opts+=no-mdc2 no-rc5 no-rdrand no-rfc3779 no-rsax no-sctp no-seed no-sha0 no-static_engine no-whirlpool no-rc2 no-rc4 no-ssl2 no-ssl3 $(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags) -$(package)_config_opts_linux=-fPIC +$(package)_config_opts_linux=-fPIC -Wa,--noexecstack $(package)_config_opts_x86_64_linux=linux-x86_64 $(package)_config_opts_i686_linux=linux-generic32 $(package)_config_opts_arm_linux=linux-generic32 diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 6d4206cc..fd62a77e 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,6 +1,6 @@ PACKAGE=qt $(package)_version=5.2.1 -$(package)_download_path=http://download.qt-project.org/official_releases/qt/5.2/$($(package)_version)/single +$(package)_download_path=http://download.qt.io/archive/qt/5.2/$($(package)_version)/single $(package)_file_name=$(package)-everywhere-opensource-src-$($(package)_version).tar.gz $(package)_sha256_hash=84e924181d4ad6db00239d87250cc89868484a14841f77fb85ab1f1dbdcd7da1 $(package)_dependencies=openssl diff --git a/doc/Doxyfile b/doc/Doxyfile index 1ca0751a..ca81a093 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -34,7 +34,7 @@ PROJECT_NAME = Silk # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 1.0.0.0 +PROJECT_NUMBER = 1.0.1.3 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index bf5a0023..cfbe902e 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -8,11 +8,6 @@ CURDIR=$(cd $(dirname "$0"); pwd) export SILKCLI=${BUILDDIR}/qa/pull-tester/run-silk-cli export SILKD=${REAL_SILKD} -if [ "x${EXEEXT}" = "x.exe" ]; then - echo "Win tests currently disabled" - exit 0 -fi - #Run the tests if [ "x${ENABLE_SILKD}${ENABLE_UTILS}${ENABLE_WALLET}" = "x111" ]; then diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py new file mode 100644 index 00000000..ee100373 --- /dev/null +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python2 +# +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from test_framework.test_framework import ComparisonTestFramework +from test_framework.util import * +from test_framework.mininode import CTransaction, NetworkThread +from test_framework.blocktools import create_coinbase, create_block +from test_framework.comptool import TestInstance, TestManager +from test_framework.script import CScript, OP_1NEGATE, OP_NOP2, OP_DROP +from binascii import hexlify, unhexlify +import cStringIO +import time + +def cltv_invalidate(tx): + '''Modify the signature in vin 0 of the tx to fail CLTV + + Prepends -1 CLTV DROP in the scriptSig itself. + ''' + tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_NOP2, OP_DROP] + + list(CScript(tx.vin[0].scriptSig))) + +''' +This test is meant to exercise BIP65 (CHECKLOCKTIMEVERIFY) +Connect to a single node. +Mine 2 (version 3) blocks (save the coinbases for later). +Generate 98 more version 3 blocks, verify the node accepts. +Mine 749 version 4 blocks, verify the node accepts. +Check that the new CLTV rules are not enforced on the 750th version 4 block. +Check that the new CLTV rules are enforced on the 751st version 4 block. +Mine 199 new version blocks. +Mine 1 old-version block. +Mine 1 new version block. +Mine 1 old version block, see that the node rejects. +''' + +class BIP65Test(ComparisonTestFramework): + + def __init__(self): + self.num_nodes = 1 + + def setup_network(self): + # Must set the blockversion for this test + self.nodes = start_nodes(1, self.options.tmpdir, + extra_args=[['-debug', '-whitelist=127.0.0.1', '-blockversion=3']], + binary=[self.options.testbinary]) + + def run_test(self): + test = TestManager(self, self.options.tmpdir) + test.add_all_connections(self.nodes) + NetworkThread().start() # Start up network handling in another thread + test.run() + + def create_transaction(self, node, coinbase, to_address, amount): + from_txid = node.getblock(coinbase)['tx'][0] + inputs = [{ "txid" : from_txid, "vout" : 0}] + outputs = { to_address : amount } + rawtx = node.createrawtransaction(inputs, outputs) + signresult = node.signrawtransaction(rawtx) + tx = CTransaction() + f = cStringIO.StringIO(unhexlify(signresult['hex'])) + tx.deserialize(f) + return tx + + def get_tests(self): + + self.coinbase_blocks = self.nodes[0].setgenerate(True, 2) + self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0) + self.nodeaddress = self.nodes[0].getnewaddress() + self.last_block_time = time.time() + + ''' 98 more version 3 blocks ''' + test_blocks = [] + for i in xrange(98): + block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) + block.nVersion = 3 + block.rehash() + block.solve() + test_blocks.append([block, True]) + self.last_block_time += 1 + self.tip = block.sha256 + yield TestInstance(test_blocks, sync_every_block=False) + + ''' Mine 749 version 4 blocks ''' + test_blocks = [] + for i in xrange(749): + block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) + block.nVersion = 4 + block.rehash() + block.solve() + test_blocks.append([block, True]) + self.last_block_time += 1 + self.tip = block.sha256 + yield TestInstance(test_blocks, sync_every_block=False) + + ''' + Check that the new CLTV rules are not enforced in the 750th + version 3 block. + ''' + spendtx = self.create_transaction(self.nodes[0], + self.coinbase_blocks[0], self.nodeaddress, 1.0) + cltv_invalidate(spendtx) + spendtx.rehash() + + block = create_block(self.tip, create_coinbase(2), self.last_block_time + 1) + block.nVersion = 4 + block.vtx.append(spendtx) + block.hashMerkleRoot = block.calc_merkle_root() + block.rehash() + block.solve() + + self.last_block_time += 1 + self.tip = block.sha256 + yield TestInstance([[block, True]]) + + ''' + Check that the new CLTV rules are enforced in the 751st version 4 + block. + ''' + spendtx = self.create_transaction(self.nodes[0], + self.coinbase_blocks[1], self.nodeaddress, 1.0) + cltv_invalidate(spendtx) + spendtx.rehash() + + block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) + block.nVersion = 4 + block.vtx.append(spendtx) + block.hashMerkleRoot = block.calc_merkle_root() + block.rehash() + block.solve() + self.last_block_time += 1 + yield TestInstance([[block, False]]) + + ''' Mine 199 new version blocks on last valid tip ''' + test_blocks = [] + for i in xrange(199): + block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) + block.nVersion = 4 + block.rehash() + block.solve() + test_blocks.append([block, True]) + self.last_block_time += 1 + self.tip = block.sha256 + yield TestInstance(test_blocks, sync_every_block=False) + + ''' Mine 1 old version block ''' + block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) + block.nVersion = 3 + block.rehash() + block.solve() + self.last_block_time += 1 + self.tip = block.sha256 + yield TestInstance([[block, True]]) + + ''' Mine 1 new version block ''' + block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) + block.nVersion = 4 + block.rehash() + block.solve() + self.last_block_time += 1 + self.tip = block.sha256 + yield TestInstance([[block, True]]) + + ''' Mine 1 old version block, should be invalid ''' + block = create_block(self.tip, create_coinbase(1), self.last_block_time + 1) + block.nVersion = 3 + block.rehash() + block.solve() + self.last_block_time += 1 + yield TestInstance([[block, False]]) + +if __name__ == '__main__': + BIP65Test().main() \ No newline at end of file diff --git a/qa/rpc-tests/bip65-cltv.py b/qa/rpc-tests/bip65-cltv.py new file mode 100644 index 00000000..e18dac6c --- /dev/null +++ b/qa/rpc-tests/bip65-cltv.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python2 +# Copyright (c) 2015-2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test the CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic +# + +from test_framework.test_framework import SilkTestFramework +from test_framework.util import * +import os +import shutil + +class BIP65Test(SilkTestFramework): + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, [])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=3"])) + self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=4"])) + connect_nodes(self.nodes[1], 0) + connect_nodes(self.nodes[2], 0) + self.is_network_split = False + self.sync_all() + + def run_test(self): + cnt = self.nodes[0].getblockcount() + + # Mine some old-version blocks + self.nodes[1].setgenerate(True, 100) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 100): + raise AssertionError("Failed to mine 100 version=3 blocks") + + # Mine 750 new-version blocks + for i in xrange(15): + self.nodes[2].setgenerate(True, 50) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 850): + raise AssertionError("Failed to mine 750 version=4 blocks") + + # TODO: check that new CHECKLOCKTIMEVERIFY rules are not enforced + + # Mine 1 new-version block + self.nodes[2].setgenerate(True, 1) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 851): + raise AssertionFailure("Failed to mine a version=4 blocks") + + # TODO: check that new CHECKLOCKTIMEVERIFY rules are enforced + + # Mine 198 new-version blocks + for i in xrange(2): + self.nodes[2].setgenerate(True, 99) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 1049): + raise AssertionError("Failed to mine 198 version=4 blocks") + + # Mine 1 old-version block + self.nodes[1].setgenerate(True, 1) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 1050): + raise AssertionError("Failed to mine a version=3 block after 949 version=4 blocks") + + # Mine 1 new-version blocks + self.nodes[2].setgenerate(True, 1) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 1051): + raise AssertionError("Failed to mine a version=4 block") + + # Mine 1 old-version blocks + try: + self.nodes[1].setgenerate(True, 1) + raise AssertionError("Succeeded to mine a version=3 block after 950 version=4 blocks") + except JSONRPCException: + pass + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 1051): + raise AssertionError("Accepted a version=3 block after 950 version=4 blocks") + + # Mine 1 new-version blocks + self.nodes[2].setgenerate(True, 1) + self.sync_all() + if (self.nodes[0].getblockcount() != cnt + 1052): + raise AssertionError("Failed to mine a version=4 block") + +if __name__ == '__main__': + BIP65Test().main() \ No newline at end of file diff --git a/silk-qt.pro b/silk-qt.pro index 410cb8e0..f45b805b 100644 --- a/silk-qt.pro +++ b/silk-qt.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = Silk -VERSION = 1.0.1.2 +VERSION = 1.1.0.0 # for boost 1.37, add -mt to the boost libraries # use: qmake BOOST_LIB_SUFFIX=-mt diff --git a/src/addrman.h b/src/addrman.h index 286cc5ee..32423bd8 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -268,7 +268,7 @@ class CAddrMan * Notice that vvTried, mapAddr and vVector are never encoded explicitly; * they are instead reconstructed from the other information. * - * vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change, + * vvNew is serialized, but only used if ADDRMAN_UNKNOWN_BUCKET_COUNT didn't change, * otherwise it is reconstructed as well. * * This format is more complex, but significantly smaller (at most 1.5 MiB), and supports diff --git a/src/chainparams.cpp b/src/chainparams.cpp index b26773e4..32c3816c 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -45,11 +45,11 @@ void MineGenesis(CBlock genesis, uint256 bnProofOfWorkLimit){ printf("New best: %s\n", newhash.GetHex().c_str()); } } - printf("Gensis Hash: %s\n", genesis.GetHash().ToString().c_str()); - printf("Gensis Hash Merkle: %s\n", genesis.hashMerkleRoot.ToString().c_str()); - printf("Gensis nTime: %u\n", genesis.nTime); - printf("Gensis nBits: %08x\n", genesis.nBits); - printf("Gensis Nonce: %u\n\n\n", genesis.nNonce); + printf("Genesis Hash: %s\n", genesis.GetHash().ToString().c_str()); + printf("Genesis Hash Merkle: %s\n", genesis.hashMerkleRoot.ToString().c_str()); + printf("Genesis nTime: %u\n", genesis.nTime); + printf("Genesis nBits: %08x\n", genesis.nBits); + printf("Genesis Nonce: %u\n\n\n", genesis.nNonce); } /** @@ -148,6 +148,7 @@ static Checkpoints::MapCheckpoints mapCheckpoints = ( 4000, uint256("0x00000030b6226239a0d3b809e3b076e3a75476fe8048dc41859a6138f63a7a28")) ( 10000, uint256("0x0a7589f8bdc5e49f55e4ba3ba8875b909e7ca4802a0505b94d0b42b5f55d1598")) ( 40000, uint256("0xc6b43d4102098d0babf3529ebe9fc772bec026a36319b534d94f6fde64b963d9")) + ( 160000, uint256("0x62cf48b78e93ef09d60c83da6da1c7b3dfa6602126e36d6756706124d2fb730b")) ; static const Checkpoints::CCheckpointData data = { &mapCheckpoints, @@ -209,6 +210,7 @@ class CMainParams : public CChainParams { nStakeMaxAge = std::numeric_limits::max(); // Unlimited stake age nModifierInterval = 15 * 60; // 15 minutes to elapse before new modifier is computed nLastPOWBlock = 10000; // Proof of Work finishes on block 10000 + nMaxTipAge = 24 * 60 * 60; genesis = CreateGenesisBlock(1473949500, 37239843, bnProofOfWorkLimit.GetCompact(), 1, (0 * COIN)); @@ -272,6 +274,7 @@ class CTestNetParams : public CMainParams { nStakeMinAge = 30 * 60; // 30 minute minimum stake age nModifierInterval = 15 * 60; // 15 minutes to elapse before new modifier is computed nLastPOWBlock = 100000; // Proof of Work finishes on block 300000 + nMaxTipAge = 24 * 60 * 60; //bool startNewChain = true; genesis = CreateTestNetGenesisBlock(1478107000, 82131309, bnProofOfWorkLimit.GetCompact(), 1, (0 * COIN)); @@ -327,7 +330,7 @@ class CRegTestParams : public CTestNetParams { bnProofOfWorkLimit = ~uint256(0) >> 22; bnProofOfStakeLimit = ~uint256(0) >> 22; nLastPOWBlock = 100; // Proof of Work finishes on block 100 - + nMaxTipAge = 24 * 60 * 60; genesis = CreateGenesisBlock(1473949500, 1427578, 0x1e00ffff, 1, (0 * COIN)); diff --git a/src/chainparams.h b/src/chainparams.h index b817c233..9855ca22 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -81,6 +81,7 @@ class CChainParams int64_t StakeMaxAge() const { return nStakeMaxAge; } int64_t ModifierInterval() const { return nModifierInterval; } int LastPOWBlock() const { return nLastPOWBlock; } + int64_t MaxTipAge() const { return nMaxTipAge; } /** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } @@ -109,6 +110,7 @@ class CChainParams int nRejectBlockOutdatedMajority; int nToCheckBlockUpgradeMajority; int nMinerThreads; + long nMaxTipAge; int64_t nTargetSpacingMax; int64_t nPoWTargetSpacing; int64_t nPoSTargetSpacing; diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index e5c7109f..d1233c13 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -182,8 +182,7 @@ bool WriteSyncCheckpoint(const uint256& hashCheckpoint) { return error("WriteSyncCheckpoint(): failed to write to txdb sync checkpoint %s", hashCheckpoint.ToString()); } - if (!pblocktree->Sync()) - return error("WriteSyncCheckpoint(): failed to commit to txdb sync checkpoint %s", hashCheckpoint.ToString()); + FlushStateToDisk(); hashSyncCheckpoint = hashCheckpoint; return true; diff --git a/src/clientversion.h b/src/clientversion.h index 48d17ed7..4e027b2d 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -17,9 +17,9 @@ //! These need to be macros, as clientversion.cpp's and silk*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 1 -#define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 1 -#define CLIENT_VERSION_BUILD 2 +#define CLIENT_VERSION_MINOR 1 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 0 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/compat.h b/src/compat.h index d6246fa2..5865f8c5 100644 --- a/src/compat.h +++ b/src/compat.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -91,4 +92,12 @@ typedef u_int SOCKET; size_t strnlen_int( const char *start, size_t max_len); +bool static inline IsSelectableSocket(SOCKET s) { +#ifdef WIN32 + return true; +#else + return (s < FD_SETSIZE); +#endif +} + #endif // SILK_COMPAT_H diff --git a/src/dns/dns.cpp b/src/dns/dns.cpp index 5ec24911..2bebae73 100644 --- a/src/dns/dns.cpp +++ b/src/dns/dns.cpp @@ -83,6 +83,15 @@ string limitString(const string& inp, unsigned int size, string message = "") return ret; } +string encodeNameVal(const CNameVal& input, const string& format) +{ + string output; + if (format == "hex") output = HexStr(input); + else if (format == "base64") output = EncodeBase64(input.data(), input.size()); + else output = stringFromNameVal(input); + return output; +} + // Calculate at which block will expire. bool CalculateExpiresAt(CNameRecord& nameRec) { @@ -366,18 +375,20 @@ bool CNamecoinHooks::RemoveNameScriptPrefix(const CScript& scriptIn, CScript& sc UniValue name_list(const UniValue& params, bool fHelp) { - if (fHelp || params.size() > 1) + if (fHelp || params.size() > 2) throw runtime_error( - "name_list []\n" - "list my own names" - ); + "name_list [name] [valuetype]\n" + "list my own names.\n" + "\nArguments:\n" + "1. name (string, required) Restrict output to specific name.\n" + "2. valuetype (string, optional) If \"hex\" or \"base64\" is specified then it will print value in corresponding format instead of string.\n" + ); if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Silk is downloading blocks..."); - CNameVal nameUniq; - if (params.size() == 1) - nameUniq = nameValFromValue(params[0]); + CNameVal nameUniq = params.size() > 0 ? nameValFromValue(params[0]) : CNameVal(); + string outputType = params.size() > 1 ? params[1].get_str() : ""; map mapNames, mapPending; GetNameList(nameUniq, mapNames, mapPending); @@ -387,7 +398,7 @@ UniValue name_list(const UniValue& params, bool fHelp) { UniValue oName(UniValue::VOBJ); oName.push_back(Pair("name", stringFromNameVal(item.second.name))); - oName.push_back(Pair("value", stringFromNameVal(item.second.value))); + oName.push_back(Pair("value", encodeNameVal(item.second.value, outputType))); if (item.second.fIsMine == false) oName.push_back(Pair("transferred", true)); oName.push_back(Pair("address", item.second.strAddress)); @@ -506,11 +517,14 @@ UniValue name_debug(const UniValue& params, bool fHelp) UniValue name_show(const UniValue& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( - "name_show [filepath]\n" + "name_show [valuetype] [filepath]\n" "Show values of a name.\n" - "If filepath is specified name value will be saved in that file in binary format (file will be overwritten!).\n" + "\nArguments:\n" + "1. name (string, required).\n" + "2. valuetype (string, optional) If \"hex\" or \"base64\" is specified then it will print value in corresponding format instead of string.\n" + "3. filepath (string, optional) save name value in binary format in specified file (file will be overwritten!).\n" ); if (IsInitialBlockDownload()) @@ -518,6 +532,7 @@ UniValue name_show(const UniValue& params, bool fHelp) UniValue oName(UniValue::VOBJ); CNameVal name = nameValFromValue(params[0]); + string outputType = params.size() > 1 ? params[1].get_str() : ""; string sName = stringFromNameVal(name); NameTxInfo nti; { @@ -538,8 +553,7 @@ UniValue name_show(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_WALLET_ERROR, "failed to decode name"); oName.push_back(Pair("name", sName)); - string value = stringFromNameVal(nti.value); - oName.push_back(Pair("value", value)); + oName.push_back(Pair("value", encodeNameVal(nti.value, outputType))); oName.push_back(Pair("txid", tx.GetHash().GetHex())); oName.push_back(Pair("address", nti.strAddress)); oName.push_back(Pair("expires_in", nameRec.nExpiresAt - chainActive.Height())); @@ -552,9 +566,9 @@ UniValue name_show(const UniValue& params, bool fHelp) oName.push_back(Pair("expired", true)); } - if (params.size() > 1) + if (params.size() > 2) { - string filepath = params[1].get_str(); + string filepath = params[2].get_str(); ofstream file; file.open(filepath.c_str(), ios::out | ios::binary | ios::trunc); if (!file.is_open()) @@ -569,13 +583,14 @@ UniValue name_show(const UniValue& params, bool fHelp) UniValue name_history (const UniValue& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (fHelp || params.size() < 1 || params.size() > 3) throw std::runtime_error ( - "name_history \"name\" ( fullhistory )\n" + "name_history [fullhistory] [valuetype]\n" "\nLook up the current and all past data for the given name.\n" "\nArguments:\n" - "1. \"name\" (string, required) the name to query for\n" - "2. \"fullhistory\" (boolean, optional) shows full history, even if name is not active\n" + "1. name (string, required) the name to query for\n" + "2. fullhistory (boolean, optional) shows full history, even if name is not active\n" + "3. valuetype (string, optional) If \"hex\" or \"base64\" is specified then it will print value in corresponding format instead of string.\n" "\nResult:\n" "[\n" " {\n" @@ -598,9 +613,8 @@ UniValue name_history (const UniValue& params, bool fHelp) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Silk is downloading blocks..."); CNameVal name = nameValFromValue(params[0]); - bool fFullHistory = false; - if (params.size() > 1) - fFullHistory = params[1].get_bool(); + bool fFullHistory = params.size() > 1 ? params[1].get_bool() : false; + string outputType = params.size() > 2 ? params[2].get_str() : ""; CNameRecord nameRec; { @@ -638,7 +652,7 @@ UniValue name_history (const UniValue& params, bool fHelp) if (nti.op == OP_NAME_UPDATE || nti.op == OP_NAME_NEW || nti.op == OP_NAME_MULTISIG) obj.push_back(Pair("days_added", nti.nRentalDays)); if (nti.op == OP_NAME_UPDATE || nti.op == OP_NAME_NEW || nti.op == OP_NAME_MULTISIG) - obj.push_back(Pair("value", stringFromNameVal(nti.value))); + obj.push_back(Pair("value", encodeNameVal(nti.value, outputType))); res.push_back(obj); } @@ -648,9 +662,11 @@ UniValue name_history (const UniValue& params, bool fHelp) UniValue name_mempool (const UniValue& params, bool fHelp) { - if (fHelp || params.size() > 0) + if (fHelp || params.size() > 1) throw std::runtime_error ( - "name_mempool\n" + "name_mempool [valuetype]\n" + "\nArguments:\n" + "1. valuetype (string, optional) If \"hex\" or \"base64\" is specified then it will print value in corresponding format instead of string.\n" "\nList pending name transactions in mempool.\n" "\nResult:\n" "[\n" @@ -670,6 +686,8 @@ UniValue name_mempool (const UniValue& params, bool fHelp) + HelpExampleRpc ("name_mempool", "" ) ); + string outputType = params.size() > 0 ? params[0].get_str() : ""; + UniValue res(UniValue::VARR); BOOST_FOREACH(const PAIRTYPE(CNameVal, set) &pairPending, mapNamePending) { @@ -695,7 +713,7 @@ UniValue name_mempool (const UniValue& params, bool fHelp) if (nti.op == OP_NAME_UPDATE || nti.op == OP_NAME_NEW || nti.op == OP_NAME_MULTISIG) obj.push_back(Pair("days_added", nti.nRentalDays)); if (nti.op == OP_NAME_UPDATE || nti.op == OP_NAME_NEW || nti.op == OP_NAME_MULTISIG) - obj.push_back(Pair("value", stringFromNameVal(nti.value))); + obj.push_back(Pair("value", encodeNameVal(nti.value, outputType))); res.push_back(obj); } @@ -710,17 +728,19 @@ bool mycompare2 (const UniValue& lhs, const UniValue& rhs) return lhs[pos].get_int() < rhs[pos].get_int(); } + UniValue name_filter(const UniValue& params, bool fHelp) { - if (fHelp || params.size() > 5) + if (fHelp || params.size() > 6) throw runtime_error( - "name_filter [[[[[regexp] maxage=0] from=0] nb=0] stat]\n" + "name_filter [regexp] [maxage=0] [from=0] [nb=0] [stat] [valuetype]\n" "scan and filter names\n" "[regexp] : apply [regexp] on names, empty means all names\n" "[maxage] : look in last [maxage] blocks\n" "[from] : show results from number [from]\n" "[nb] : show [nb] results, 0 means all\n" - "[stats] : show some stats instead of results\n" + "[stat] : show some stats instead of results\n" + "[valuetype] : if \"hex\" or \"base64\" is specified then it will print value in corresponding format instead of string.\n" "name_filter \"\" 5 # list names updated in last 5 blocks\n" "name_filter \"^id/\" # list all names from the \"id\" namespace\n" "name_filter \"^id/\" 0 0 0 stat # display stats (number of names) on active names from the \"id\" namespace\n" @@ -729,29 +749,16 @@ UniValue name_filter(const UniValue& params, bool fHelp) if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Silk is downloading blocks..."); - string strRegexp; - int nFrom = 0; - int nNb = 0; - int nMaxAge = 0; - bool fStat = false; int nCountFrom = 0; int nCountNb = 0; - if (params.size() > 0) - strRegexp = params[0].get_str(); - - if (params.size() > 1) - nMaxAge = params[1].get_int(); - - if (params.size() > 2) - nFrom = params[2].get_int(); - - if (params.size() > 3) - nNb = params[3].get_int(); - - if (params.size() > 4) - fStat = (params[4].get_str() == "stat" ? true : false); + string strRegexp = params.size() > 0 ? params[0].get_str() : ""; + int nMaxAge = params.size() > 1 ? params[1].get_int() : 0; + int nFrom = params.size() > 2 ? params[2].get_int() : 0; + int nNb = params.size() > 3 ? params[3].get_int() : 0; + bool fStat = params.size() > 4 ? (params[4].get_str() == "stat" ? true : false) : false; + string outputType = params.size() > 5 ? params[5].get_str() : ""; CNameDB dbName("r"); vector oRes; @@ -802,9 +809,7 @@ UniValue name_filter(const UniValue& params, bool fHelp) UniValue oName(UniValue::VOBJ); if (!fStat) { oName.push_back(Pair("name", name)); - - string value = stringFromNameVal(txName.value); - oName.push_back(Pair("value", limitString(value, 300, "\n...(value too large - use name_show to see full value)"))); + oName.push_back(Pair("value", limitString(encodeNameVal(txName.value, outputType), 300, "\n...(value too large - use name_show to see full value)"))); oName.push_back(Pair("registered_at", nHeight)); // pos = 2 in comparison function (above name_filter) @@ -842,31 +847,22 @@ UniValue name_filter(const UniValue& params, bool fHelp) UniValue name_scan(const UniValue& params, bool fHelp) { - if (fHelp || params.size() > 3) + if (fHelp || params.size() > 4) throw runtime_error( - "name_scan [start-name] [max-returned] [max-value-length=-1]\n" + "name_scan [start-name] [max-returned] [max-value-length=-1] [valuetype]\n" "Scan all names, starting at start-name and returning a maximum number of entries (default 500)\n" "You can also control the length of shown value (0 = full value)\n" + "[valuetype] : if \"hex\" or \"base64\" is specified then it will print value in corresponding format instead of string.\n" ); if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Silk is downloading blocks..."); - CNameVal name; + CNameVal name = params.size() > 0 ? nameValFromValue(params[0]) : CNameVal(); string strSearchName = ""; - unsigned int nMax = 500; - unsigned int mMaxShownValue = 96; - if (params.size() > 0) - { - name = nameValFromValue(params[0]); - strSearchName = params[0].get_str(); - } - - if (params.size() > 1) - nMax = params[1].get_int(); - - if (params.size() > 2) - mMaxShownValue = params[2].get_int(); + int nMax = params.size() > 1 ? params[1].get_int() : 500; + int mMaxShownValue = params.size() > 2 ? params[2].get_int() : 0; + string outputType = params.size() > 3 ? params[3].get_str() : ""; CNameDB dbName("r"); UniValue oRes(UniValue::VARR); @@ -889,9 +885,8 @@ UniValue name_scan(const UniValue& params, bool fHelp) CNameIndex txName = pairScan.second.first; int nExpiresAt = pairScan.second.second; CNameVal value = txName.value; - string sValue = stringFromNameVal(value); - oName.push_back(Pair("value", limitString(sValue, mMaxShownValue, "\n...(value too large - use name_show to see full value)"))); + oName.push_back(Pair("value", limitString(encodeNameVal(value, outputType), mMaxShownValue, "\n...(value too large - use name_show to see full value)"))); oName.push_back(Pair("expires_in", nExpiresAt - chainActive.Height())); if (nExpiresAt - chainActive.Height() <= 0) oName.push_back(Pair("expired", true)); @@ -977,25 +972,28 @@ UniValue name_new(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( - "name_new [toaddress] [valueAsFilepath]\n" + "name_new [toaddress] [valuetype]\n" "Creates new key->value pair which expires after specified number of days.\n" "Cost is square root of (1% of last PoW + 1% per year of last PoW)." - "If [valueAsFilepath] is non-zero it will interpret as a filepath and try to write file contents in binary format.\n" + "If is empty then previous value is left intact.\n" + "\nArguments:\n" + "1. name (string, required) Name to create.\n" + "2. value (string, required) Value to write.\n" + "3. toaddress (string, optional) Address of recipient. Empty string = transaction to yourself.\n" + "4. valuetype (string, optional) Interpretation of value string. Can be \"hex\", \"base64\" or filepath.\n" + " not specified or empty - Write value as a unicode string.\n" + " \"hex\" or \"base64\" - Decode value string as a binary data in hex or base64 string format.\n" + " otherwise - Decode value string as a filepath from which to read the data.\n" + HelpRequiringPassphrase()); // make sure the DDNS entry is all lowercase. CNameVal name = CNameValToLowerCase(nameValFromValue(params[0])); CNameVal value = nameValFromValue(params[1]); int nRentalDays = params[2].get_int(); - string strAddress = ""; - if (params.size() == 4) - strAddress = params[3].get_str(); - - bool fValueAsFilepath = false; - if (params.size() > 4) - fValueAsFilepath = (params[4].get_int() != 0); + string strAddress = params.size() > 3 ? params[3].get_str() : ""; + string strValueType = params.size() > 4 ? params[4].get_str() : ""; - NameTxReturn ret = name_operation(OP_NAME_NEW, name, value, nRentalDays, strAddress, fValueAsFilepath); + NameTxReturn ret = name_operation(OP_NAME_NEW, name, value, nRentalDays, strAddress, strValueType); if (!ret.ok) throw JSONRPCError(ret.err_code, ret.err_msg); return ret.hex.GetHex(); @@ -1005,24 +1003,27 @@ UniValue name_update(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( - "name_update [toaddress] [valueAsFilepath]\n" + "name_update [toaddress] [valuetype]\n" "Update name value, add days to expiration time and possibly transfer a name to diffrent address.\n" - "If [valueAsFilepath] is non-zero it will interpret as a filepath and try to write file contents in binary format.\n" + "\nArguments:\n" + "1. name (string, required) Name to update.\n" + "2. value (string, required) Value to write. Empty string = use previous value.\n" + "3. toaddress (string, optional) Address of recipient. Empty string = transaction to yourself.\n" + "4. valuetype (string, optional) Interpretation of value string. Can be \"hex\", \"base64\" or filepath.\n" + " not specified or empty - Write value as a unicode string.\n" + " \"hex\" or \"base64\" - Decode value string as a binary data in hex or base64 string format.\n" + " otherwise - Decode value string as a filepath from which to read the data.\n" + HelpRequiringPassphrase()); // make sure the DDNS entry is all lowercase. CNameVal name = CNameValToLowerCase(nameValFromValue(params[0])); CNameVal value = nameValFromValue(params[1]); int nRentalDays = params[2].get_int(); - string strAddress = ""; - if (params.size() == 4) - strAddress = params[3].get_str(); + string strAddress = params.size() > 3 ? params[3].get_str() : ""; + string strValueType = params.size() > 4 ? params[4].get_str() : ""; - bool fValueAsFilepath = false; - if (params.size() > 4) - fValueAsFilepath = (params[4].get_int() != 0); + NameTxReturn ret = name_operation(OP_NAME_UPDATE, name, value, nRentalDays, strAddress, strValueType); - NameTxReturn ret = name_operation(OP_NAME_UPDATE, name, value, nRentalDays, strAddress, fValueAsFilepath); if (!ret.ok) throw JSONRPCError(ret.err_code, ret.err_msg); return ret.hex.GetHex(); @@ -1038,21 +1039,21 @@ UniValue name_delete(const UniValue& params, bool fHelp) // make sure the DDNS entry is all lowercase. CNameVal name = CNameValToLowerCase(nameValFromValue(params[0])); - NameTxReturn ret = name_operation(OP_NAME_DELETE, name, CNameVal(), 0, ""); + NameTxReturn ret = name_operation(OP_NAME_DELETE, name, CNameVal(), 0, "", ""); if (!ret.ok) throw JSONRPCError(ret.err_code, ret.err_msg); return ret.hex.GetHex(); } -NameTxReturn name_operation(const int op, const CNameVal& name, CNameVal value, const int nRentalDays, const string strAddress, bool fValueAsFilepath) +NameTxReturn name_operation(const int op, const CNameVal& name, CNameVal value, const int nRentalDays, const string& strAddress, const string& strValueType) { NameTxReturn ret; ret.err_code = RPC_INTERNAL_ERROR; // default value in case of abnormal exit ret.err_msg = "unkown error"; ret.ok = false; - if ((op == OP_NAME_NEW || op == OP_NAME_UPDATE || op == OP_NAME_MULTISIG) && value.empty()) + if ((op == OP_NAME_NEW || op == OP_NAME_MULTISIG) && value.empty()) { ret.err_msg = "value must not be empty"; return ret; @@ -1065,31 +1066,54 @@ NameTxReturn name_operation(const int op, const CNameVal& name, CNameVal value, return ret; } - if (fValueAsFilepath) + // decode value or leave it as is + if (!strValueType.empty() && !value.empty()) { - string filepath = stringFromNameVal(value); - std::ifstream ifs; - ifs.open(filepath.c_str(), std::ios::binary | std::ios::ate); - if (!ifs) + string strValue = stringFromNameVal(value); + if (strValueType == "hex") { - ret.err_msg = "failed to open file"; - return ret; + if (!IsHex(strValue)) + { + ret.err_msg = "failed to decode value as hex"; + return ret; + } + value = ParseHex(strValue); } - std::streampos fileSize = ifs.tellg(); - if (fileSize > MAX_VALUE_LENGTH) + else if (strValueType == "base64") { - ret.err_msg = "file is larger than maximum allowed size"; - return ret; + bool fInvalid = false; + value = DecodeBase64(strValue.c_str(), &fInvalid); + if (fInvalid) + { + ret.err_msg = "failed to decode value as base64"; + return ret; + } } + else // decode as filepath + { + std::ifstream ifs; + ifs.open(strValue.c_str(), std::ios::binary | std::ios::ate); + if (!ifs) + { + ret.err_msg = "failed to open file"; + return ret; + } + std::streampos fileSize = ifs.tellg(); + if (fileSize > MAX_VALUE_LENGTH) + { + ret.err_msg = "file is larger than maximum allowed size"; + return ret; + } - ifs.clear(); - ifs.seekg(0, std::ios::beg); + ifs.clear(); + ifs.seekg(0, std::ios::beg); - value.resize(fileSize); - if (!ifs.read(reinterpret_cast(&value[0]), fileSize)) - { - ret.err_msg = "failed to read file"; - return ret; + value.resize(fileSize); + if (!ifs.read(reinterpret_cast(&value[0]), fileSize)) + { + ret.err_msg = "failed to read file"; + return ret; + } } } @@ -1155,12 +1179,17 @@ NameTxReturn name_operation(const int op, const CNameVal& name, CNameVal value, { CNameDB dbName("r"); CTransaction prevTx; - if (!GetLastTxOfName(dbName, name, prevTx)) + CNameRecord nameRec; + if (!GetLastTxOfName(dbName, name, prevTx, nameRec)) { ret.err_msg = "could not find tx with this name"; return ret; } + // empty value == reuse old value + if ((op == OP_NAME_UPDATE || op == OP_NAME_MULTISIG) && value.empty()) + value = nameRec.vtxPos.back().value; + uint256 wtxInHash = prevTx.GetHash(); if (!pwalletMain->mapWallet.count(wtxInHash)) { diff --git a/src/dns/dns.h b/src/dns/dns.h index af121cb8..88ed25bc 100644 --- a/src/dns/dns.h +++ b/src/dns/dns.h @@ -124,7 +124,7 @@ struct NameTxReturn std::string address; uint256 hex; // Transaction hash in hex }; -NameTxReturn name_operation(const int op, const CNameVal& name, CNameVal value, const int nRentalDays, const string strAddress, bool fValueAsFilepath = false); +NameTxReturn name_operation(const int op, const CNameVal& name, CNameVal value, const int nRentalDays, const string& strAddress, const string& strValueType); struct nameTempProxy diff --git a/src/dns/slkdns.cpp b/src/dns/slkdns.cpp index 68a08440..06151360 100644 --- a/src/dns/slkdns.cpp +++ b/src/dns/slkdns.cpp @@ -52,12 +52,15 @@ #define BUF_SIZE (512 + 512) #define MAX_OUT 512 // Old DNS restricts UDP to 512 bytes #define MAX_TOK 64 // Maximal TokenQty in the vsl_list, like A=IP1,..,IPn -#define MAX_DOM 10 // Maximal domain level +#define MAX_DOM 20 // Maximal domain level; min 10 is needed for NAPTR E164 #define VAL_SIZE (MAX_VALUE_LENGTH + 16) #define DNS_PREFIX "dns" #define REDEF_SYM '~' +// HT offset contains it for ENUM SPFUN +#define ENUM_FLAG (1 << 14) + /*---------------------------------------------------*/ #ifdef WIN32 @@ -84,16 +87,31 @@ int inet_pton(int af, const char *src, void *dst) } return 0; } + +char *strsep(char **s, const char *ct) +{ + char *sstart = *s; + char *end; + + if (sstart == NULL) + return NULL; + + end = strpbrk(sstart, ct); + if (end) + *end++ = '\0'; + *s = end; + return sstart; +} #endif /*---------------------------------------------------*/ SlkDns::SlkDns(const char *bind_ip, uint16_t port_no, - const char *gw_suffix, const char *allowed_suff, const char *local_fname, uint8_t verbose) - : m_status(0), m_thread(StatRun, this) { + const char *gw_suffix, const char *allowed_suff, const char *local_fname, const char *enums, const char *tollfree, uint8_t verbose) + : m_status(-1), m_thread(StatRun, this) { - // Set object to a new state - memset(this, 0, sizeof(SlkDns)); // Clear previous state + // Clear vars [m_hdr..m_verbose) + memset(&m_hdr, 0, &m_verbose - (uint8_t *)&m_hdr); // Clear previous state m_verbose = verbose; // Create and socket @@ -150,8 +168,9 @@ SlkDns::SlkDns(const char *bind_ip, uint16_t port_no, if(*p == '.') m_gw_suf_dots++; + // Activate DAP only on the public gateways, with some suffixes, like .emergate.net // If no memory, DAP inactive - this is not critical problem - m_dap_ht = (allowed_len | m_gw_suf_len)? (DNSAP*)calloc(SLKDNS_DAPSIZE, sizeof(DNSAP)) : NULL; + m_dap_ht = (allowed_len && m_gw_suf_len)? (DNSAP*)calloc(SLKDNS_DAPSIZE, sizeof(DNSAP)) : NULL; m_daprand = GetRand(0xffffffff) | 1; m_value = (char *)malloc(VAL_SIZE + BUF_SIZE + 2 + @@ -160,6 +179,19 @@ SlkDns::SlkDns(const char *bind_ip, uint16_t port_no, if(m_value == NULL) throw runtime_error("SlkDns::SlkDns: Cannot allocate buffer"); + // Temporary use m_value for parse enum-verifiers and toll-free lists, if exist + if(enums && *enums) { + char *str = strcpy(m_value, enums); + Verifier empty_ver; + while(char *p_tok = strsep(&str, "|,")) + if(*p_tok) { + if(m_verbose > 5) + LogPrintf("\tEmcDns::EmcDns: enumtrust=%s\n", p_tok); + m_verifiers[string(p_tok)] = empty_ver; + } + } // ENUMs completed + + // Assign data buffers inside m_value hyper-array m_buf = (uint8_t *)(m_value + VAL_SIZE); m_bufend = m_buf + MAX_OUT; char *varbufs = m_value + VAL_SIZE + BUF_SIZE + 2; @@ -177,18 +209,28 @@ SlkDns::SlkDns(const char *bind_ip, uint16_t port_no, *p = pos = step = 0; continue; } - if(c == '.') { + if(c == '.' || c == '$') { + *p = 64; if(p[1] > 040) { // if allowed domain is not empty - save it into ht step |= 1; - if(m_verbose > 3) - LogPrintf("\tSlkDns::SlkDns: Insert TLD=%s: pos=%u step=%u\n", p + 1, pos, step); do pos += step; while(m_ht_offset[pos] != 0); m_ht_offset[pos] = p + 1 - m_allowed_base; + const char *dnstype = "DNS"; + if(c == '$') { + m_ht_offset[pos] |= ENUM_FLAG; + char *pp = p; // ref to $ + while(--pp >= m_allowed_base && *pp >= '0' && *pp <= '9'); + if(++pp < p) + *p = atoi(pp); + dnstype = "ENUM"; + } m_allowed_qty++; + if(m_verbose > 3) + LogPrintf("\tSlkDns::EmcDns: Insert %s TLD=%s:%u\n", dnstype, p + 1, *p); } - *p = pos = step = 0; + pos = step = 0; continue; } pos = ((pos >> 7) | (pos << 1)) + c; @@ -226,9 +268,43 @@ SlkDns::SlkDns(const char *bind_ip, uint16_t port_no, m_address.sin_addr.s_addr == INADDR_ANY? "INADDR_ANY" : bind_ip, port_no, m_allowed_qty, local_qty); - m_status = 1; // Active + // Hack - pass TF file list through m_value to HandlePacket() + + if(tollfree && *tollfree) { + if(m_verbose > 3) + LogPrintf("\tSlkDns::EmcDns: Setup deferred toll-free=%s\n", tollfree); + strcpy(m_value, tollfree); + } else + m_value[0] = 0; + + m_status = 1; // Active, and maybe download } // SlkDns::SlkDns +/*---------------------------------------------------*/ +void SlkDns::AddTF(char *tf_tok) { + // Skip comments and empty lines + if(tf_tok[0] < '0') + return; + + // Clear TABs and SPs at the end of the line + char *end = strchr(tf_tok, 0); + while(*--end <= 040) { + *end = 0; + if(end <= tf_tok) + return; + } + + if(tf_tok[0] == '=') { + if(tf_tok[1]) + m_tollfree.push_back(TollFree(tf_tok + 1)); + } else + if(!m_tollfree.empty()) + m_tollfree.back().e2u.push_back(string(tf_tok)); + + if(m_verbose > 3) + LogPrintf("\tSlkDns::AddTF: Added token [%s] %u:%u\n", tf_tok, m_tollfree.size(), m_tollfree.back().e2u.size()); +} // EmcDns::AddTF + /*---------------------------------------------------*/ SlkDns::~SlkDns() { @@ -258,8 +334,8 @@ void SlkDns::StatRun(void *p) { void SlkDns::Run() { if(m_verbose > 2) LogPrintf("SlkDns::Run: started\n"); - while(m_status == 0) - MilliSleep(133); + while(m_status < 0) // not initied yet + MilliSleep(133); for( ; ; ) { m_addrLen = sizeof(m_clientAddress); @@ -329,13 +405,40 @@ void SlkDns::HandlePacket() { break; } - if(IsInitialBlockDownload()) { - m_hdr->Bits |= 2; // Server failure - not available valud nameindex DB yet - break; - } + if(m_status) { + if((m_status = IsInitialBlockDownload())) { + m_hdr->Bits |= 2; // Server failure - not available valid nameindex DB yet + break; + } else { + // Fill deferred toll-free default entries + char *tf_str = m_value; + // Iterate the list of Toll-Free fnames; can be fnames and NVS records + while(char *tf_fname = strsep(&tf_str, "|")) { + if(m_verbose > 3) + LogPrintf("\tSlkDns::HandlePacket: handle deferred toll-free=%s\n", tf_fname); + if(tf_fname[0] == '@') { // this is NVS record + string value; + if(hooks->getNameValue(string(tf_fname + 1), value)) { + char *tf_val = strcpy(m_value, value.c_str()); + while(char *tf_tok = strsep(&tf_val, "\r\n")) + AddTF(tf_tok); + } + } else { // This is file + FILE *tf = fopen(tf_fname, "r"); + if(tf != NULL) { + while(fgets(m_value, VAL_SIZE, tf)) + AddTF(m_value); + fclose(tf); + } + } // if @ + } // while tf_name + } // m_status #2 + } // m_status #1 // Handle questions here - for(uint16_t qno = 0; qno < m_hdr->QDCount && m_snd < m_bufend; qno--) { + for(uint16_t qno = 0; qno < m_hdr->QDCount && m_snd < m_bufend; qno++) { + if(m_verbose > 5) + LogPrintf("\tSlkDns::HandlePacket: qno=%u m_hdr->QDCount=%u\n", qno, m_hdr->QDCount); uint16_t rc = HandleQuery(); if(rc) { m_hdr->Bits |= rc; @@ -371,7 +474,7 @@ uint16_t SlkDns::HandleQuery() { uint8_t key[BUF_SIZE]; // Key, transformed to dot-separated LC uint8_t *key_end = key; uint8_t *domain_ndx[MAX_DOM]; // indexes to domains - uint8_t **domain_ndx_p = domain_ndx; // Ptr to end + uint8_t **domain_ndx_p = domain_ndx; // Ptr to the end // m_rcv is pointer to QNAME // Set reference to domain label @@ -386,10 +489,10 @@ uint16_t SlkDns::HandleQuery() { return 1; // Invalid request *domain_ndx_p++ = key_end; do { - *key_end++ = tolower(*m_rcv++); + *key_end++ = 040 | *m_rcv++; } while(--dom_len); *key_end++ = '.'; // Set DOT at domain end - } + } // while(dom_len) *--key_end = 0; // Remove last dot, set EOLN if(m_verbose > 3) @@ -404,9 +507,9 @@ uint16_t SlkDns::HandleQuery() { if(qclass != 1) return 4; // Not implemented - support INET only - // If thid is puplic gateway, gw-suffix can be specified, like + // If thid is public gateway, gw-suffix can be specified, like // slkdnssuffix=.xyz.com - // Followind block cuts this suffix, if exist. + // Followind block cuts this suffix, if exists. // If received domain name "xyz.com" only, keyp is empty string if(m_gw_suf_len) { // suffix defined [public DNS], need to cut @@ -468,7 +571,12 @@ uint16_t SlkDns::HandleQuery() { LogPrintf("SlkDns::HandleQuery: TLD-suffix=[.%s] in given key=%s is not allowed; return NXDOMAIN\n", p, key); return 3; // Reached EndOfList, so NXDOMAIN } - } while(m_ht_offset[pos] < 0 || strcmp((const char *)p, m_allowed_base + m_ht_offset[pos]) != 0); + } while(m_ht_offset[pos] < 0 || strcmp((const char *)p, m_allowed_base + (m_ht_offset[pos] & ~ENUM_FLAG)) != 0); + + // ENUM SPFUN works only if TLD-filter is active + if(m_ht_offset[pos] & ENUM_FLAG) + return SpfunENUM(m_allowed_base[(m_ht_offset[pos] & ~ENUM_FLAG) - 1], domain_ndx, domain_ndx_p); + } // if(m_allowed_qty) uint8_t **cur_ndx_p, **prev_ndx_p = domain_ndx_p - 2; @@ -611,7 +719,7 @@ void SlkDns::Answer_ALL(uint16_t qtype, char *buf) { case 16 : key = "TXT"; break; case 28 : key = "AAAA"; break; default: return; - } // swithc + } // switch char *tokens[MAX_TOK]; int tokQty = Tokenize(key, ",", tokens, buf); @@ -760,3 +868,313 @@ DNSAP *SlkDns::CheckDAP(uint32_t ip_addr) { return (dap->ed_size <= SLKDNS_DAPTRESHOLD)? dap : NULL; } // SlkDns::CheckDAP + +/*---------------------------------------------------*/ +// Handle Special function - phone number in the E.164 format +// to support ENUM service +int SlkDns::SpfunENUM(uint8_t len, uint8_t **domain_start, uint8_t **domain_end) { + int dom_length = domain_end - domain_start; + const char *tld = (const char*)domain_end[-1]; + + if(m_verbose > 3) + LogPrintf("\tSlkDns::SpfunENUM: Domain=[%s] N=%u TLD=[%s] Len=%u\n", + (const char*)*domain_start, dom_length, tld, len); + + do { + if(dom_length < 2) + break; // no domains for phone number - NXDOMAIN + + if(m_verifiers.empty() && m_tollfree.empty()) + break; // no verifier - all ENUMs untrusted + + // convert reversed domain record to ITU-T number + char itut_num[68], *pitut = itut_num, *pitutend = itut_num + len; + for(const uint8_t *p = domain_end[-1]; --p >= *domain_start; ) + if(*p >= '0' && *p <= '9') { + *pitut++ = *p; + if(pitut >= pitutend) + break; + } + *pitut = 0; // EOLN at phone number end + + if(pitut == itut_num) + break; // Empty phone number - NXDOMAIN + + if(m_verbose > 3) + LogPrintf("\tSlkDns::SpfunENUM: ITU-T num=[%s]\n", itut_num); + + // Itrrate all available ENUM-records, and build joined answer from them + if(!m_verifiers.empty()) + for(int16_t qno = 0; qno >= 0; qno++) { + char q_str[100]; + sprintf(q_str, "%s:%s:%u", tld, itut_num, qno); + if(m_verbose > 1) + LogPrintf("\tSlkDns::SpfunENUM Search(%s)\n", q_str); + + string value; + if(!hooks->getNameValue(string(q_str), value)) + break; + + strcpy(m_value, value.c_str()); + Answer_ENUM(q_str); + } // for + + // If notheing found in the ENUM - try to search in the Toll-Free + m_ttl = 24 * 3600; // 24h by default + boost::xpressive::smatch nameparts; + for(vector::const_iterator tf = m_tollfree.begin(); + m_hdr->ANCount == 0 && tf != m_tollfree.end(); + tf++) { + bool matched = regex_match(string(itut_num), nameparts, tf->regex); + // bool matched = regex_search(string(itut_num), nameparts, tf->regex); + if(m_verbose > 3) + LogPrintf("\tEmcDns::SpfunENUM TF-match N=[%s] RE=[%s] -> %u\n", itut_num, tf->regex_str.c_str(), matched); + if(matched) + for(vector::const_iterator e2u = tf->e2u.begin(); e2u != tf->e2u.end(); e2u++) + HandleE2U(strcpy(m_value, e2u->c_str())); + } // tf processing + + if(m_hdr->ANCount) + return 0; // if collected some answers - OK + + } while(false); + + return 3; // NXDOMAIN +} // SlkDns::SpfunENUM + +/*---------------------------------------------------*/ + +#define ENC3(a, b, c) (a | (b << 8) | (c << 16)) + +/*---------------------------------------------------*/ +// Generate answewr for found EMUM NVS record +void SlkDns::Answer_ENUM(const char *q_str) { + char *str_val = m_value; + const char *pttl; + char *e2u[VAL_SIZE / 4]; // 20kb max input, and min 4 bytes per token + uint16_t e2uN = 0; + bool sigOK = false; + + m_ttl = 24 * 3600; // 24h by default + + // Tokenize lines in the NVS-value. + // There can be prefixes SIG=, TTL=, E2U + while(char *tok = strsep(&str_val, "\n\r")) + switch((*(uint32_t*)tok & 0xffffff) | 0x202020) { + case ENC3('e', '2', 'u'): + e2u[e2uN++] = tok; + continue; + + case ENC3('t', 't', 'l'): + pttl = strchr(tok + 3, '='); + if(pttl) + m_ttl = atoi(pttl + 1); + continue; + + case ENC3('s', 'i', 'g'): + if(!sigOK) + sigOK = CheckEnumSig(q_str, strchr(tok + 3, '=')); + continue; + + default: + continue; + } // while + switch + + if(!sigOK) + return; // This ENUM-record does not contain a valid signature + + // Generate ENUM-answers here + for(uint16_t e2undx = 0; e2undx < e2uN; e2undx++) + if(m_snd < m_bufend - 24) + HandleE2U(e2u[e2undx]); + +} // EmcDns::Answer_ENUM + +/*---------------------------------------------------*/ +void SlkDns::OutS(const char *p) { + int len = strlen(strcpy((char *)m_snd + 1, p)); + *m_snd = len; + m_snd += len + 1; +} // SlkDns::OutS + +/*---------------------------------------------------*/ + // Generate ENUM-answers for a single E2U entry + // E2U+sip=100|10|!^(.*)$!sip:17771234567@in.callcentric.com! +void SlkDns::HandleE2U(char *e2u) { + char *data = strchr(e2u, '='); + if(data == NULL) + return; + + // Cleanum sufix for service; Service started from E2U + for(char *p = data; *--p <= 040; *p = 0) {} + + unsigned int ord, pref; + char re[VAL_SIZE]; + + *data++ = 0; // Remove '=' + + if(sscanf(data, "%u | %u | %s", &ord, &pref, re) != 3) + return; + + if(m_verbose > 3) + LogPrintf("\tEmcDns::HandleE2U: Parsed: %u %u %s %s\n", ord, pref, e2u, re); + + if(m_snd + strlen(re) + strlen(e2u) + 24 >= m_bufend) + return; + + Out2(m_label_ref); + Out2(0x23); // NAPTR record + Out2(1); // INET + Out4(m_ttl); + uint8_t *snd0 = m_snd; m_snd += 2; + Out2(ord); + Out2(pref); + OutS("u"); + OutS(e2u); + OutS(re); + *m_snd++ = 0; + + uint16_t len = m_snd - snd0 - 2; + *snd0++ = len >> 8; + *snd0++ = len; + + m_hdr->ANCount++; +} // SlkDns::HandleE2U + +/*---------------------------------------------------*/ +bool SlkDns::CheckEnumSig(const char *q_str, char *sig_str) { + if(sig_str == NULL) + return false; + + // skip SP/TABs in signature + while(*++sig_str <= ' '); + + char *signature = strchr(sig_str, '|'); + if(signature == NULL) + return false; + + for(char *p = signature; *--p <= 040; *p = 0) {} + *signature++ = 0; + + map::iterator it = m_verifiers.find(sig_str); + if(it == m_verifiers.end()) + return false; // Unknown verifier - do not trust it + + Verifier &ver = it->second; + + if(ver.mask < 0) { + if(ver.mask == VERMASK_BLOCKED) + return false; // Already unable to fetch + + do { + NameTxInfo nti; + CNameRecord nameRec; + CTransaction tx; + LOCK(cs_main); + CNameDB dbName("r"); + if(!dbName.ReadName(CNameVal(it->first.c_str(), it->first.c_str() + it->first.size()), nameRec)) + break; // failed to read from name DB + if(nameRec.vtxPos.size() < 1) + break; // no result returned + if(!tx.ReadFromDisk(nameRec.vtxPos.back().txPos)) + break; // failed to read from from disk + if(!DecodeNameTx(tx, nti, true)) + break; // failed to decode name + CSilkAddress addr(nti.strAddress); + if(!addr.IsValid()) + break; // Invalid address + if(!addr.GetKeyID(ver.keyID)) + break; // Address does not refer to key + + // Verifier has been read successfully, configure SRL if exist + char valbuf[VAL_SIZE], *str_val = valbuf; + memcpy(valbuf, &nti.value[0], nti.value.size()); + valbuf[nti.value.size()] = 0; + + // Proces SRL-line like + // SRL=5|srl:hello-%02x + ver.mask = VERMASK_NOSRL; + while(char *tok = strsep(&str_val, "\n\r")) + if(((*(uint32_t*)tok & 0xffffff) | 0x202020) == ENC3('s', 'r', 'l') && (tok = strchr(tok + 3, '='))) { + unsigned nbits = atoi(++tok); + if(nbits > 30) nbits = 30; + ///mask = (1 << mask) - 1; + tok = strchr(tok, '|'); + if(tok != NULL) { + do { + if(*++tok == 0) + break; // empty SRL, thus keep VERMASK_NOSRL + char *pp = strchr(tok, '%'); + if(pp != NULL) { + if(*++pp == '0') + do ++pp; while(*pp >= '0' && *pp <= '9'); + if(strchr("diouXx", *pp) == NULL) + break; // Invalid char in the template + if(strchr(pp, '%')) + break; // Not allowed 2nd % symbol + } else + nbits = 0; // Don't needed nbits/mask for no-bucket srl_tpl + + ver.srl_tpl.assign(tok); + ver.mask = (1 << nbits) - 1; + } while(false); + } // if(tok != NULL) + if(ver.mask != VERMASK_NOSRL) + break; // Mask found + } // while + if + + } while(false); + if(ver.mask < 0) { + ver.mask = VERMASK_BLOCKED; // Unable to read - block next read + return false; + } // if(ver.mask < 0) - after try-fill verifiyer + + } // if(ver.mask < 0) - main + + while(*signature <= 040 && *signature) + signature++; + + bool fInvalid = false; + vector vchSig(DecodeBase64(signature, &fInvalid)); + + if(fInvalid) + return false; + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << string(q_str); + + CPubKey pubkey; + if(!pubkey.RecoverCompact(ss.GetHash(), vchSig)) + return false; + + if(pubkey.GetID() != ver.keyID) + return false; // Signature check did not passed + + if(ver.mask == VERMASK_NOSRL) + return true; // This verifiyer does not have active SRL + + char valbuf[VAL_SIZE]; + // Compute a simple hash from q_str like enum:17771234567:0 + // This hasu must be used by verifiyers for build buckets + unsigned h = 0x5555; + for(const char *p = q_str; *p; p++) + h += (h << 5) + *p; + sprintf(valbuf, ver.srl_tpl.c_str(), h & ver.mask); + + string value; + if(!hooks->getNameValue(string(valbuf), value)) + return true; // Unable fetch SRL - as same as SRL does not exist + + // Is q_str missing in the SRL + return value.find(q_str) == string::npos; + +#if 0 + char *valstr = strcpy(valbuf, value.c_str()); + while(char *tok = strsep(&valstr, "|, \r\n\t")) + if(strcmp(tok, q_str) == 0) + reurn false; + + return true; +#endif +} // SlkDns::CheckEnumSig diff --git a/src/dns/slkdns.h b/src/dns/slkdns.h index 58751856..bf433c58 100644 --- a/src/dns/slkdns.h +++ b/src/dns/slkdns.h @@ -8,10 +8,22 @@ #ifndef SLKDNS_H #define SLKDNS_H +#include +#include + #include +#include + +using namespace std; + +#include "pubkey.h" #define SLKDNS_DAPSIZE (8 * 1024) -#define SLKDNS_DAPTRESHOLD 300 // 20K/min limit answer +#define SLKDNS_DAPTRESHOLD 3000 // 200K/min limit answer + +#define VERMASK_NEW -1 +#define VERMASK_BLOCKED -2 +#define VERMASK_NOSRL (1 << 24) struct DNSHeader { static const uint32_t QR_MASK = 0x8000; @@ -41,10 +53,28 @@ struct DNSAP { // DNS Amplifier Protector ExpDecay structure uint16_t ed_size; // ExpDecay output size in 64-byte units } __attribute__((packed)); +struct Verifier { + Verifier() : mask(VERMASK_NEW) {} // -1 == uninited, neg != -1 == cant fetch + int32_t mask; // Signature Revocation List mask + string srl_tpl; // Signature Revocation List template + CKeyID keyID; // Key for verify message +}; // 72 bytes = 18 words + +struct TollFree { + TollFree(const char *re) : + regex(boost::xpressive::sregex::compile(string(re))), regex_str(re) + {} + boost::xpressive::sregex regex; + string regex_str; + vector e2u; +}; + class SlkDns { public: SlkDns(const char *bind_ip, uint16_t port_no, - const char *gw_suffix, const char *allowed_suff, const char *local_fname, uint8_t verbose); + const char *gw_suffix, const char *allowed_suff, + const char *local_fname, const char *enums, const char *tollfree, + uint8_t verbose); ~SlkDns(); void Run(); @@ -61,13 +91,23 @@ class SlkDns { void Fill_RD_DName(char *txt, uint8_t mxsz, int8_t txtcor); int TryMakeref(uint16_t label_ref); + // Handle Special function - phone number in the E.164 format + // to support ENUM service + int SpfunENUM(uint8_t len, uint8_t **domain_start, uint8_t **domain_end); + // Generate answewr for found EMUM NVS record + void Answer_ENUM(const char *q_str); + void HandleE2U(char *e2u); + bool CheckEnumSig(const char *q_str, char *sig_str); + void AddTF(char *tf_tok); + // Returns x = hash index to update size; x==NULL = disable; DNSAP *CheckDAP(uint32_t ip_addr); inline void Out2(uint16_t x) { x = htons(x); memcpy(m_snd, &x, 2); m_snd += 2; } inline void Out4(uint32_t x) { x = htonl(x); memcpy(m_snd, &x, 4); m_snd += 4; } + void OutS(const char *p); - DNSHeader *m_hdr; + DNSHeader *m_hdr; // 1st bzero element DNSAP *m_dap_ht; // Hashtable for DAP; index is hash(IP) char *m_value; const char *m_gw_suffix; @@ -78,18 +118,20 @@ class SlkDns { uint32_t m_ttl; uint16_t m_label_ref; uint16_t m_gw_suf_len; - uint8_t m_gw_suf_dots; - uint8_t m_verbose; - uint8_t m_allowed_qty; - uint8_t m_status; char *m_allowed_base; char *m_local_base; int16_t m_ht_offset[0x100]; // Hashtable for allowed TLD-suffixes(>0) and local names(<0) struct sockaddr_in m_clientAddress; struct sockaddr_in m_address; socklen_t m_addrLen; - + uint8_t m_gw_suf_dots; + uint8_t m_allowed_qty; + uint8_t m_verbose; // LAST bzero element + + int8_t m_status; boost::thread m_thread; + map m_verifiers; + vector m_tollfree; }; // class SlkDns #endif // SLKDNS_H diff --git a/src/init.cpp b/src/init.cpp index 86187965..06fefdb0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -279,6 +279,7 @@ std::string HelpMessage(HelpMessageMode mode) string strUsage = _("Options:") + "\n"; strUsage += " -? " + _("This help message") + "\n"; strUsage += " -alertnotify= " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; + strUsage += " -alerts " + strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS); strUsage += " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; strUsage += " -checkblocks= " + strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), 288) + "\n"; strUsage += " -checklevel= " + strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), 3) + "\n"; @@ -640,7 +641,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified if (SoftSetBoolArg("-listen", true)) - LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n"); + LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); + // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 + // to listen locally, so don't rely on this happening through -listen below. + if (SoftSetBoolArg("-upnp", false)) + LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); } if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { @@ -745,12 +750,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; - // Continue to put "/P2SH/" in the coinbase to monitor - // BIP16 support. - // This can be removed eventually... - const char* pszP2SH = "/P2SH/"; - COINBASE_FLAGS << std::vector(pszP2SH, pszP2SH+strlen(pszP2SH)); - // Fee-per-kilobyte amount considered the same as "free" // If you are mining, be careful setting this: // if you set it to zero then @@ -825,6 +824,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fIsBareMultisigStd = GetArg("-permitbaremultisig", true) != 0; nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); + fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS); + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Sanity check @@ -1423,8 +1424,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) string bind_ip = GetArg("-slkdnsbindip", ""); string allowed = GetArg("-slkdnsallowed", ""); string localcf = GetArg("-slkdnslocalcf", ""); + string enums = GetArg("-enumtrust", ""); + string tf = GetArg("-enumtollfree", ""); slkdns = new SlkDns(bind_ip.c_str(), port, - suffix.c_str(), allowed.c_str(), localcf.c_str(), verbose); + suffix.c_str(), allowed.c_str(), localcf.c_str(), enums.c_str(), tf.c_str(), verbose); LogPrintf("DNS server started\n"); } diff --git a/src/kernel.cpp b/src/kernel.cpp index 91c38884..b8196b6a 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -32,6 +32,8 @@ static std::map mapStakeModifierCheckpoints = ( 8000, 0x481e05e4u ) ( 16000, 0xd6f5eccbu ) ( 32000, 0x7f331195u ) + ( 64000, 0xa9ee80a2u ) + ( 128000, 0xed217450u ) ; // Get time weight diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index 37da1f1b..e75050c7 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -61,7 +61,7 @@ CLevelDBWrapper::CLevelDBWrapper(const boost::filesystem::path& path, size_t nCa } else { if (fWipe) { LogPrintf("Wiping LevelDB in %s\n", path.string()); - leveldb::DestroyDB(path.string(), options); + leveldb::Status result = leveldb::DestroyDB(path.string(), options); } TryCreateDirectory(path); LogPrintf("Opening LevelDB in %s\n", path.string()); diff --git a/src/main.cpp b/src/main.cpp index 64675cde..b5cc0fe2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ #include "kernel.h" #include "keystore.h" #include "dns/dns.h" +#include "rpc/rpcserver.h" #include @@ -63,6 +64,7 @@ bool fTxIndex = true; bool fIsBareMultisigStd = true; bool fCheckBlockIndex = false; size_t nCoinCacheUsage = 5000 * 300; +bool fAlerts = DEFAULT_ALERTS; // ppcoin values string strMintWarning; @@ -1288,6 +1290,31 @@ CAmount GetProofOfStakeReward() bool IsInitialBlockDownload() { + static bool rc = true; // Default - we're in the Initial Download + do { + if(rc == false) + break; // ret false + + const CChainParams& chainParams = Params(); + LOCK(cs_main); + + if (fImporting || fReindex) + break; // ret true + + int cah = chainActive.Height(); + + if(cah < Checkpoints::GetTotalBlocksEstimate() || cah < pindexBestHeader->nHeight - 24 * 6) + break; // ret true + + rc = pindexBestHeader->GetBlockTime() < GetTime() - chainParams.MaxTipAge(); + + } while(false); + + return rc; +} + +#if 0 +const CChainParams& chainParams = Params(); LOCK(cs_main); if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) return true; @@ -1295,11 +1322,11 @@ bool IsInitialBlockDownload() if (lockIBDState) return false; bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - 24 * 60 * 60); + pindexBestHeader->GetBlockTime() < GetTime() - chainParams.MaxTipAge()); if (!state) lockIBDState = true; return state; -} +#endif bool fLargeWorkForkFound = false; bool fLargeWorkInvalidChainFound = false; @@ -1838,6 +1865,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; flags |= SCRIPT_VERIFY_DERSIG; + // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4 + // blocks, when 75% of the network has upgraded: + if (block.nVersion >= 2 && CBlockIndex::IsSuperMajority(2, pindex->pprev, Params().EnforceBlockUpgradeMajority())) { + flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; + } + CBlockUndo blockundo; CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -2868,7 +2901,15 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, bool fProofOfStake, C CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); if (pcheckpoint && nHeight < pcheckpoint->nHeight) return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); - + + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 2 && + CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) + { + return state.Invalid(error("%s : rejected nVersion=1 block", __func__), + REJECT_OBSOLETE, "bad-version"); + } + return true; } @@ -3168,7 +3209,7 @@ bool AbortNode(const std::string &strMessage, const std::string &userMessage) { strMiscWarning = strMessage; LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox( - userMessage.empty() ? _("Error: A fatal internal error occured, see debug.log for details") : userMessage, + userMessage.empty() ? _("Error: A fatal internal error occurred, see debug.log for details") : userMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; @@ -3509,9 +3550,8 @@ bool InitBlockIndex() { // write checkpoint master key to db if (!pblocktree->WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey)) return error("LoadBlockIndex() : failed to write new checkpoint master key to db"); - if (!pblocktree->Sync()) - return error("LoadBlockIndex() : failed to commit new checkpoint master key to db"); - if ((Params().NetworkIDString() == "main") && !CheckpointsSync::ResetSyncCheckpoint()) + FlushStateToDisk(); + if (!CheckpointsSync::ResetSyncCheckpoint()) return error("LoadBlockIndex() : failed to reset sync-checkpoint"); } }*/ @@ -4717,7 +4757,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "alert") + else if (fAlerts && strCommand == "alert") { CAlert alert; vRecv >> alert; diff --git a/src/main.h b/src/main.h index a68fd854..48f61199 100644 --- a/src/main.h +++ b/src/main.h @@ -57,6 +57,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 3145728; //3MB (75% of MAX_BL static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; /** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 200000; +/** Default for accepting alerts from the P2P network. */ +static const bool DEFAULT_ALERTS = true; /** The maximum size for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_SIZE = 400000; /** The maximum allowed number of signature check operations in a block (network rule) */ @@ -81,8 +83,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB -/** 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 number of script-checking threads allowed */ static const int MAX_SCRIPTCHECK_THREADS = 16; /** -par default (number of script-checking threads, 0 = auto) */ @@ -141,6 +141,7 @@ extern bool fIsBareMultisigStd; extern bool fCheckBlockIndex; extern size_t nCoinCacheUsage; extern CFeeRate minRelayTxFee; +extern bool fAlerts; /** Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index d5eced1a..8b467694 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -140,7 +140,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch) { // traverse the partial tree unsigned int nBitsUsed = 0, nHashUsed = 0; uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch); - // verify that no problems occured during the tree traversal + // verify that no problems occurred during the tree traversal if (fBad) return 0; // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence) diff --git a/src/net.cpp b/src/net.cpp index f7051460..cb5a9c73 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -242,6 +242,7 @@ void AdvertizeLocal(CNode *pnode) } if (addrLocal.IsRoutable()) { + LogPrintf("AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); pnode->PushAddress(addrLocal); } } @@ -361,6 +362,7 @@ uint64_t CNode::nTotalBytesSent = 0; CCriticalSection CNode::cs_totalBytesRecv; CCriticalSection CNode::cs_totalBytesSent; + CNode* FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); @@ -415,24 +417,10 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) { - if (pszDest && addrConnect.IsValid()) { - // It is possible that we already have a connection to the IP/port pszDest resolved to. - // In that case, drop the connection that was just created, and return the existing CNode instead. - // Also store the name we used to connect in that CNode, so that future FindNode() calls to that - // name catch this early. - CNode* pnode = FindNode((CService)addrConnect); - if (pnode) - { - pnode->AddRef(); - { - LOCK(cs_vNodes); - if (pnode->addrName.empty()) { - pnode->addrName = std::string(pszDest); - } - } - CloseSocket(hSocket); - return pnode; - } + if (!IsSelectableSocket(hSocket)) { + LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); + CloseSocket(hSocket); + return NULL; } addrman.Attempt(addrConnect); @@ -499,6 +487,7 @@ CCriticalSection CNode::cs_setBanned; void CNode::ClearBanned() { + LOCK(cs_setBanned); setBanned.clear(); } @@ -607,7 +596,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) return false; if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) { - LogPrint("net", "Oversized message from peer=%i, disconnecting", GetId()); + LogPrint("net", "Oversized message from peer=%i, disconnecting\n", GetId()); return false; } @@ -903,8 +892,14 @@ void ThreadSocketHandler() if (nErr != WSAEWOULDBLOCK) LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); } + else if (!IsSelectableSocket(hSocket)) + { + LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString()); + CloseSocket(hSocket); + } else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS) { + LogPrint("net", "connection from %s dropped (full)\n", addr.ToString()); CloseSocket(hSocket); } else if (CNode::IsBanned(addr) && !whitelisted) @@ -914,6 +909,14 @@ void ThreadSocketHandler() } else { + // According to the internet TCP_NODELAY is not carried into accepted sockets + // on all platforms. Set it again here just to be sure. + int set = 1; +#ifdef WIN32 + setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int)); +#else + setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); +#endif CNode* pnode = new CNode(hSocket, addr, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; @@ -1499,11 +1502,6 @@ void ThreadMessageHandler() } } - - - - - bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted) { strError = ""; @@ -1527,14 +1525,26 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste return false; } + if (!IsSelectableSocket(hListenSocket)) + { + strError = "Error: Couldn't create a listenable socket for incoming connections"; + LogPrintf("%s\n", strError); + return false; + } + #ifndef WIN32 #ifdef SO_NOSIGPIPE // Different way of disabling SIGPIPE on BSD setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int)); #endif // Allow binding if the port is still in TIME_WAIT state after - // the program was closed and restarted. Not an issue on windows! + // the program was closed and restarted. setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); + // Disable Nagle's algorithm + setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int)); +#else + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int)); + setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int)); #endif // Set to non-blocking, incoming connections will also inherit this diff --git a/src/netbase.cpp b/src/netbase.cpp index 4194ce85..f3656661 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -273,6 +273,9 @@ bool static InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSock } else { // Other error or blocking int nErr = WSAGetLastError(); if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { + if (!IsSelectableSocket(hSocket)) { + return false; + } struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait)); fd_set fdset; FD_ZERO(&fdset); @@ -410,12 +413,19 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe if (hSocket == INVALID_SOCKET) return false; -#ifdef SO_NOSIGPIPE int set = 1; +#ifdef SO_NOSIGPIPE // Different way of disabling SIGPIPE on BSD setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); #endif + //Disable Nagle's algorithm +#ifdef WIN32 + setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int)); +#else + setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); +#endif + // Set to non-blocking if (!SetSocketNonBlocking(hSocket, true)) return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError())); diff --git a/src/primitives/block.h b/src/primitives/block.h index 80d9cd8c..0d8fafeb 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -39,7 +39,7 @@ class CBlockHeader public: // header static const int32_t NORMAL_SERIALIZE_SIZE=80; - static const int32_t CURRENT_VERSION=1; + static const int32_t CURRENT_VERSION=2; int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; diff --git a/src/qt/calcdialog.cpp b/src/qt/calcdialog.cpp index 88955e5d..29578a22 100644 --- a/src/qt/calcdialog.cpp +++ b/src/qt/calcdialog.cpp @@ -41,8 +41,7 @@ void calcDialog::pushButtonClicked() float rate = 0; CWalletTx tx; CWalletTx ptx; - CWallet *wallet; - const TransactionRecord *wtx; + const TransactionRecord *wtx = nullptr; uint256 hash; QString strRewardSize = ui->stakeDaysEdit->text(); // strUserSize, blockSizeEdit diff --git a/src/qt/dnspage.cpp b/src/qt/dnspage.cpp index 3e6211a9..059f3521 100644 --- a/src/qt/dnspage.cpp +++ b/src/qt/dnspage.cpp @@ -126,6 +126,7 @@ DNSPage::DNSPage(QWidget *parent) : connect(copyAllAction, SIGNAL(triggered()), this, SLOT(onCopyAllAction())); connect(saveValueAsBinaryAction, SIGNAL(triggered()), this, SLOT(onSaveValueAsBinaryAction())); + connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); @@ -332,19 +333,19 @@ void DNSPage::on_submitNameButton_clicked() { nHeight = NameTableEntry::NAME_NEW; status = CT_NEW; - res = name_operation(OP_NAME_NEW, name, value, days, newAddress.toStdString()); + res = name_operation(OP_NAME_NEW, name, value, days, newAddress.toStdString(), ""); } else if (txType == "NAME_UPDATE") { nHeight = NameTableEntry::NAME_UPDATE; status = CT_UPDATED; - res = name_operation(OP_NAME_UPDATE, name, value, days, newAddress.toStdString()); + res = name_operation(OP_NAME_UPDATE, name, value, days, newAddress.toStdString(), ""); } else if (txType == "NAME_DELETE") { nHeight = NameTableEntry::NAME_DELETE; status = CT_UPDATED; //we still want to display this name until it is deleted - res = name_operation(OP_NAME_DELETE, name, CNameVal(), 0, ""); + res = name_operation(OP_NAME_DELETE, name, CNameVal(), 0, "", ""); } importedAsBinaryFile.clear(); diff --git a/src/qt/dnspage.h b/src/qt/dnspage.h index fa3f9c75..73f49bd9 100644 --- a/src/qt/dnspage.h +++ b/src/qt/dnspage.h @@ -92,6 +92,10 @@ private slots: void on_importValueButton_clicked(); void on_registerValue_textChanged(); void on_tableView_doubleClicked(const QModelIndex& index); + +signals: + void doubleClicked(const QModelIndex&); + }; #endif // DNSPAGE_H diff --git a/src/qt/forms/dnspage.ui b/src/qt/forms/dnspage.ui index 61e19989..e9a6079a 100644 --- a/src/qt/forms/dnspage.ui +++ b/src/qt/forms/dnspage.ui @@ -346,7 +346,7 @@ color: rgb(0, 0, 0); Qt::CustomContextMenu - Double-click name to configure + Double-click name to copy all values Qt::ScrollBarAlwaysOn diff --git a/src/qt/multisigdialog.cpp b/src/qt/multisigdialog.cpp index 9b697afa..3bdad2fc 100644 --- a/src/qt/multisigdialog.cpp +++ b/src/qt/multisigdialog.cpp @@ -106,9 +106,10 @@ bool MultisigDialog::AdvertisePublicKeyForMultiSig(const std::string& address, c string strAddress = ""; CNameVal name = nameValFromString("address:" + address); CNameVal value = nameValFromString(publickey); + string strValue = stringFromNameVal(value); int nRentalDays = 35; - NameTxReturn ret = name_operation(OP_NAME_MULTISIG, name, value, nRentalDays, address); + NameTxReturn ret = name_operation(OP_NAME_MULTISIG, name, value, nRentalDays, address, strValue); if (!ret.ok) { QMessageBox::critical(this, tr("Multisig Dialog: Advertise PublicKey Error!"), tr("%1: %2").arg(ret.err_code).arg(ret.err_msg.c_str())); diff --git a/src/qt/silk.cpp b/src/qt/silk.cpp index 8dc35544..ef17f938 100644 --- a/src/qt/silk.cpp +++ b/src/qt/silk.cpp @@ -566,7 +566,7 @@ int main(int argc, char *argv[]) // Show help message immediately after parsing command-line options (for "-lang") and setting locale, // but before showing splash screen. - if (mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) + if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) { HelpMessageDialog help(NULL, mapArgs.count("-version")); help.showOrPrint(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 475df96d..9d0b5993 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -263,7 +263,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact { return InvalidAddress; } - if(rcp.amount <= MIN_TXOUT_AMOUNT) + if(rcp.amount < MIN_TXOUT_AMOUNT) { return InvalidAmount; } diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index feb124e6..820b306a 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -73,6 +73,9 @@ WalletView::WalletView(QWidget *parent): // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); + // Double-clicking on a name on the name page copies all values + connect(dnsPage, SIGNAL(doubleClicked(QModelIndex)), dnsPage, SLOT(onCopyAllAction())); + // Clicking on "Export" allows to export the transaction list connect(exportButton, SIGNAL(clicked()), transactionView, SLOT(exportClicked())); diff --git a/src/rpc/rpcclient.cpp b/src/rpc/rpcclient.cpp index d4181ade..7383e8db 100644 --- a/src/rpc/rpcclient.cpp +++ b/src/rpc/rpcclient.cpp @@ -103,9 +103,7 @@ static const CRPCConvertParam vRPCConvertParams[] = // Silk: { "name_new", 2 }, - { "name_new", 4 }, { "name_update", 2 }, - { "name_update", 4 }, { "name_filter", 1 }, { "name_filter", 2 }, { "name_filter", 3 }, diff --git a/src/rpc/rpcmisc.cpp b/src/rpc/rpcmisc.cpp index 567264e6..27d49f9b 100644 --- a/src/rpc/rpcmisc.cpp +++ b/src/rpc/rpcmisc.cpp @@ -98,6 +98,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) if (pwalletMain) { obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + obj.push_back(Pair("encrypted", pwalletMain->IsCrypted())); + obj.push_back(Pair("mintonly", fWalletUnlockMintOnly)); } if (pwalletMain && pwalletMain->IsCrypted()) obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); diff --git a/src/rpc/rpcprotocol.cpp b/src/rpc/rpcprotocol.cpp index ad45a750..3fc73370 100644 --- a/src/rpc/rpcprotocol.cpp +++ b/src/rpc/rpcprotocol.cpp @@ -263,7 +263,7 @@ string JSONRPCRequest(const string& strMethod, const UniValue& params, const Uni request.push_back(Pair("method", strMethod)); request.push_back(Pair("params", params)); request.push_back(Pair("id", id)); - return request.write() + "\n"; + return request.write(0, 0, GetBoolArg("-legacyrpc", true)) + "\n"; } UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id) @@ -281,7 +281,8 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id) { UniValue reply = JSONRPCReplyObj(result, error, id); - return reply.write() + "\n"; + static bool legacy = GetBoolArg("-legacyrpc", true); + return reply.write(0, 0, legacy) + "\n"; } UniValue JSONRPCError(int code, const string& message) diff --git a/src/rpc/rpcregister.h b/src/rpc/rpcregister.h index f715dcbd..b80e4bc2 100644 --- a/src/rpc/rpcregister.h +++ b/src/rpc/rpcregister.h @@ -6,8 +6,9 @@ #ifndef SILK_RPCREGISTER_H #define SILK_RPCREGISTER_H -/** These are in one header file to avoid creating tons of single-function - * headers for everything under src/rpc/* */ +// These are in one header file to avoid creating tons of single-function +// headers for everything under src/rpc/* + class CRPCTable; /** Register block chain RPC commands */ diff --git a/src/rpc/rpcserver.cpp b/src/rpc/rpcserver.cpp index 05e8e42c..8995e3fb 100644 --- a/src/rpc/rpcserver.cpp +++ b/src/rpc/rpcserver.cpp @@ -91,13 +91,19 @@ void RPCTypeCheckObj(const UniValue& o, } } +static inline int64_t roundint64(double d) +{ + return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); +} + CAmount AmountFromValue(const UniValue& value) { if (!value.isNum() && !value.isStr()) throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); - CAmount amount; - if (!ParseFixedPoint(value.getValStr(), 8, &amount)) + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > MAX_MONEY) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + CAmount amount = roundint64(dAmount * COIN); if (!MoneyRange(amount)) throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); return amount; @@ -110,7 +116,7 @@ UniValue ValueFromAmount(const CAmount& amount) int64_t quotient = n_abs / COIN; int64_t remainder = n_abs % COIN; return UniValue(UniValue::VNUM, - strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder)); + strprintf("%s%d.%06d", sign ? "-" : "", quotient, remainder)); } uint256 ParseHashV(const UniValue& v, string strName) @@ -766,7 +772,8 @@ static string JSONRPCExecBatch(const UniValue& vReq) for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) ret.push_back(JSONRPCExecOne(vReq[reqIdx])); - return ret.write() + "\n"; + static bool legacy = GetBoolArg("-legacyrpc", true); + return ret.write(0, 0, legacy) + "\n"; } static bool HTTPReq_JSONRPC(AcceptedConnection *conn, @@ -798,7 +805,8 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn, { // Parse request UniValue valRequest; - if (!valRequest.read(strRequest)) + static bool legacy = GetBoolArg("-legacyrpc", true); + if (!valRequest.read(strRequest, legacy ? 2 : 0)) throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); // Return immediately if in warmup diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index aba1d68a..516f07c2 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -341,7 +341,49 @@ bool EvalScript(vector >& stack, const CScript& script, un case OP_NOP: break; - case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_CHECKLOCKTIMEVERIFY: + { + if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { + // not enabled; treat as a NOP2 + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + break; + } + + if (stack.size() < 1) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // Note that elsewhere numeric opcodes are limited to + // operands in the range -2**31+1 to 2**31-1, however it is + // legal for opcodes to produce results exceeding that + // range. This limitation is implemented by CScriptNum's + // default 4-byte limit. + // + // If we kept to that limit we'd have a year 2038 problem, + // even though the nLockTime field in transactions + // themselves is uint32 which only becomes meaningless + // after the year 2106. + // + // Thus as a special case we tell CScriptNum to accept up + // to 5-byte bignums, which are good until 2**39-1, well + // beyond the 2**32-1 limit of the nLockTime field itself. + const CScriptNum nLockTime(stacktop(-1), fRequireMinimal, 5); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKLOCKTIMEVERIFY. + if (nLockTime < 0) + return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME); + + // Actually compare the specified lock time with the transaction. + if (!checker.CheckLockTime(nLockTime)) + return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME); + + break; + } + + case OP_NOP1: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) @@ -1089,6 +1131,42 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +{ + // There are two times of nLockTime: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nLockTime < LOCKTIME_THRESHOLD. + // + // We want to compare apples to apples, so fail the script + // unless the type of nLockTime being tested is the same as + // the nLockTime in the transaction. + if (!( + (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) + )) + return false; + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nLockTime > (int64_t)txTo->nLockTime) + return false; + + // Finally the nLockTime feature can be disabled and thus + // CHECKLOCKTIMEVERIFY bypassed if every txin has been + // finalized by setting nSequence to maxint. The + // transaction would be allowed into the blockchain, making + // the opcode ineffective. + // + // Testing if this vin is not final is sufficient to + // prevent this condition. Alternatively we could test all + // inputs, but testing just this input minimizes the data + // required to prove correct CHECKLOCKTIMEVERIFY execution. + if (txTo->vin[nIn].IsFinal()) + return false; + + return true; +} + bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool fNamecoin) { set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 5ae604d5..a2c8921f 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -70,8 +70,12 @@ enum // discouraged NOPs fails the script. This verification flag will never be // a mandatory flag applied to scripts in a block. NOPs that are not // executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected. - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7) + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7), + // Verify CHECKLOCKTIMEVERIFY + // + // See BIP65 for details. + SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), }; uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); @@ -84,6 +88,11 @@ class BaseSignatureChecker return false; } + virtual bool CheckLockTime(const CScriptNum& nLockTime) const + { + return false; + } + virtual ~BaseSignatureChecker() {} }; @@ -99,6 +108,7 @@ class TransactionSignatureChecker : public BaseSignatureChecker public: TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; + bool CheckLockTime(const CScriptNum& nLockTime) const; }; class MutableTransactionSignatureChecker : public TransactionSignatureChecker diff --git a/src/script/script.h b/src/script/script.h index 042f6ede..4702d28b 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -21,6 +21,10 @@ typedef std::vector valtype; static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes +// 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 + template std::vector ToByteVector(const T& in) { @@ -153,6 +157,7 @@ enum opcodetype // expansion OP_NOP1 = 0xb0, OP_NOP2 = 0xb1, + OP_CHECKLOCKTIMEVERIFY = OP_NOP2, OP_NOP3 = 0xb2, OP_NOP4 = 0xb3, OP_NOP5 = 0xb4, @@ -203,7 +208,10 @@ class CScriptNum m_value = n; } - explicit CScriptNum(const std::vector& vch, bool fRequireMinimal) + static const size_t nDefaultMaxNumSize = 4; + + explicit CScriptNum(const std::vector& vch, bool fRequireMinimal, + const size_t nMaxNumSize = nDefaultMaxNumSize) { if (vch.size() > nMaxNumSize) { throw scriptnum_error("script number overflow"); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index f6652807..40379052 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -48,6 +48,10 @@ const char* ScriptErrorString(const ScriptError serror) return "OP_RETURN was encountered"; case SCRIPT_ERR_UNBALANCED_CONDITIONAL: return "Invalid OP_IF construction"; + case SCRIPT_ERR_NEGATIVE_LOCKTIME: + return "Negative locktime"; + case SCRIPT_ERR_UNSATISFIED_LOCKTIME: + return "Locktime requirement not satisfied"; case SCRIPT_ERR_SIG_HASHTYPE: return "Signature hash type missing or not understood"; case SCRIPT_ERR_SIG_DER: diff --git a/src/script/script_error.h b/src/script/script_error.h index 7121575b..2ffd70fe 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -36,6 +36,10 @@ typedef enum ScriptError_t SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, SCRIPT_ERR_UNBALANCED_CONDITIONAL, + /* OP_CHECKLOCKTIMEVERIFY */ + SCRIPT_ERR_NEGATIVE_LOCKTIME, + SCRIPT_ERR_UNSATISFIED_LOCKTIME, + /* BIP62 */ SCRIPT_ERR_SIG_HASHTYPE, SCRIPT_ERR_SIG_DER, diff --git a/src/script/silkconsensus.h b/src/script/silkconsensus.h index c7f3f585..1620d407 100644 --- a/src/script/silkconsensus.h +++ b/src/script/silkconsensus.h @@ -48,6 +48,7 @@ enum silkconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, silkconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts silkconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance + silkconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) }; /// Returns 1 if the input nIn of the serialized transaction pointed to by diff --git a/src/script/standard.h b/src/script/standard.h index 1e741bda..0228f69d 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -50,7 +50,9 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_MINIMALDATA | SCRIPT_VERIFY_NULLDUMMY | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS; + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | + SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | + SCRIPT_VERIFY_LOW_S; /** For convenience, standard but not mandatory verify flags. */ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; diff --git a/src/silk-cli.cpp b/src/silk-cli.cpp index af5d3923..c70cf1b9 100644 --- a/src/silk-cli.cpp +++ b/src/silk-cli.cpp @@ -68,7 +68,7 @@ static bool AppInitRPC(int argc, char* argv[]) // Parameters // ParseParameters(argc, argv); - if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) { + if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) { std::string strUsage = _("Silk Core RPC client version") + " " + FormatFullVersion() + "\n"; if (!mapArgs.count("-version")) { strUsage += "\n" + _("Usage:") + "\n" + @@ -147,9 +147,17 @@ UniValue CallRPC(const string& strMethod, const UniValue& params) else if (strReply.empty()) throw runtime_error("no response from server"); + // silk: + // we get emercoind output (possibly binary) of .write() that either: + // 1) escapes some unicode characters (defined in univalue_escapes.h) + // 2) escapes all unicode character as in legacy json_spirit + // we must get back the same string byte by byte by invoking .read() with mode=1 or mode=2. + // finaly we "return reply" and it gets .write() in upper function + int mode = GetBoolArg("-legacyrpc", true) ? 2 : (strMethod.rfind("name_", 0) != std::string::npos ? 1 : 0); + // Parse reply UniValue valReply(UniValue::VSTR); - if (!valReply.read(strReply)) + if (!valReply.read(strReply, mode)) throw runtime_error("couldn't parse reply from server"); const UniValue& reply = valReply.get_obj(); if (reply.empty()) diff --git a/src/silk-tx.cpp b/src/silk-tx.cpp index 9b802e4f..220fdbe0 100644 --- a/src/silk-tx.cpp +++ b/src/silk-tx.cpp @@ -47,7 +47,7 @@ static bool AppInitRawTx(int argc, char* argv[]) fCreateBlank = GetBoolArg("-create", false); - if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help")) + if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help")) { // First part of help message is specific to this utility std::string strUsage = _("Silk Core silk-tx utility version") + " " + FormatFullVersion() + "\n\n" + diff --git a/src/silkd.cpp b/src/silkd.cpp index 1fd985e9..d127b1ca 100644 --- a/src/silkd.cpp +++ b/src/silkd.cpp @@ -70,7 +70,7 @@ bool AppInit(int argc, char* argv[]) ParseParameters(argc, argv); // Process help and version before taking care about datadir - if (mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) + if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) { std::string strUsage = _("Silk Core Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n"; diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 638a705f..7df2eaf3 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -103,5 +103,78 @@ [[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]], "01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "P2SH"], +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument just beyond tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument missing"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blockheight nLockTime=0"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument negative with by-blocktime nLockTime=500,000,000"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Input locked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Another input being unlocked isn't sufficient; the CHECKLOCKTIMEVERIFY-using input must be unlocked"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"] , + ["0000000000000000000000000000000000000000000000000000000000000200", 1, "1"]], +"010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument/tx height/time mismatch, both versions"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Argument 2^32 with nLockTime=2^32-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967296 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Same, but with nLockTime=2^31-1"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "P2SH,CHECKLOCKTIMEVERIFY"], + +["6 byte non-minimally-encoded arguments are invalid even in their contents are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Failure due to failing CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index aa8e5ca6..0fb4542e 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -177,6 +177,47 @@ ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]], "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH"], +["CHECKLOCKTIMEVERIFY tests"], + +["By-height locks, with argument == 0 and == tx nLockTime"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["By-time locks, with argument just beyond tx nLockTime (but within numerical boundries)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Any non-maxint nSequence is fine"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000feffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["The argument can be calculated rather than created directly by a PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 1ADD NOP2 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Perhaps even by an ADD producing a 5-byte result that is out of bounds for other opcodes"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483647 2147483647 ADD NOP2 1"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"], + +["5 byte non-minimally-encoded arguments are valid"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x05 0x0000000000 NOP2 1"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Valid CHECKLOCKTIMEVERIFY in scriptSig"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"], + +["Valid CHECKLOCKTIMEVERIFY in redeemScript"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], +"0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000001000000", "P2SH,CHECKLOCKTIMEVERIFY"], ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 7eb30840..f4ab3645 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -40,6 +40,7 @@ static std::map mapFlagNames = boost::assign::map_list_of (string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA) (string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY) (string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS); + (string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY); unsigned int ParseScriptFlags(string strFlags) { diff --git a/src/uint256hm.h b/src/uint256hm.h index 168b385b..99293ecc 100644 --- a/src/uint256hm.h +++ b/src/uint256hm.h @@ -47,7 +47,7 @@ template if((int32_t)size < 0) m_mask = 0x80000000; else - for(m_mask = 64; m_mask < size; m_mask <<= 1); + for(m_mask = 8; m_mask < size; m_mask <<= 1); LogPrintf("uint256HashMap:Set(%u/%u) data=%u sz=%u\n", size, m_mask, (unsigned)sizeof(struct Data), (unsigned)(m_mask * sizeof(struct Data))); // allocate memory diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am index 6c1ec81e..4adaa1b1 100644 --- a/src/univalue/Makefile.am +++ b/src/univalue/Makefile.am @@ -3,7 +3,7 @@ ACLOCAL_AMFLAGS = -I build-aux/m4 .INTERMEDIATE: $(GENBIN) include_HEADERS = include/univalue.h -noinst_HEADERS = lib/univalue_escapes.h lib/univalue_utffilter.h +noinst_HEADERS = lib/univalue_escapes.h lib/univalue_utffilter.h lib/json_spirit_legacy.h lib_LTLIBRARIES = libunivalue.la diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index 8428b1c6..5a06c778 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -122,11 +122,12 @@ class UniValue { bool pushKVs(const UniValue& obj); std::string write(unsigned int prettyIndent = 0, - unsigned int indentLevel = 0) const; + unsigned int indentLevel = 0, + bool legacy = false) const; - bool read(const char *raw); - bool read(const std::string& rawStr) { - return read(rawStr.c_str()); + bool read(const char *raw, int mode = 0); + bool read(const std::string& rawStr, int mode = 0) { + return read(rawStr.c_str(), mode); } private: @@ -136,8 +137,8 @@ class UniValue { std::vector values; int findKey(const std::string& key) const; - void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; - void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; + void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s, bool legacy = false) const; + void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s, bool legacy = false) const; public: // Strict type-specific getters, these throw std::runtime_error if the @@ -240,7 +241,7 @@ enum jtokentype { }; extern enum jtokentype getJsonToken(std::string& tokenVal, - unsigned int& consumed, const char *raw); + unsigned int& consumed, const char *raw, int mode = 0); extern const char *uvTypeName(UniValue::VType t); static inline bool jsonTokenIsValue(enum jtokentype jtt) diff --git a/src/univalue/lib/json_spirit_legacy.h b/src/univalue/lib/json_spirit_legacy.h new file mode 100644 index 00000000..e412b33b --- /dev/null +++ b/src/univalue/lib/json_spirit_legacy.h @@ -0,0 +1,211 @@ +#ifndef JSON_SPIRIT_LEGACY_H +#define JSON_SPIRIT_LEGACY_H + + +/* ----------------------------------------------------------------------- + * -------------------------------- write -------------------------------- + * ----------------------------------------------------------------------- + */ + +template < class String_type > +String_type to_str( const char* c_str ) +{ + String_type result; + + for( const char* p = c_str; *p != 0; ++p ) + { + result += *p; + } + + return result; +} + +inline char to_hex_char( unsigned int c ) +{ + assert( c <= 0xF ); + + const char ch = static_cast< char >( c ); + + if( ch < 10 ) return '0' + ch; + + return 'A' - 10 + ch; +} + +template< class String_type > +String_type non_printable_to_string( unsigned int c ) +{ + // Silence the warning: typedef ‘Char_type’ locally defined but not used [-Wunused-local-typedefs] + // typedef typename String_type::value_type Char_type; + + String_type result( 6, '\\' ); + + result[1] = 'u'; + + result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4; + result[ 2 ] = to_hex_char( c & 0x000F ); + + return result; +} + +template< typename Char_type, class String_type > +bool add_esc_char( Char_type c, String_type& s ) +{ + switch( c ) + { + case '"': s += to_str< String_type >( "\\\"" ); return true; + case '\\': s += to_str< String_type >( "\\\\" ); return true; + case '\b': s += to_str< String_type >( "\\b" ); return true; + case '\f': s += to_str< String_type >( "\\f" ); return true; + case '\n': s += to_str< String_type >( "\\n" ); return true; + case '\r': s += to_str< String_type >( "\\r" ); return true; + case '\t': s += to_str< String_type >( "\\t" ); return true; + } + + return false; +} + +template< class String_type > +String_type add_esc_chars( const String_type& s ) +{ + typedef typename String_type::const_iterator Iter_type; + typedef typename String_type::value_type Char_type; + + String_type result; + + const Iter_type end( s.end() ); + + for( Iter_type i = s.begin(); i != end; ++i ) + { + const Char_type c( *i ); + + if( add_esc_char( c, result ) ) continue; + + const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c ); + + if( iswprint( unsigned_c ) ) + { + result += c; + } + else + { + result += non_printable_to_string< String_type >( unsigned_c ); + } + } + + return result; +} + +/* ---------------------------------------------------------------------- + * -------------------------------- read -------------------------------- + * ---------------------------------------------------------------------- + */ + +template< class Char_type > +Char_type hex_to_num( const Char_type c ) +{ + if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0'; + if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10; + if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10; + return 0; +} + +template< class Char_type, class Iter_type > +Char_type hex_str_to_char( Iter_type& begin ) +{ + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 ); +} + +template< class Char_type, class Iter_type > +Char_type unicode_str_to_char( Iter_type& begin ) +{ + const Char_type c1( *( ++begin ) ); + const Char_type c2( *( ++begin ) ); + const Char_type c3( *( ++begin ) ); + const Char_type c4( *( ++begin ) ); + + return ( hex_to_num( c1 ) << 12 ) + + ( hex_to_num( c2 ) << 8 ) + + ( hex_to_num( c3 ) << 4 ) + + hex_to_num( c4 ); +} + +template< class String_type > +void append_esc_char_and_incr_iter( String_type& s, + typename String_type::const_iterator& begin, + typename String_type::const_iterator end ) +{ + typedef typename String_type::value_type Char_type; + + const Char_type c2( *begin ); + + switch( c2 ) + { + case 't': s += '\t'; break; + case 'b': s += '\b'; break; + case 'f': s += '\f'; break; + case 'n': s += '\n'; break; + case 'r': s += '\r'; break; + case '\\': s += '\\'; break; + case '/': s += '/'; break; + case '"': s += '"'; break; + case 'x': + { + if( end - begin >= 3 ) // expecting "xHH..." + { + s += hex_str_to_char< Char_type >( begin ); + } + break; + } + case 'u': + { + if( end - begin >= 5 ) // expecting "uHHHH..." + { + s += unicode_str_to_char< Char_type >( begin ); + } + break; + } + } +} + +template< class String_type > +String_type substitute_esc_chars( typename String_type::const_iterator begin, + typename String_type::const_iterator end ) +{ + typedef typename String_type::const_iterator Iter_type; + + if( end - begin < 2 ) return String_type( begin, end ); + + String_type result; + + result.reserve( end - begin ); + + const Iter_type end_minus_1( end - 1 ); + + Iter_type substr_start = begin; + Iter_type i = begin; + + for( ; i < end_minus_1; ++i ) + { + if( *i == '\\' ) + { + result.append( substr_start, i ); + + ++i; // skip the '\' + + append_esc_char_and_incr_iter( result, i, end ); + + substr_start = i + 1; + } + } + + result.append( substr_start, end ); + + return result; +} + +#endif // JSON_SPIRIT_LEGACY_H \ No newline at end of file diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp index 95bac695..95fd9294 100644 --- a/src/univalue/lib/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -7,6 +7,7 @@ #include #include "univalue.h" #include "univalue_utffilter.h" +#include "json_spirit_legacy.h" using namespace std; @@ -43,7 +44,7 @@ static const char *hatoui(const char *first, const char *last, } enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, - const char *raw) + const char *raw, int mode) { tokenVal.clear(); consumed = 0; @@ -178,10 +179,13 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, JSONUTF8StringFilter writer(valStr); while (*raw) { - if ((unsigned char)*raw < 0x20) + if ((unsigned char)*raw < 0x20 && mode == 0) return JTOK_ERR; else if (*raw == '\\') { + if (mode == 2) { // write 2 bytes + valStr += *raw; raw++; valStr += *raw; raw++; + } else { raw++; // skip backslash switch (*raw) { @@ -209,6 +213,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, } raw++; // skip esc'd char + } } else if (*raw == '"') { @@ -217,11 +222,19 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, } else { - writer.push_back(*raw); + if (mode == 1 || mode == 2) + valStr += *raw; + else + writer.push_back(*raw); raw++; } } + if (mode == 2) // read string as json_spirit output + { + valStr = substitute_esc_chars< std::string >( valStr.begin(), valStr.end() ); + } + if (!writer.finalize()) return JTOK_ERR; tokenVal = valStr; @@ -246,7 +259,12 @@ enum expect_bits { #define setExpect(bit) (expectMask |= EXP_##bit) #define clearExpect(bit) (expectMask &= ~EXP_##bit) -bool UniValue::read(const char *raw) +// mode: +// 0 - Default univalue behavior +// 1 - Allow strings with binary data to be read without errors. +// This is needed for outputing name commands that might have binary data in their value. +// 2 - Legacy mode. Read string that was generated by json_spirit. +bool UniValue::read(const char *raw, int mode) { clear(); @@ -260,7 +278,7 @@ bool UniValue::read(const char *raw) do { last_tok = tok; - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, mode); if (tok == JTOK_NONE || tok == JTOK_ERR) return false; raw += consumed; diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp index cfbdad32..7b1adb1f 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -7,11 +7,15 @@ #include #include "univalue.h" #include "univalue_escapes.h" +#include "json_spirit_legacy.h" using namespace std; -static string json_escape(const string& inS) +static string json_escape(const string& inS, bool legacy=false) { + if (legacy) + return add_esc_chars(inS); + string outS; outS.reserve(inS.size() * 2); @@ -29,7 +33,8 @@ static string json_escape(const string& inS) } string UniValue::write(unsigned int prettyIndent, - unsigned int indentLevel) const + unsigned int indentLevel, + bool legacy) const { string s; s.reserve(1024); @@ -43,13 +48,13 @@ string UniValue::write(unsigned int prettyIndent, s += "null"; break; case VOBJ: - writeObject(prettyIndent, modIndent, s); + writeObject(prettyIndent, modIndent, s, legacy); break; case VARR: - writeArray(prettyIndent, modIndent, s); + writeArray(prettyIndent, modIndent, s, legacy); break; case VSTR: - s += "\"" + json_escape(val) + "\""; + s += "\"" + json_escape(val, legacy) + "\""; break; case VNUM: s += val; @@ -67,7 +72,7 @@ static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, strin s.append(prettyIndent * indentLevel, ' '); } -void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const +void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s, bool legacy) const { s += "["; if (prettyIndent) @@ -76,7 +81,7 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s for (unsigned int i = 0; i < values.size(); i++) { if (prettyIndent) indentStr(prettyIndent, indentLevel, s); - s += values[i].write(prettyIndent, indentLevel + 1); + s += values[i].write(prettyIndent, indentLevel + 1, legacy); if (i != (values.size() - 1)) { s += ","; if (prettyIndent) @@ -91,7 +96,7 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s s += "]"; } -void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const +void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s, bool legacy) const { s += "{"; if (prettyIndent) @@ -103,7 +108,7 @@ void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, s += "\"" + json_escape(keys[i]) + "\":"; if (prettyIndent) s += " "; - s += values.at(i).write(prettyIndent, indentLevel + 1); + s += values.at(i).write(prettyIndent, indentLevel + 1, legacy); if (i != (values.size() - 1)) s += ","; if (prettyIndent) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index ea421a9e..f39c8f4f 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -200,7 +200,7 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive, std::vector::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second;