aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj195
-rw-r--r--build_msvc/bitcoin.sln20
-rw-r--r--build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in166
-rw-r--r--build_msvc/msvc-autogen.py1
-rw-r--r--configure.ac20
-rw-r--r--src/Makefile.am40
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/bitcoin-wallet-res.rc35
-rw-r--r--src/bitcoin-wallet.cpp121
-rw-r--r--src/wallet/crypter.cpp4
-rw-r--r--src/wallet/crypter.h2
-rw-r--r--src/wallet/db.cpp47
-rw-r--r--src/wallet/db.h34
-rw-r--r--src/wallet/test/db_tests.cpp72
-rw-r--r--src/wallet/wallet.cpp7
-rw-r--r--src/wallet/wallet.h2
-rw-r--r--src/wallet/wallettool.cpp139
-rw-r--r--src/wallet/wallettool.h20
-rwxr-xr-xtest/functional/test_framework/test_framework.py1
-rwxr-xr-xtest/functional/test_runner.py3
-rwxr-xr-xtest/functional/tool_wallet.py101
22 files changed, 988 insertions, 44 deletions
diff --git a/.gitignore b/.gitignore
index 380d2eab98..994d67402f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ src/bitcoin
src/bitcoind
src/bitcoin-cli
src/bitcoin-tx
+src/bitcoin-wallet
src/test/test_bitcoin
src/test/test_bitcoin_fuzzy
src/qt/test/test_bitcoin-qt
diff --git a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
new file mode 100644
index 0000000000..b618aca817
--- /dev/null
+++ b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Label="configInitTarget" Project="..\common.init.vcxproj" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\bitcoin-wallet.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
+ <Project>{2b384fa8-9ee1-4544-93cb-0d733c25e8ce}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_cli\libbitcoin_cli.vcxproj">
+ <Project>{0667528c-d734-4009-adf9-c0d6c4a5a5a6}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_common\libbitcoin_common.vcxproj">
+ <Project>{7c87e378-df58-482e-aa2f-1bc129bc19ce}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_crypto\libbitcoin_crypto.vcxproj">
+ <Project>{6190199c-6cf4-4dad-bfbd-93fa72a760c1}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_server\libbitcoin_server.vcxproj">
+ <Project>{460fee33-1fe1-483f-b3bf-931ff8e969a5}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_util\libbitcoin_util.vcxproj">
+ <Project>{b53a5535-ee9d-4c6f-9a26-f79ee3bc3754}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_wallet\libbitcoin_wallet.vcxproj">
+ <Project>{93b86837-b543-48a5-a89b-7c87abb77df2}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_zmq\libbitcoin_zmq.vcxproj">
+ <Project>{792d487f-f14c-49fc-a9de-3fc150f31c3f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libunivalue\libunivalue.vcxproj">
+ <Project>{5724ba7d-a09a-4ba8-800b-c4c1561b3d69}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\libbitcoin_wallet_tool\libbitcoin_wallet_tool.vcxproj">
+ <Project>{f91ac55e-6f5e-4c58-9ac5-b40db7deef93}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{84DE8790-EDE3-4483-81AC-C32F15E861F4}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>bitcointx</RootNamespace>
+ <VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
+ <VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>crypt32.lib;Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>crypt32.lib;Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>crypt32.lib;Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>crypt32.lib;Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Label="configTarget" Project="..\common.vcxproj" />
+</Project>
diff --git a/build_msvc/bitcoin.sln b/build_msvc/bitcoin.sln
index 32068d81f6..b84a525afa 100644
--- a/build_msvc/bitcoin.sln
+++ b/build_msvc/bitcoin.sln
@@ -32,6 +32,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bench_bitcoin", "bench_bitc
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bitcoin-tx", "bitcoin-tx\bitcoin-tx.vcxproj", "{D3022AF6-AD33-4CE3-B358-87CB6A1B29CF}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bitcoin-wallet", "bitcoin-wallet\bitcoin-wallet.vcxproj", "{84DE8790-EDE3-4483-81AC-C32F15E861F4}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbitcoin_wallet_tool", "libbitcoin_wallet_tool\libbitcoin_wallet_tool.vcxproj", "{F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -160,6 +164,22 @@ Global
{D3022AF6-AD33-4CE3-B358-87CB6A1B29CF}.Release|x64.Build.0 = Release|x64
{D3022AF6-AD33-4CE3-B358-87CB6A1B29CF}.Release|x86.ActiveCfg = Release|Win32
{D3022AF6-AD33-4CE3-B358-87CB6A1B29CF}.Release|x86.Build.0 = Release|Win32
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Debug|x64.ActiveCfg = Debug|x64
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Debug|x64.Build.0 = Debug|x64
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Debug|x86.ActiveCfg = Debug|Win32
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Debug|x86.Build.0 = Debug|Win32
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Release|x64.ActiveCfg = Release|x64
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Release|x64.Build.0 = Release|x64
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Release|x86.ActiveCfg = Release|Win32
+ {84DE8790-EDE3-4483-81AC-C32F15E861F4}.Release|x86.Build.0 = Release|Win32
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Debug|x64.ActiveCfg = Debug|x64
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Debug|x64.Build.0 = Debug|x64
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Debug|x86.ActiveCfg = Debug|Win32
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Debug|x86.Build.0 = Debug|Win32
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Release|x64.ActiveCfg = Release|x64
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Release|x64.Build.0 = Release|x64
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Release|x86.ActiveCfg = Release|Win32
+ {F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in b/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
new file mode 100644
index 0000000000..b9cf68aee0
--- /dev/null
+++ b/build_msvc/libbitcoin_wallet_tool/libbitcoin_wallet_tool.vcxproj.in
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Label="configInitTarget" Project="..\common.init.vcxproj" />
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+@SOURCE_FILES@
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{F91AC55E-6F5E-4C58-9AC5-B40DB7DEEF93}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>libbitcoin_zmq</RootNamespace>
+ <VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
+ <VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <SuppressStartupBanner>false</SuppressStartupBanner>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <SuppressStartupBanner>false</SuppressStartupBanner>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <SuppressStartupBanner>false</SuppressStartupBanner>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NOMINMAX;ZMQ_STATIC;HAVE_CONFIG_H;_SCL_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <SuppressStartupBanner>false</SuppressStartupBanner>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Label="configTarget" Project="..\common.vcxproj" />
+</Project>
diff --git a/build_msvc/msvc-autogen.py b/build_msvc/msvc-autogen.py
index f351532f9d..c8df29eecb 100644
--- a/build_msvc/msvc-autogen.py
+++ b/build_msvc/msvc-autogen.py
@@ -11,6 +11,7 @@ libs = [
'libbitcoin_crypto',
'libbitcoin_server',
'libbitcoin_util',
+ 'libbitcoin_wallet_tool',
'libbitcoin_wallet',
'libbitcoin_zmq',
]
diff --git a/configure.ac b/configure.ac
index 2620ed56da..8ac5cb02d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -19,6 +19,7 @@ BITCOIN_DAEMON_NAME=bitcoind
BITCOIN_GUI_NAME=bitcoin-qt
BITCOIN_CLI_NAME=bitcoin-cli
BITCOIN_TX_NAME=bitcoin-tx
+BITCOIN_WALLET_TOOL_NAME=bitcoin-wallet
dnl Unless the user specified ARFLAGS, force it to be cr
AC_ARG_VAR(ARFLAGS, [Flags for the archiver, defaults to <cr> if not set])
@@ -419,7 +420,7 @@ CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS"
AC_ARG_WITH([utils],
[AS_HELP_STRING([--with-utils],
- [build bitcoin-cli bitcoin-tx (default=yes)])],
+ [build bitcoin-cli bitcoin-tx bitcoin-wallet (default=yes)])],
[build_bitcoin_utils=$withval],
[build_bitcoin_utils=yes])
@@ -435,6 +436,12 @@ AC_ARG_ENABLE([util-tx],
[build_bitcoin_tx=$enableval],
[build_bitcoin_tx=$build_bitcoin_utils])
+AC_ARG_ENABLE([util-wallet],
+ [AS_HELP_STRING([--enable-util-wallet],
+ [build bitcoin-wallet])],
+ [build_bitcoin_wallet=$enableval],
+ [build_bitcoin_wallet=$build_bitcoin_utils])
+
AC_ARG_WITH([libs],
[AS_HELP_STRING([--with-libs],
[build libraries (default=yes)])],
@@ -904,7 +911,7 @@ BITCOIN_QT_INIT
dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus
BITCOIN_QT_CONFIGURE([$use_pkgconfig])
-if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononono; then
+if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then
use_boost=no
else
use_boost=yes
@@ -1186,7 +1193,7 @@ dnl univalue check
need_bundled_univalue=yes
-if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononono; then
+if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then
need_bundled_univalue=no
else
@@ -1248,6 +1255,10 @@ AC_MSG_CHECKING([whether to build bitcoin-tx])
AM_CONDITIONAL([BUILD_BITCOIN_TX], [test x$build_bitcoin_tx = xyes])
AC_MSG_RESULT($build_bitcoin_tx)
+AC_MSG_CHECKING([whether to build bitcoin-wallet])
+AM_CONDITIONAL([BUILD_BITCOIN_WALLET], [test x$build_bitcoin_wallet = xyes])
+AC_MSG_RESULT($build_bitcoin_wallet)
+
AC_MSG_CHECKING([whether to build libraries])
AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test x$build_bitcoin_libs = xyes])
if test x$build_bitcoin_libs = xyes; then
@@ -1389,7 +1400,7 @@ else
AC_MSG_RESULT([no])
fi
-if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnonononononono; then
+if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononononono; then
AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests])
fi
@@ -1436,6 +1447,7 @@ AC_SUBST(BITCOIN_DAEMON_NAME)
AC_SUBST(BITCOIN_GUI_NAME)
AC_SUBST(BITCOIN_CLI_NAME)
AC_SUBST(BITCOIN_TX_NAME)
+AC_SUBST(BITCOIN_WALLET_TOOL_NAME)
AC_SUBST(RELDFLAGS)
AC_SUBST(DEBUG_CPPFLAGS)
diff --git a/src/Makefile.am b/src/Makefile.am
index efac2b4695..a57fcb0711 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -41,6 +41,7 @@ LIBBITCOINCONSENSUS=libbitcoinconsensus.la
endif
if ENABLE_WALLET
LIBBITCOIN_WALLET=libbitcoin_wallet.a
+LIBBITCOIN_WALLET_TOOL=libbitcoin_wallet_tool.a
endif
LIBBITCOIN_CRYPTO= $(LIBBITCOIN_CRYPTO_BASE)
@@ -70,6 +71,7 @@ EXTRA_LIBRARIES += \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_CLI) \
$(LIBBITCOIN_WALLET) \
+ $(LIBBITCOIN_WALLET_TOOL) \
$(LIBBITCOIN_ZMQ)
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
@@ -89,6 +91,11 @@ endif
if BUILD_BITCOIN_TX
bin_PROGRAMS += bitcoin-tx
endif
+if ENABLE_WALLET
+if BUILD_BITCOIN_WALLET
+ bin_PROGRAMS += bitcoin-wallet
+endif
+endif
.PHONY: FORCE check-symbols check-security
# bitcoin core #
@@ -205,6 +212,7 @@ BITCOIN_CORE_H = \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
+ wallet/wallettool.h \
wallet/walletutil.h \
wallet/coinselection.h \
warnings.h \
@@ -308,6 +316,12 @@ libbitcoin_wallet_a_SOURCES = \
wallet/coinselection.cpp \
$(BITCOIN_CORE_H)
+libbitcoin_wallet_tool_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+libbitcoin_wallet_tool_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+libbitcoin_wallet_tool_a_SOURCES = \
+ wallet/wallettool.cpp \
+ $(BITCOIN_CORE_H)
+
# crypto primitives library
crypto_libbitcoin_crypto_base_a_CPPFLAGS = $(AM_CPPFLAGS)
crypto_libbitcoin_crypto_base_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
@@ -525,6 +539,32 @@ bitcoin_tx_LDADD = \
bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
#
+# bitcoin-wallet binary #
+bitcoin_wallet_SOURCES = bitcoin-wallet.cpp
+bitcoin_wallet_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+bitcoin_wallet_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+bitcoin_wallet_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+
+if TARGET_WINDOWS
+bitcoin_wallet_SOURCES += bitcoin-wallet-res.rc
+endif
+
+bitcoin_wallet_LDADD = \
+ $(LIBBITCOIN_WALLET_TOOL) \
+ $(LIBBITCOIN_WALLET) \
+ $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_CONSENSUS) \
+ $(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_CRYPTO) \
+ $(LIBLEVELDB) \
+ $(LIBLEVELDB_SSE42) \
+ $(LIBMEMENV) \
+ $(LIBSECP256K1)
+
+bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
+#
+
# bitcoinconsensus library #
if BUILD_BITCOIN_LIBS
include_HEADERS = script/bitcoinconsensus.h
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 2e1a2c7766..12112375ea 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -138,6 +138,7 @@ endif
if ENABLE_WALLET
BITCOIN_TESTS += \
+ wallet/test/db_tests.cpp \
wallet/test/psbt_wallet_tests.cpp \
wallet/test/wallet_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
diff --git a/src/bitcoin-wallet-res.rc b/src/bitcoin-wallet-res.rc
new file mode 100644
index 0000000000..e9fa2dbb40
--- /dev/null
+++ b/src/bitcoin-wallet-res.rc
@@ -0,0 +1,35 @@
+#include <windows.h> // needed for VERSIONINFO
+#include "clientversion.h" // holds the needed client version information
+
+#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION,CLIENT_VERSION_BUILD
+#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD)
+#define VER_FILEVERSION VER_PRODUCTVERSION
+#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION VER_FILEVERSION
+PRODUCTVERSION VER_PRODUCTVERSION
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4" // U.S. English - multilingual (hex)
+ BEGIN
+ VALUE "CompanyName", "Bitcoin"
+ VALUE "FileDescription", "bitcoin-wallet (CLI tool for " PACKAGE_NAME " wallets)"
+ VALUE "FileVersion", VER_FILEVERSION_STR
+ VALUE "InternalName", "bitcoin-wallet"
+ VALUE "LegalCopyright", COPYRIGHT_STR
+ VALUE "LegalTrademarks1", "Distributed under the MIT software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php."
+ VALUE "OriginalFilename", "bitcoin-wallet.exe"
+ VALUE "ProductName", "bitcoin-wallet"
+ VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal)
+ END
+END
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
new file mode 100644
index 0000000000..293e3efa5c
--- /dev/null
+++ b/src/bitcoin-wallet.cpp
@@ -0,0 +1,121 @@
+// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <consensus/consensus.h>
+#include <logging.h>
+#include <util/system.h>
+#include <util/strencodings.h>
+#include <wallet/wallettool.h>
+
+#include <stdio.h>
+
+const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
+
+static void SetupWalletToolArgs()
+{
+ SetupChainParamsBaseOptions();
+
+ gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise.", false, OptionsCategory::DEBUG_TEST);
+
+ gArgs.AddArg("info", "Get wallet info", false, OptionsCategory::COMMANDS);
+ gArgs.AddArg("create", "Create new wallet file", false, OptionsCategory::COMMANDS);
+
+ // Hidden
+ gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
+ gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
+}
+
+static bool WalletAppInit(int argc, char* argv[])
+{
+ SetupWalletToolArgs();
+ std::string error_message;
+ if (!gArgs.ParseParameters(argc, argv, error_message)) {
+ fprintf(stderr, "Error parsing command line arguments: %s\n", error_message.c_str());
+ return false;
+ }
+ if (argc < 2 || HelpRequested(gArgs)) {
+ std::string usage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" +
+ "wallet-tool is an offline tool for creating and interacting with Bitcoin Core wallet files.\n" +
+ "By default wallet-tool will act on wallets in the default mainnet wallet directory in the datadir.\n" +
+ "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n" +
+ "Usage:\n" +
+ " bitcoin-wallet [options] <command>\n\n" +
+ gArgs.GetHelpMessage();
+
+ fprintf(stdout, "%s", usage.c_str());
+ return false;
+ }
+
+ // check for printtoconsole, allow -debug
+ g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
+
+ if (!fs::is_directory(GetDataDir(false))) {
+ fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
+ return false;
+ }
+ // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ SelectParams(gArgs.GetChainName());
+
+ return true;
+}
+
+int main(int argc, char* argv[])
+{
+#ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+ std::tie(argc, argv) = winArgs.get();
+#endif
+ SetupEnvironment();
+ RandomInit();
+ try {
+ if (!WalletAppInit(argc, argv)) return EXIT_FAILURE;
+ } catch (const std::exception& e) {
+ PrintExceptionContinue(&e, "WalletAppInit()");
+ return EXIT_FAILURE;
+ } catch (...) {
+ PrintExceptionContinue(nullptr, "WalletAppInit()");
+ return EXIT_FAILURE;
+ }
+
+ std::string method {};
+ for(int i = 1; i < argc; ++i) {
+ if (!IsSwitchChar(argv[i][0])) {
+ if (!method.empty()) {
+ fprintf(stderr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]);
+ return EXIT_FAILURE;
+ }
+ method = argv[i];
+ }
+ }
+
+ if (method.empty()) {
+ fprintf(stderr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
+ return EXIT_FAILURE;
+ }
+
+ // A name must be provided when creating a file
+ if (method == "create" && !gArgs.IsArgSet("-wallet")) {
+ fprintf(stderr, "Wallet name must be provided when creating a new wallet.\n");
+ return EXIT_FAILURE;
+ }
+
+ std::string name = gArgs.GetArg("-wallet", "");
+
+ ECCVerifyHandle globalVerifyHandle;
+ ECC_Start();
+ if (!WalletTool::ExecuteWalletToolFunc(method, name))
+ return EXIT_FAILURE;
+ ECC_Stop();
+ return EXIT_SUCCESS;
+}
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index f5ac6e98b2..1dc78255f6 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -175,7 +175,7 @@ bool CCryptoKeyStore::Lock()
return true;
}
-bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
+bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys)
{
{
LOCK(cs_KeyStore);
@@ -204,7 +204,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
}
- if (keyFail || !keyPass)
+ if (keyFail || (!keyPass && !accept_no_keys))
return false;
vMasterKey = vMasterKeyIn;
fDecryptionThoroughlyChecked = true;
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index 418316c398..8e195ca8fa 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -133,7 +133,7 @@ protected:
//! will encrypt previously unencrypted keys
bool EncryptKeys(CKeyingMaterial& vMasterKeyIn);
- bool Unlock(const CKeyingMaterial& vMasterKeyIn);
+ bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false);
CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore);
public:
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index a2e795056a..ae40553268 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -48,7 +48,7 @@ void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filena
}
CCriticalSection cs_db;
-std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
+std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
} // namespace
bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
@@ -80,19 +80,29 @@ bool IsWalletLoaded(const fs::path& wallet_path)
LOCK(cs_db);
auto env = g_dbenvs.find(env_directory.string());
if (env == g_dbenvs.end()) return false;
- return env->second.IsDatabaseLoaded(database_filename);
+ auto database = env->second.lock();
+ return database && database->IsDatabaseLoaded(database_filename);
}
-BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+/**
+ * @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
+ * @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
+ * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
+ * erases the weak pointer from the g_dbenvs map.
+ * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
+ */
+std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
{
fs::path env_directory;
SplitWalletPath(wallet_path, env_directory, database_filename);
LOCK(cs_db);
- // Note: An unused temporary BerkeleyEnvironment object may be created inside the
- // emplace function if the key already exists. This is a little inefficient,
- // but not a big concern since the map will be changed in the future to hold
- // pointers instead of objects, anyway.
- return &g_dbenvs.emplace(std::piecewise_construct, std::forward_as_tuple(env_directory.string()), std::forward_as_tuple(env_directory)).first->second;
+ auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
+ if (inserted.second) {
+ auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
+ inserted.first->second = env;
+ return env;
+ }
+ return inserted.first->second.lock();
}
//
@@ -137,6 +147,7 @@ BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir
BerkeleyEnvironment::~BerkeleyEnvironment()
{
+ g_dbenvs.erase(strPath);
Close();
}
@@ -214,10 +225,10 @@ bool BerkeleyEnvironment::Open(bool retry)
return true;
}
-void BerkeleyEnvironment::MakeMock()
+//! Construct an in-memory mock Berkeley environment for testing and as a place-holder for g_dbenvs emplace
+BerkeleyEnvironment::BerkeleyEnvironment()
{
- if (fDbEnvInit)
- throw std::runtime_error("BerkeleyEnvironment::MakeMock: Already initialized");
+ Reset();
boost::this_thread::interruption_point();
@@ -305,7 +316,7 @@ BerkeleyBatch::SafeDbt::operator Dbt*()
bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
{
std::string filename;
- BerkeleyEnvironment* env = GetWalletEnv(file_path, filename);
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
// Recovery procedure:
// move wallet file to walletfilename.timestamp.bak
@@ -374,7 +385,7 @@ bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, boo
bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& errorStr)
{
std::string walletFile;
- BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile);
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
fs::path walletDir = env->Directory();
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(nullptr, nullptr, nullptr));
@@ -398,7 +409,7 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& er
bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
{
std::string walletFile;
- BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile);
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile);
fs::path walletDir = env->Directory();
if (fs::exists(walletDir / walletFile))
@@ -502,7 +513,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
{
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
fFlushOnClose = fFlushOnCloseIn;
- env = database.env;
+ env = database.env.get();
if (database.IsDummy()) {
return;
}
@@ -559,7 +570,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
// versions of BDB have an set_lk_exclusive method for this
// purpose, but the older version we use does not.)
for (const auto& env : g_dbenvs) {
- CheckUniqueFileid(env.second, strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
+ CheckUniqueFileid(*env.second.lock().get(), strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
}
pdb = pdb_temp.release();
@@ -660,7 +671,7 @@ bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
if (database.IsDummy()) {
return true;
}
- BerkeleyEnvironment *env = database.env;
+ BerkeleyEnvironment *env = database.env.get();
const std::string& strFile = database.strFile;
while (true) {
{
@@ -791,7 +802,7 @@ bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
return true;
}
bool ret = false;
- BerkeleyEnvironment *env = database.env;
+ BerkeleyEnvironment *env = database.env.get();
const std::string& strFile = database.strFile;
TRY_LOCK(cs_db, lockDb);
if (lockDb)
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 9dc373c89b..9df965305a 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -50,10 +50,10 @@ public:
std::condition_variable_any m_db_in_use;
BerkeleyEnvironment(const fs::path& env_directory);
+ BerkeleyEnvironment();
~BerkeleyEnvironment();
void Reset();
- void MakeMock();
bool IsMock() const { return fMockDb; }
bool IsInitialized() const { return fDbEnvInit; }
bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
@@ -102,7 +102,7 @@ public:
bool IsWalletLoaded(const fs::path& wallet_path);
/** Get BerkeleyEnvironment and database filename given a wallet path. */
-BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
+std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
/** An instance of this class represents one database.
* For BerkeleyDB this is just a (env, strFile) tuple.
@@ -117,17 +117,11 @@ public:
}
/** Create DB handle to real database */
- BerkeleyDatabase(const fs::path& wallet_path, bool mock = false) :
- nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
+ BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
+ nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
{
- env = GetWalletEnv(wallet_path, strFile);
- auto inserted = env->m_databases.emplace(strFile, std::ref(*this));
+ auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
assert(inserted.second);
- if (mock) {
- env->Close();
- env->Reset();
- env->MakeMock();
- }
}
~BerkeleyDatabase() {
@@ -140,7 +134,8 @@ public:
/** Return object for accessing database at specified path. */
static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
{
- return MakeUnique<BerkeleyDatabase>(path);
+ std::string filename;
+ return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
}
/** Return object for accessing dummy database with no read/write capabilities. */
@@ -152,7 +147,7 @@ public:
/** Return object for accessing temporary in-memory database. */
static std::unique_ptr<BerkeleyDatabase> CreateMock()
{
- return MakeUnique<BerkeleyDatabase>("", true /* mock */);
+ return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
}
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
@@ -176,12 +171,21 @@ public:
unsigned int nLastFlushed;
int64_t nLastWalletUpdate;
+ /**
+ * Pointer to shared database environment.
+ *
+ * Normally there is only one BerkeleyDatabase object per
+ * BerkeleyEnvivonment, but in the special, backwards compatible case where
+ * multiple wallet BDB data files are loaded from the same directory, this
+ * will point to a shared instance that gets freed when the last data file
+ * is closed.
+ */
+ std::shared_ptr<BerkeleyEnvironment> env;
+
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
std::unique_ptr<Db> m_db;
private:
- /** BerkeleyDB specific */
- BerkeleyEnvironment *env;
std::string strFile;
/** Return whether this database handle is a dummy for testing.
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
new file mode 100644
index 0000000000..2a64749379
--- /dev/null
+++ b/src/wallet/test/db_tests.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <memory>
+
+#include <boost/test/unit_test.hpp>
+
+#include <fs.h>
+#include <test/test_bitcoin.h>
+#include <wallet/db.h>
+
+
+BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(getwalletenv_file)
+{
+ std::string test_name = "test_name.dat";
+ fs::path datadir = SetDataDir("tempdir");
+ fs::path file_path = datadir / test_name;
+ std::ofstream f(file_path.BOOST_FILESYSTEM_C_STR);
+ f.close();
+
+ std::string filename;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
+ BOOST_CHECK(filename == test_name);
+ BOOST_CHECK(env->Directory() == datadir);
+}
+
+BOOST_AUTO_TEST_CASE(getwalletenv_directory)
+{
+ std::string expected_name = "wallet.dat";
+ fs::path datadir = SetDataDir("tempdir");
+
+ std::string filename;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
+ BOOST_CHECK(filename == expected_name);
+ BOOST_CHECK(env->Directory() == datadir);
+}
+
+BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
+{
+ fs::path datadir = SetDataDir("tempdir");
+ fs::path datadir_2 = SetDataDir("tempdir_2");
+ std::string filename;
+
+ std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
+ std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
+ std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
+
+ BOOST_CHECK(env_1 == env_2);
+ BOOST_CHECK(env_2 != env_3);
+}
+
+BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
+{
+ fs::path datadir = SetDataDir("tempdir");
+ fs::path datadir_2 = SetDataDir("tempdir_2");
+ std::string filename;
+
+ std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
+ std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
+ env_1_a.reset();
+
+ std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
+ std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
+
+ BOOST_CHECK(env_1_a != env_1_b);
+ BOOST_CHECK(env_2_a == env_2_b);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 757cb81795..5c379aacd6 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -398,7 +398,7 @@ bool CWallet::LoadWatchOnly(const CScript &dest)
return CCryptoKeyStore::AddWatchOnly(dest);
}
-bool CWallet::Unlock(const SecureString& strWalletPassphrase)
+bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys)
{
CCrypter crypter;
CKeyingMaterial _vMasterKey;
@@ -411,7 +411,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase)
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
continue; // try another master key
- if (CCryptoKeyStore::Unlock(_vMasterKey))
+ if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys))
return true;
}
}
@@ -3922,6 +3922,9 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
return false;
}
+ // Keep same database environment instance across Verify/Recover calls below.
+ std::unique_ptr<WalletDatabase> database = WalletDatabase::Create(wallet_path);
+
try {
if (!WalletBatch::VerifyEnvironment(wallet_path, error_string)) {
return false;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 06c7900ce1..4776b0eacc 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -884,7 +884,7 @@ public:
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
int64_t nRelockTime = 0;
- bool Unlock(const SecureString& strWalletPassphrase);
+ bool Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys = false);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
new file mode 100644
index 0000000000..30b0c48eef
--- /dev/null
+++ b/src/wallet/wallettool.cpp
@@ -0,0 +1,139 @@
+// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <base58.h>
+#include <fs.h>
+#include <interfaces/chain.h>
+#include <util/system.h>
+#include <wallet/wallet.h>
+#include <wallet/walletutil.h>
+
+namespace WalletTool {
+
+// The standard wallet deleter function blocks on the validation interface
+// queue, which doesn't exist for the bitcoin-wallet. Define our own
+// deleter here.
+static void WalletToolReleaseWallet(CWallet* wallet)
+{
+ wallet->WalletLogPrintf("Releasing wallet\n");
+ wallet->Flush();
+ delete wallet;
+}
+
+static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path)
+{
+ if (fs::exists(path)) {
+ fprintf(stderr, "Error: File exists already\n");
+ return nullptr;
+ }
+ // dummy chain interface
+ auto chain = interfaces::MakeChain();
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ bool first_run = true;
+ DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
+ if (load_wallet_ret != DBErrors::LOAD_OK) {
+ fprintf(stderr, "Error creating %s", name.c_str());
+ return nullptr;
+ }
+
+ wallet_instance->SetMinVersion(FEATURE_HD_SPLIT);
+
+ // generate a new HD seed
+ CPubKey seed = wallet_instance->GenerateNewSeed();
+ wallet_instance->SetHDSeed(seed);
+
+ fprintf(stdout, "Topping up keypool...\n");
+ wallet_instance->TopUpKeyPool();
+ return wallet_instance;
+}
+
+static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path)
+{
+ if (!fs::exists(path)) {
+ fprintf(stderr, "Error: Wallet files does not exist\n");
+ return nullptr;
+ }
+
+ // dummy chain interface
+ auto chain = interfaces::MakeChain();
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ DBErrors load_wallet_ret;
+ try {
+ bool first_run;
+ load_wallet_ret = wallet_instance->LoadWallet(first_run);
+ } catch (const std::runtime_error) {
+ fprintf(stderr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
+ return nullptr;
+ }
+
+ if (load_wallet_ret != DBErrors::LOAD_OK) {
+ wallet_instance = nullptr;
+ if (load_wallet_ret == DBErrors::CORRUPT) {
+ fprintf(stderr, "Error loading %s: Wallet corrupted", name.c_str());
+ return nullptr;
+ } else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) {
+ fprintf(stderr, "Error reading %s! All keys read correctly, but transaction data"
+ " or address book entries might be missing or incorrect.",
+ name.c_str());
+ } else if (load_wallet_ret == DBErrors::TOO_NEW) {
+ fprintf(stderr, "Error loading %s: Wallet requires newer version of %s",
+ name.c_str(), PACKAGE_NAME);
+ return nullptr;
+ } else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
+ fprintf(stderr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
+ return nullptr;
+ } else {
+ fprintf(stderr, "Error loading %s", name.c_str());
+ return nullptr;
+ }
+ }
+
+ return wallet_instance;
+}
+
+static void WalletShowInfo(CWallet* wallet_instance)
+{
+ // lock required because of some AssertLockHeld()
+ LOCK(wallet_instance->cs_wallet);
+
+ fprintf(stdout, "Wallet info\n===========\n");
+ fprintf(stdout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
+ fprintf(stdout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes");
+ fprintf(stdout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
+ fprintf(stdout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
+ fprintf(stdout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
+}
+
+bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
+{
+ fs::path path = fs::absolute(name, GetWalletDir());
+
+ if (command == "create") {
+ std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path);
+ if (wallet_instance) {
+ WalletShowInfo(wallet_instance.get());
+ wallet_instance->Flush();
+ }
+ } else if (command == "info") {
+ if (!fs::exists(path)) {
+ fprintf(stderr, "Error: no wallet file at %s\n", name.c_str());
+ return false;
+ }
+ std::string error;
+ if (!WalletBatch::VerifyEnvironment(path, error)) {
+ fprintf(stderr, "Error loading %s. Is wallet being used by other process?\n", name.c_str());
+ return false;
+ }
+ std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
+ if (!wallet_instance) return false;
+ WalletShowInfo(wallet_instance.get());
+ wallet_instance->Flush();
+ } else {
+ fprintf(stderr, "Invalid command: %s\n", command.c_str());
+ return false;
+ }
+
+ return true;
+}
+} // namespace WalletTool
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
new file mode 100644
index 0000000000..5b06fd1792
--- /dev/null
+++ b/src/wallet/wallettool.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_WALLETTOOL_H
+#define BITCOIN_WALLET_WALLETTOOL_H
+
+#include <script/ismine.h>
+#include <wallet/wallet.h>
+
+namespace WalletTool {
+
+std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path);
+std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path);
+void WalletShowInfo(CWallet* wallet_instance);
+bool ExecuteWalletToolFunc(const std::string& command, const std::string& file);
+
+} // namespace WalletTool
+
+#endif // BITCOIN_WALLET_WALLETTOOL_H
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 352fa32b5b..6dcaff0696 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -139,6 +139,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
config = configparser.ConfigParser()
config.read_file(open(self.options.configfile))
+ self.config = config
self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/bitcoind' + config["environment"]["EXEEXT"])
self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/bitcoin-cli' + config["environment"]["EXEEXT"])
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 8c6f6706e7..5c92370b85 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -108,6 +108,7 @@ BASE_SCRIPTS = [
'interface_bitcoin_cli.py',
'mempool_resurrect.py',
'wallet_txn_doublespend.py --mineblock',
+ 'tool_wallet.py',
'wallet_txn_clone.py',
'wallet_txn_clone.py --segwit',
'rpc_getchaintips.py',
@@ -562,7 +563,7 @@ class TestResult():
def check_script_prefixes():
"""Check that test scripts start with one of the allowed name prefixes."""
- good_prefixes_re = re.compile("(example|feature|interface|mempool|mining|p2p|rpc|wallet)_")
+ good_prefixes_re = re.compile("(example|feature|interface|mempool|mining|p2p|rpc|wallet|tool)_")
bad_script_names = [script for script in ALL_SCRIPTS if good_prefixes_re.match(script) is None]
if bad_script_names:
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
new file mode 100755
index 0000000000..fbcf21e729
--- /dev/null
+++ b/test/functional/tool_wallet.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test bitcoin-wallet."""
+import subprocess
+import textwrap
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class ToolWalletTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def bitcoin_wallet_process(self, *args):
+ binary = self.config["environment"]["BUILDDIR"] + '/src/bitcoin-wallet' + self.config["environment"]["EXEEXT"]
+ args = ['-datadir={}'.format(self.nodes[0].datadir), '-regtest'] + list(args)
+ return subprocess.Popen([binary] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+
+ def assert_raises_tool_error(self, error, *args):
+ p = self.bitcoin_wallet_process(*args)
+ stdout, stderr = p.communicate()
+ assert_equal(p.poll(), 1)
+ assert_equal(stdout, '')
+ assert_equal(stderr.strip(), error)
+
+ def assert_tool_output(self, output, *args):
+ p = self.bitcoin_wallet_process(*args)
+ stdout, stderr = p.communicate()
+ assert_equal(p.poll(), 0)
+ assert_equal(stderr, '')
+ assert_equal(stdout, output)
+
+ def run_test(self):
+
+ self.assert_raises_tool_error('Invalid command: foo', 'foo')
+ # `bitcoin-wallet help` is an error. Use `bitcoin-wallet -help`
+ self.assert_raises_tool_error('Invalid command: help', 'help')
+ self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
+ self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
+ self.assert_raises_tool_error('Error loading wallet.dat. Is wallet being used by other process?', '-wallet=wallet.dat', 'info')
+ self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info')
+
+ # stop the node to close the wallet to call info command
+ self.stop_node(0)
+
+ out = textwrap.dedent('''\
+ Wallet info
+ ===========
+ Encrypted: no
+ HD (hd seed available): yes
+ Keypool Size: 2
+ Transactions: 0
+ Address Book: 3
+ ''')
+ self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
+
+ # mutate the wallet to check the info command output changes accordingly
+ self.start_node(0)
+ self.nodes[0].generate(1)
+ self.stop_node(0)
+
+ out = textwrap.dedent('''\
+ Wallet info
+ ===========
+ Encrypted: no
+ HD (hd seed available): yes
+ Keypool Size: 2
+ Transactions: 1
+ Address Book: 3
+ ''')
+ self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
+
+ out = textwrap.dedent('''\
+ Topping up keypool...
+ Wallet info
+ ===========
+ Encrypted: no
+ HD (hd seed available): yes
+ Keypool Size: 2000
+ Transactions: 0
+ Address Book: 0
+ ''')
+ self.assert_tool_output(out, '-wallet=foo', 'create')
+
+ self.start_node(0, ['-wallet=foo'])
+ out = self.nodes[0].getwalletinfo()
+ self.stop_node(0)
+
+ assert_equal(0, out['txcount'])
+ assert_equal(1000, out['keypoolsize'])
+ assert_equal(1000, out['keypoolsize_hd_internal'])
+ assert_equal(True, 'hdseedid' in out)
+
+if __name__ == '__main__':
+ ToolWalletTest().main()