From 2cfd5297b0fcfa4ab16f5ead40ffd9e21ac153a5 Mon Sep 17 00:00:00 2001
From: alex <alex@infiniteadaptability.org>
Date: Wed, 22 Jun 2022 18:02:19 -0700
Subject: [PATCH] ...

---
 .gitignore                                    |   3 -
 cold-setup                                    |  18 +
 test/setup.sh                                 | 708 ++++++++++++++++++
 test/wallet_correctness.tests.sh              |  20 +
 test/wallet_create_blank.tests.sh             |  21 +
 test/wallet_dump_descriptors.tests.sh         |  21 +
 ...llet_dump_descriptors_correctness.tests.sh |  32 +
 test/wallet_load.tests.sh                     | 149 ++++
 8 files changed, 969 insertions(+), 3 deletions(-)
 create mode 100755 test/wallet_correctness.tests.sh
 create mode 100755 test/wallet_create_blank.tests.sh
 create mode 100755 test/wallet_dump_descriptors.tests.sh
 create mode 100755 test/wallet_dump_descriptors_correctness.tests.sh
 create mode 100755 test/wallet_load.tests.sh

diff --git a/.gitignore b/.gitignore
index c0755f8..707288d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,9 +18,6 @@ configure.scan
 **/Makefile.in
 stamp-h1
 
-# tags
-tags
-
 # build objects
 *.o
 *.log
diff --git a/cold-setup b/cold-setup
index 8c441fe..ec45d85 100755
--- a/cold-setup
+++ b/cold-setup
@@ -120,6 +120,12 @@ wallet_create() {
 	log_info "created wallet: wallet$1"
 }
 
+wallet_create_blank() {
+	local CMD="$BITCOIN_CLI -named createwallet wallet_name=wallet$1 descriptors=true blank=true"
+	eval "$CMD" > /dev/null
+	log_info "created blank wallet: wallet$1"
+}
+
 wallet_descriptors() {
 	local CMD="$BITCOIN_CLI -rpcwallet=wallet$1 listdescriptors | jq -j '.descriptors | map(select(.desc | startswith(\"wpkh(\"))) | map(select(.internal == false)) | map(.desc | ltrimstr(\"wpkh(\")) | .[] | split(\")\") | .[0]'"
 	local DESCRIPTORS=$(eval "$CMD")
@@ -134,6 +140,18 @@ wallet_dump_descriptors() {
 	log_info "dumped descriptors for wallet$1 into $DUMPFILE"
 }
 
+wallet_load() {
+	if [[ ! -e "$2" ]]; then
+		log_error "file not found: $2"
+		exit 1
+	fi
+
+	local CMD="$BITCOIN_CLI -stdin -rpcwallet=wallet$1 importdescriptors"
+	wallet_create_blank "$1"
+	cat "$2" | jq -r '.descriptors' | tr -d '\n' | eval "$CMD"
+	log_info "loaded descriptors for wallet$1 from $2"
+}
+
 wallets() {
 	for((i = 1; i <= $1; i++)); do
 		wallet_create "$i"
diff --git a/test/setup.sh b/test/setup.sh
index 14eabda..adbda6e 100644
--- a/test/setup.sh
+++ b/test/setup.sh
@@ -16,6 +16,714 @@ setup_env() {
 	bitcoin_core_start
 } > /dev/null 2>&1
 
+setup_test_wallets() {
+	cat > wallet1.descriptors << EOF
+{
+  "wallet_name": "wallet1",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/44'/1'/0'/0/*)#tzs7n72a",
+      "timestamp": 1655929836,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/49'/1'/0'/0/*))#jmxrm0zc",
+      "timestamp": 1655929836,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/84'/1'/0'/0/*)#llselrjy",
+      "timestamp": 1655929836,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/86'/1'/0'/0/*)#p0vpuryp",
+      "timestamp": 1655929836,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/44'/1'/0'/1/*)#6k4lwt69",
+      "timestamp": 1655929836,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/49'/1'/0'/1/*))#5cwxqzfv",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/84'/1'/0'/1/*)#wt4czkzu",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPeD8yyw44GTpzonPir41WK5cS3o7UMDLJiufmNbW4ReMCk9q5UrS9ynty9vyH2QCbxatZsDg4MZUrrujWfSPVNCujUXtSDKx/86'/1'/0'/1/*)#smfqpk5e",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+	cat > wallet2.descriptors << EOF
+{
+  "wallet_name": "wallet2",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/44'/1'/0'/0/*)#hta7kvm6",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/49'/1'/0'/0/*))#qgt78vw3",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/84'/1'/0'/0/*)#7n5mv4hy",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/86'/1'/0'/0/*)#5rj7jqqr",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/44'/1'/0'/1/*)#xlcltetz",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/49'/1'/0'/1/*))#xtrmup99",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/84'/1'/0'/1/*)#08363q8u",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPdqqBLnUjZibmJ1CQfNPengbCfJCN4H1ryHVvW36CMhZ7kTCyyGANzeLGFzZwFCYuiMs9m1u32YVgrqA3c7Bm7yk3kM6AVvd/86'/1'/0'/1/*)#9hhl04sm",
+      "timestamp": 1655929837,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+	cat > wallet3.descriptors << EOF
+{
+  "wallet_name": "wallet3",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/44'/1'/0'/0/*)#f4evjc6q",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/49'/1'/0'/0/*))#64pdkks9",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/84'/1'/0'/0/*)#0m0c82sf",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/86'/1'/0'/0/*)#frryg4st",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/44'/1'/0'/1/*)#cpud0d2c",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/49'/1'/0'/1/*))#ukfgdmm3",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/84'/1'/0'/1/*)#702e6lq3",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPf8ajraNisX2tTyjeGHaeqh4kzu3uzdc4D9RrRJmoErxhhzb5dcNUH1PqUcr3d3aGxJLdBqi6w38iiD31GyjQVxKcuoNdU7V/86'/1'/0'/1/*)#chx94qqn",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+	cat > wallet4.descriptors << EOF
+{
+  "wallet_name": "wallet4",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/44'/1'/0'/0/*)#vhqrracp",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/49'/1'/0'/0/*))#0wh3dpek",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/84'/1'/0'/0/*)#xklz7ech",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/86'/1'/0'/0/*)#cq8cfl65",
+      "timestamp": 1655929838,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/44'/1'/0'/1/*)#ar9z7gge",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/49'/1'/0'/1/*))#fdl5kvjz",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/84'/1'/0'/1/*)#hz6rrvg0",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPfLE512gVXfTk3irRnPToR2EhJ1iWJQWgjqqhDN17YTqr31nV1KuBtX86NrfFzvCSoxB9zQKAwPVttXqCN831WAreyPff1Q2/86'/1'/0'/1/*)#f5ze522v",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+	cat > wallet5.descriptors << EOF
+{
+  "wallet_name": "wallet5",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/44'/1'/0'/0/*)#h5mmuhls",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/49'/1'/0'/0/*))#0jj2f86a",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/84'/1'/0'/0/*)#3jv9sxhx",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/86'/1'/0'/0/*)#3d8cykkz",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/44'/1'/0'/1/*)#xq76pz0g",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/49'/1'/0'/1/*))#f360j23f",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/84'/1'/0'/1/*)#qxfydn87",
+      "timestamp": 1655929839,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPdzeP7ZpsmpfNR1vVg8Vk4fRbQtStrrzuBx2NieArfZ8ykZtvBfbFtrhsnfSrSGKZqRdyXK8CJdCw3GmXx12JvMi91R8Y5MB/86'/1'/0'/1/*)#qezeerx6",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+	cat > wallet6.descriptors << EOF
+{
+  "wallet_name": "wallet6",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/44'/1'/0'/0/*)#z6tplava",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/49'/1'/0'/0/*))#ktstdeta",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/84'/1'/0'/0/*)#3t483zfh",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/86'/1'/0'/0/*)#23xjrzsm",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/44'/1'/0'/1/*)#nwwqzgu9",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/49'/1'/0'/1/*))#sgcwk5qf",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/84'/1'/0'/1/*)#qlsxvhe0",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPedDiqJFarv77bfM5gsHkdknBrJUj5PRzt27zW778tQZzuDoxAir8CwamSDRkFWtTY3RRBEHaqDdTexvFHJ4cvurLiQ4fbzW/86'/1'/0'/1/*)#m9rn7hqr",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+	cat > wallet7.descriptors << EOF
+{
+  "wallet_name": "wallet7",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/44'/1'/0'/0/*)#c4ec65vr",
+      "timestamp": 1655929840,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/49'/1'/0'/0/*))#vuy63der",
+      "timestamp": 1655929841,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/84'/1'/0'/0/*)#u2825h6t",
+      "timestamp": 1655929841,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/86'/1'/0'/0/*)#hs066nsw",
+      "timestamp": 1655929841,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/44'/1'/0'/1/*)#fpue8pum",
+      "timestamp": 1655929841,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/49'/1'/0'/1/*))#2lvl2qjh",
+      "timestamp": 1655929841,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/84'/1'/0'/1/*)#d7ztfz2n",
+      "timestamp": 1655929841,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPefbF8gAMusMAzPTZ4JdaFe4sVt28fenqXhpCUmKKW7oFxxKKrctrWJXhieR9mywgsfFq4eXiudU27Jri7pZLrFzxvVN3Jc3/86'/1'/0'/1/*)#xy2m8xqk",
+      "timestamp": 1655929841,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+	for((i = 1; i<= 7; i++)); do
+		(wallet_load "$i" "wallet$i.descriptors" > result 2>&1)
+		EXPECTED="created blank wallet: wallet$i
+[
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  }
+]
+loaded descriptors for wallet$i from wallet$i.descriptors"
+		RESULT=$(<result)
+		assert "$EXPECTED" "$RESULT"
+	done
+}
+
 reset_env() {
 	clean_env
 	setup_env
diff --git a/test/wallet_correctness.tests.sh b/test/wallet_correctness.tests.sh
new file mode 100755
index 0000000..8009f82
--- /dev/null
+++ b/test/wallet_correctness.tests.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source setup.sh
+
+setup_env
+
+setup_test_wallets
+
+for((i = 1; i <= 7; i++)); do
+	for((j = 1; j <= 1000; j++)); do
+		echo "`$BITCOIN_CLI -rpcwallet=wallet$i getnewaddress`" >> addresses
+	done
+done
+
+assert "4c51b3003c3733763bac695a214ffa11c81fcff4099752536da661c8349eb4e90b60b8463152c4009542611c0639df7be10ece2c291d27a06673292f1d395bb4  addresses" "`b2sum addresses`"
+clean_env
+
+test_succeeded
diff --git a/test/wallet_create_blank.tests.sh b/test/wallet_create_blank.tests.sh
new file mode 100755
index 0000000..10230b3
--- /dev/null
+++ b/test/wallet_create_blank.tests.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source setup.sh
+
+setup_env
+
+(wallet_create_blank 1 > result 2>&1)
+EXPECTED="created blank wallet: wallet1"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+(wallet_create_blank 2 > result 2>&1)
+EXPECTED="created blank wallet: wallet2"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+clean_env
+
+test_succeeded
diff --git a/test/wallet_dump_descriptors.tests.sh b/test/wallet_dump_descriptors.tests.sh
new file mode 100755
index 0000000..400ac9d
--- /dev/null
+++ b/test/wallet_dump_descriptors.tests.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source setup.sh
+
+setup_env
+
+(wallet_create 1 > result 2>&1)
+EXPECTED="created wallet: wallet1"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+(wallet_dump_descriptors 1 > result 2>&1)
+EXPECTED="dumped descriptors for wallet1 into $DATA_DIRECTORY/wallet1.descriptors"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+clean_env
+
+test_succeeded
diff --git a/test/wallet_dump_descriptors_correctness.tests.sh b/test/wallet_dump_descriptors_correctness.tests.sh
new file mode 100755
index 0000000..77afe14
--- /dev/null
+++ b/test/wallet_dump_descriptors_correctness.tests.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source setup.sh
+
+setup_env
+
+setup_test_wallets
+
+rm *.descriptors
+
+for((i = 1; i <= 7; i++)); do
+	(wallet_dump_descriptors "$i" > result 2>&1)
+	EXPECTED="dumped descriptors for wallet$i into $DATA_DIRECTORY/wallet$i.descriptors"
+	RESULT=$(<result)
+	assert "$EXPECTED" "$RESULT"
+done
+
+EXPECTED="8abc670921298bdff8d28593b4fd6d581a4c83f539e3ffb6d295248dbbf31558fff8681af20b607f9483d0981b184b39cfad107e63a2309f9a6319321659f750  $DATA_DIRECTORY/wallet1.descriptors
+0f8b2546105fc4501dabffba85d4eddcb066a0d8e8e9fd557489de239f1cff47549ea9ee3a001eae3a88ee28355941e4814157c3508f53538131548fb4f97d5d  $DATA_DIRECTORY/wallet2.descriptors
+fa731c649b6dce35dfacabe7bc863462170f4cc612c84957320ee65a1c9ef85b20222c0c199e0f4244413176e75c76e2a42b81ee1cd90906270ae99ec2d003eb  $DATA_DIRECTORY/wallet3.descriptors
+f86b85d9ff88604e1669beaf37138674671d024decb7ff801543208209b387ccce466dc970b1e5b7c3a3a99511ce034e2aa9585208ef3cc3e9a5a52c7cd6a1c9  $DATA_DIRECTORY/wallet4.descriptors
+13cbc0c1b4911720cdda5e0fa8f10bb2798aa34bf03f32ffcebf194f5c664e7082192c58ae4f3812789f24ac96127d105315658e70801315fe6987e0530d36f3  $DATA_DIRECTORY/wallet5.descriptors
+0b3d072b59b4c01161daff81467fda485ee97360767d79c3d5161f783ae4ac282ec03dc2d9bb33b490006df85f62d5b676e178589f6c67c178c4c4300a231783  $DATA_DIRECTORY/wallet6.descriptors
+df5d6ac961e6008ab83ab1e4843cb52628f2250d84df467dffd3b208c290e2d0125de6f12faf9f47ee1201703084f8a06b18772e434d7fa5f40424bcd86a03a6  $DATA_DIRECTORY/wallet7.descriptors"
+RESULT="`b2sum $DATA_DIRECTORY/*.descriptors`"
+assert "$EXPECTED" "$RESULT"
+
+clean_env
+
+test_succeeded
diff --git a/test/wallet_load.tests.sh b/test/wallet_load.tests.sh
new file mode 100755
index 0000000..3097cc3
--- /dev/null
+++ b/test/wallet_load.tests.sh
@@ -0,0 +1,149 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+source setup.sh
+
+setup_env
+
+set +e
+(wallet_load 1 "walletX.descriptors" > result 2>&1)
+assert "1" "$?"
+set -e
+EXPECTED="file not found: walletX.descriptors"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+cat > wallet2.descriptors << EOF
+{
+  "wallet_name": "wallet2",
+  "descriptors": [
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/44'/1'/0'/0/*)#gfjq40y4",
+      "timestamp": 1655928865,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/49'/1'/0'/0/*))#sek2p7p6",
+      "timestamp": 1655928865,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/84'/1'/0'/0/*)#f82ydgve",
+      "timestamp": 1655928865,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/86'/1'/0'/0/*)#am0y6gu3",
+      "timestamp": 1655928865,
+      "active": true,
+      "internal": false,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "pkh(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/44'/1'/0'/1/*)#eahpg65d",
+      "timestamp": 1655928865,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "sh(wpkh(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/49'/1'/0'/1/*))#k6706n2w",
+      "timestamp": 1655928865,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "wpkh(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/84'/1'/0'/1/*)#cn09saup",
+      "timestamp": 1655928865,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    },
+    {
+      "desc": "tr(tprv8ZgxMBicQKsPdbbppJxbeuNmjC9wNgqefkQeJPtnDPmRZNCgwb6paPYvRQxUAMm1TCEicmUbdKtWdbD2Yz37R3nMJtgNBiabNUK3Dvq9yf4/86'/1'/0'/1/*)#v0298avf",
+      "timestamp": 1655928866,
+      "active": true,
+      "internal": true,
+      "range": [
+        0,
+        999
+      ],
+      "next": 0
+    }
+  ]
+}
+EOF
+
+assert "ae894dbe95838f35513bff5707a0bcd6930920fe210940215505cff2b55c9ae7f493a41010a0dcc2789bec2583c4d6af7001da0a04a0ea92e371137e636bd6c5  wallet2.descriptors" "`b2sum wallet2.descriptors`"
+
+(wallet_load 2 "wallet2.descriptors" > result 2>&1)
+EXPECTED="created blank wallet: wallet2
+[
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  },
+  {
+    \"success\": true
+  }
+]
+loaded descriptors for wallet2 from wallet2.descriptors"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+clean_env
+
+test_succeeded
-- 
2.39.5