Exemple #1
0
    def __init__(
        self,
        datadir,
        noise_priv,
        bitcoin_priv,
        listen_port,
        managers_noisekeys,
    ):
        TailableProc.__init__(self, datadir, verbose=VERBOSE)
        self.conf_file = os.path.join(datadir, "config.toml")
        self.cmd_line = [COSIGNERD_PATH, "--conf", f"{self.conf_file}"]
        self.prefix = "cosignerd"

        noise_secret_file = os.path.join(datadir, "noise_secret")
        with open(noise_secret_file, "wb") as f:
            f.write(noise_priv)

        bitcoin_secret_file = os.path.join(datadir, "bitcoin_secret")
        with open(bitcoin_secret_file, "wb") as f:
            f.write(bitcoin_priv)

        with open(self.conf_file, "w") as f:
            f.write("daemon = false\n")
            f.write(f'data_dir = "{datadir}"\n')
            f.write(f'log_level = "{LOG_LEVEL}"\n')
            f.write(f'listen = "127.0.0.1:{listen_port}"\n')

            for k in managers_noisekeys:
                f.write("[[managers]]\n")
                f.write(f'    noise_key = "{k.hex()}"\n')
Exemple #2
0
 def start(self):
     TailableProc.start(self)
     self.wait_for_logs([
         "revaultd started on network regtest",
         "bitcoind now synced",
         "JSONRPC server started",
         "Signature fetcher thread started",
     ])
Exemple #3
0
def test_sigfetcher(revault_network, bitcoind, executor):
    rn = revault_network
    rn.deploy(7, 3, n_stkmanagers=2)
    # First of all, activate a vault
    vault = revault_network.fund(0.05)
    revault_network.secure_vault(vault)
    revault_network.activate_vault(vault)

    # Stopping revaultd, deleting the database
    for w in rn.participants():
        w.stop()
        datadir_db = os.path.join(w.datadir_with_network, "revaultd.sqlite3")
        os.remove(datadir_db)

    # Starting revaultd again
    for w in rn.participants():
        # Manually starting it so that we can check that
        # the db is being created again
        TailableProc.start(w)
        w.wait_for_logs([
            "No database at .*, creating a new one",
            "revaultd started on network regtest",
            "bitcoind now synced",
            "JSONRPC server started",
            "Signature fetcher thread started",
        ])

    # They should all get back to the 'active' state, pulling sigs from the coordinator
    for w in rn.participants():
        w.wait_for_log("Got a new unconfirmed deposit")
        wait_for(lambda: len(w.rpc.listvaults(["funded"], [])) == 1)
    for w in rn.stks():
        w.wait_for_logs([
            "Syncing Unvault Emergency signature",
            "Syncing Emergency signature",
            "Syncing Cancel signature",
            "Syncing Unvault signature",
        ])
    for w in rn.man_wallets:
        w.wait_for_logs([
            "Syncing Cancel signature",
            "Syncing Unvault signature",
        ])
Exemple #4
0
 def stop(self, timeout=10):
     try:
         self.rpc.stop()
         self.wait_for_logs([
             "Stopping revaultd.",
             "Bitcoind received shutdown.",
             "Signature fetcher thread received shutdown.",
         ])
         self.proc.wait(timeout)
     except Exception as e:
         logging.error(f"{self.prefix} : error when calling stop: '{e}'")
     return TailableProc.stop(self)
Exemple #5
0
    def __init__(self, bitcoin_dir, rpcport=None):
        TailableProc.__init__(self, bitcoin_dir, verbose=False)

        if rpcport is None:
            rpcport = reserve()

        self.bitcoin_dir = bitcoin_dir
        self.rpcport = rpcport
        self.p2pport = reserve()
        self.prefix = "bitcoind"

        regtestdir = os.path.join(bitcoin_dir, "regtest")
        if not os.path.exists(regtestdir):
            os.makedirs(regtestdir)

        self.cmd_line = [
            "bitcoind",
            "-datadir={}".format(bitcoin_dir),
            "-printtoconsole",
            "-server",
            "-logtimestamps",
            "-rpcthreads=4",
        ]
        bitcoind_conf = {
            "port": self.p2pport,
            "rpcport": rpcport,
            "debug": 1,
            "fallbackfee": Decimal(1000) / bitcoin.core.COIN,
        }
        self.conf_file = os.path.join(bitcoin_dir, "bitcoin.conf")
        with open(self.conf_file, "w") as f:
            f.write("chain=regtest\n")
            f.write("[regtest]\n")
            for k, v in bitcoind_conf.items():
                f.write(f"{k}={v}\n")

        self.rpc = SimpleBitcoinProxy(
            bitcoind_dir=self.bitcoin_dir, bitcoind_port=self.rpcport
        )
        self.proxies = []
Exemple #6
0
    def __init__(self, bitcoin_dir, rpcport=None):
        TailableProc.__init__(self, bitcoin_dir, verbose=False)

        if rpcport is None:
            rpcport = reserve()

        self.bitcoin_dir = bitcoin_dir
        self.rpcport = rpcport
        self.p2pport = reserve()
        self.prefix = "bitcoind"

        regtestdir = os.path.join(bitcoin_dir, "regtest")
        if not os.path.exists(regtestdir):
            os.makedirs(regtestdir)

        self.cmd_line = [
            BITCOIND_PATH,
            "-datadir={}".format(bitcoin_dir),
            "-printtoconsole",
            "-server",
        ]
        bitcoind_conf = {
            "port": self.p2pport,
            "rpcport": rpcport,
            "debug": 1,
            "fallbackfee": Decimal(1000) / COIN,
            "rpcthreads": 32,
        }
        self.conf_file = os.path.join(bitcoin_dir, "bitcoin.conf")
        with open(self.conf_file, "w") as f:
            f.write("chain=regtest\n")
            f.write("[regtest]\n")
            for k, v in bitcoind_conf.items():
                f.write(f"{k}={v}\n")

        self.rpc = BitcoindRpcInterface(bitcoin_dir, "regtest", rpcport)
Exemple #7
0
 def start(self):
     TailableProc.start(self)
     self.wait_for_log("Started cosignerd daemon")
Exemple #8
0
    def __init__(
        self,
        datadir,
        deposit_desc,
        unvault_desc,
        cpfp_desc,
        noise_priv,
        coordinator_noise_key,
        coordinator_port,
        bitcoind,
        stk_config=None,
        man_config=None,
    ):
        assert stk_config is not None or man_config is not None
        TailableProc.__init__(self, datadir, verbose=VERBOSE)

        self.prefix = os.path.split(datadir)[-1]

        # The data is stored in a per-network directory. We need to create it
        # in order to write the Noise private key
        self.datadir_with_network = os.path.join(datadir, "regtest")
        os.makedirs(self.datadir_with_network, exist_ok=True)

        bin = os.path.join(os.path.dirname(__file__), "..", "..",
                           "target/debug/revaultd")
        self.conf_file = os.path.join(datadir, "config.toml")
        self.cmd_line = [bin, "--conf", f"{self.conf_file}"]
        socket_path = os.path.join(self.datadir_with_network, "revaultd_rpc")
        self.rpc = UnixDomainSocketRpc(socket_path)

        noise_secret_file = os.path.join(self.datadir_with_network,
                                         "noise_secret")
        with open(noise_secret_file, "wb") as f:
            f.write(noise_priv)

        bitcoind_cookie = os.path.join(bitcoind.bitcoin_dir, "regtest",
                                       ".cookie")
        with open(self.conf_file, "w") as f:
            f.write(f"data_dir = '{datadir}'\n")
            f.write("daemon = false\n")
            f.write(f"log_level = '{LOG_LEVEL}'\n")

            f.write(f'coordinator_host = "127.0.0.1:{coordinator_port}"\n')
            f.write(f'coordinator_noise_key = "{coordinator_noise_key}"\n')
            f.write("coordinator_poll_seconds = 5\n")

            f.write("[scripts_config]\n")
            f.write(f'deposit_descriptor = "{deposit_desc}"\n')
            f.write(f'unvault_descriptor = "{unvault_desc}"\n')
            f.write(f'cpfp_descriptor = "{cpfp_desc}"\n')

            f.write("[bitcoind_config]\n")
            f.write('network = "regtest"\n')
            f.write(f"cookie_path = '{bitcoind_cookie}'\n")
            f.write(f"addr = '127.0.0.1:{bitcoind.rpcport}'\n")
            f.write("poll_interval_secs = 10\n")

            if stk_config is not None:
                f.write("[stakeholder_config]\n")
                self.stk_keychain = stk_config["keychain"]
                f.write(f'xpub = "{self.stk_keychain.get_xpub()}"\n')
                f.write("watchtowers = [")
                for wt in stk_config["watchtowers"]:
                    f.write(f"{{ \"host\" = \"{wt['host']}\", \"noise_key\" = "
                            f"\"{wt['noise_key'].hex()}\" }}, ")
                f.write("]\n")
                f.write(
                    f"emergency_address = \"{stk_config['emergency_address']}\"\n"
                )

            if man_config is not None:
                f.write("[manager_config]\n")
                self.man_keychain = man_config["keychain"]
                f.write(f'xpub = "{self.man_keychain.get_xpub()}"\n')
                for cosig in man_config["cosigners"]:
                    f.write("[[manager_config.cosigners]]\n")
                    f.write(f"host = \"{cosig['host']}\"\n")
                    f.write(f"noise_key = \"{cosig['noise_key'].hex()}\"\n")
Exemple #9
0
 def stop(self, timeout=10):
     return TailableProc.stop(self)
Exemple #10
0
 def start(self):
     TailableProc.start(self)
     self.wait_for_logs([
         "bitcoind now synced", "Listener thread started",
         "Started miradord."
     ])
Exemple #11
0
    def __init__(
        self,
        datadir,
        deposit_desc,
        unvault_desc,
        cpfp_desc,
        emer_addr,
        listen_port,
        noise_priv,
        stk_noise_key,
        coordinator_noise_key,
        coordinator_port,
        bitcoind_rpcport,
        bitcoind_cookie,
        plugins=[],
    ):
        """All public keys must be hex"""
        TailableProc.__init__(self, datadir, verbose=VERBOSE)

        self.prefix = os.path.split(datadir)[-1]
        self.noise_secret = noise_priv
        self.listen_port = listen_port
        self.deposit_desc = deposit_desc
        self.unvault_desc = unvault_desc
        self.cpfp_desc = cpfp_desc
        self.emer_addr = emer_addr

        # The data is stored in a per-network directory. We need to create it
        # in order to write the Noise private key
        self.datadir_with_network = os.path.join(datadir, "regtest")
        os.makedirs(self.datadir_with_network, exist_ok=True)

        self.conf_file = os.path.join(datadir, "config.toml")
        self.cmd_line = [MIRADORD_PATH, "--conf", f"{self.conf_file}"]

        self.noise_secret_file = os.path.join(self.datadir_with_network,
                                              "noise_secret")
        with open(self.noise_secret_file, "wb") as f:
            f.write(noise_priv)
        wt_noise_key = bytes(Curve25519Private(noise_priv).public_key)
        logging.debug(
            f"Watchtower Noise key: {wt_noise_key.hex()}, Stakeholder Noise key: {stk_noise_key}"
        )

        with open(self.conf_file, "w") as f:
            f.write(f"data_dir = '{datadir}'\n")
            f.write("daemon = false\n")
            f.write(f"log_level = '{LOG_LEVEL}'\n")

            f.write(f'stakeholder_noise_key = "{stk_noise_key}"\n')

            f.write(f'coordinator_host = "127.0.0.1:{coordinator_port}"\n')
            f.write(f'coordinator_noise_key = "{coordinator_noise_key}"\n')
            f.write("coordinator_poll_seconds = 5\n")

            f.write(f'listen = "127.0.0.1:{listen_port}"\n')

            f.write("[scripts_config]\n")
            f.write(f'deposit_descriptor = "{deposit_desc}"\n')
            f.write(f'unvault_descriptor = "{unvault_desc}"\n')
            f.write(f'cpfp_descriptor = "{cpfp_desc}"\n')
            f.write(f'emergency_address = "{emer_addr}"\n')

            f.write("[bitcoind_config]\n")
            f.write('network = "regtest"\n')
            f.write(f"cookie_path = '{bitcoind_cookie}'\n")
            f.write(f"addr = '127.0.0.1:{bitcoind_rpcport}'\n")
            f.write("poll_interval_secs = 5\n")

            f.write(f"\n{toml.dumps({'plugins': plugins})}\n")
Exemple #12
0
    def start(self):
        TailableProc.start(self)
        self.wait_for_log("Done loading", timeout=TIMEOUT)

        logging.info("BitcoinD started")
Exemple #13
0
 def stop(self):
     for p in self.proxies:
         p.stop()
     self.rpc.stop()
     return TailableProc.stop(self)
Exemple #14
0
 def start(self):
     TailableProc.start(self)
     self.wait_for_logs(["Started revault_coordinatord"])
Exemple #15
0
    def __init__(
        self,
        datadir,
        noise_priv,
        managers_keys,
        stakeholders_keys,
        watchtowers_keys,
        listen_port,
        bitcoind_rpc_port,
        bitcoind_cookie_path,
        postgres_user,
        postgres_pass,
        postgres_host="localhost",
    ):
        # FIXME: reduce DEBUG log load
        TailableProc.__init__(self, datadir, verbose=VERBOSE)
        self.conf_file = os.path.join(datadir, "config.toml")
        self.cmd_line = [COORDINATORD_PATH, "--conf", f"{self.conf_file}"]
        self.prefix = "coordinatord"

        self.postgres_user = postgres_user
        self.postgres_pass = postgres_pass
        self.postgres_host = postgres_host
        # Use the directory fixture uid
        uid = os.path.basename(os.path.dirname(
            os.path.dirname(datadir))).replace("-", "")
        self.db_name = f"revault_coordinatord_{uid}"
        # Cleanup a potential leftover from a crashed test
        try:
            self.postgres_exec(f"DROP DATABASE {self.db_name}")
        except psycopg2.errors.InvalidCatalogName:
            pass
        # Now actually create it
        self.postgres_exec(
            f"CREATE DATABASE {self.db_name} OWNER {postgres_user}")

        noise_secret_file = os.path.join(datadir, "noise_secret")
        with open(noise_secret_file, "wb") as f:
            f.write(noise_priv)

        with open(self.conf_file, "w") as f:
            f.write("daemon = false\n")
            f.write(f'data_dir = "{datadir}"\n')
            f.write(f'log_level = "{LOG_LEVEL}"\n')

            uri = (f"postgresql://{postgres_user}:{postgres_pass}"
                   f"@{postgres_host}/{self.db_name}")
            f.write(f'postgres_uri = "{uri}"\n')

            f.write("managers = [")
            for k in managers_keys:
                f.write(f'"{k.hex()}", ')
            f.write("]\n")

            f.write("stakeholders = [")
            for k in stakeholders_keys:
                f.write(f'"{k.hex()}", ')
            f.write("]\n")

            f.write("watchtowers = [")
            for k in watchtowers_keys:
                f.write(f'"{k.hex()}", ')
            f.write("]\n")

            f.write(f'listen = "127.0.0.1:{listen_port}"\n')

            f.write("[bitcoind_config]\n")
            f.write(f"cookie_path = '{bitcoind_cookie_path}'\n")
            f.write(f"addr = '127.0.0.1:{bitcoind_rpc_port}'\n")
            f.write("broadcast_interval = 5\n")
Exemple #16
0
    def __init__(
        self,
        datadir,
        stks,
        cosigs,
        mans,
        csv,
        noise_priv,
        coordinator_noise_key,
        coordinator_port,
        bitcoind,
        stk_config=None,
        man_config=None,
    ):
        assert stk_config is not None or man_config is not None
        assert len(stks) == len(cosigs)
        TailableProc.__init__(self, datadir, verbose=VERBOSE)

        self.prefix = os.path.split(datadir)[-1]

        # The data is stored in a per-network directory. We need to create it
        # in order to write the Noise private key
        self.datadir_with_network = os.path.join(datadir, "regtest")
        os.makedirs(self.datadir_with_network, exist_ok=True)

        bin = os.path.join(os.path.dirname(__file__), "..", "..",
                           "target/debug/revaultd")
        self.conf_file = os.path.join(datadir, "config.toml")
        self.cmd_line = [bin, "--conf", f"{self.conf_file}"]
        socket_path = os.path.join(self.datadir_with_network, "revaultd_rpc")
        self.rpc = UnixDomainSocketRpc(socket_path)

        noise_secret_file = os.path.join(self.datadir_with_network,
                                         "noise_secret")
        with open(noise_secret_file, "wb") as f:
            f.write(noise_priv)

        bitcoind_cookie = os.path.join(bitcoind.bitcoin_dir, "regtest",
                                       ".cookie")
        with open(self.conf_file, "w") as f:
            f.write(f"unvault_csv = {csv}\n")
            f.write(f"data_dir = '{datadir}'\n")
            f.write("daemon = false\n")
            f.write(f"log_level = '{LOG_LEVEL}'\n")

            f.write(f'coordinator_host = "127.0.0.1:{coordinator_port}"\n')
            f.write(f'coordinator_noise_key = "{coordinator_noise_key}"\n')
            f.write("coordinator_poll_seconds = 2\n")

            f.write("stakeholders_xpubs = [")
            for stk in stks:
                f.write(f'"{stk.get_xpub()}", ')
            f.write("]\n")

            f.write("managers_xpubs = [")
            for man in mans:
                f.write(f'"{man.get_xpub()}", ')
            f.write("]\n")

            f.write("cosigners_keys = [")
            for cosig in cosigs:
                f.write(f'"{cosig.get_static_key().hex()}", ')
            f.write("]\n")

            f.write("[bitcoind_config]\n")
            f.write('network = "regtest"\n')
            f.write(f"cookie_path = '{bitcoind_cookie}'\n")
            f.write(f"addr = '127.0.0.1:{bitcoind.rpcport}'\n")
            f.write("poll_interval_secs = 3\n")

            if stk_config is not None:
                f.write("[stakeholder_config]\n")
                self.stk_keychain = stk_config["keychain"]
                f.write(f'xpub = "{self.stk_keychain.get_xpub()}"\n')
                f.write("watchtowers = [")
                for wt in stk_config["watchtowers"]:
                    f.write(f"{{ \"host\" = \"{wt['host']}\", \"noise_key\" = "
                            f"\"{wt['noise_key'].hex()}\" }}, ")
                f.write("]\n")
                # FIXME: eventually use a real one here
                f.write(
                    "emergency_address = "
                    '"bcrt1qewc2348370pgw8kjz8gy09z8xyh0d9fxde6nzamd3txc9gkmjqmq8m4cdq"\n'
                )

            if man_config is not None:
                f.write("[manager_config]\n")
                self.man_keychain = man_config["keychain"]
                f.write(f'xpub = "{self.man_keychain.get_xpub()}"\n')
                for cosig in man_config["cosigners"]:
                    f.write("[[manager_config.cosigners]]\n")
                    f.write(f"host = \"{cosig['host']}\"\n")
                    f.write(f"noise_key = \"{cosig['noise_key'].hex()}\"\n")