Skip to content

Commit

Permalink
Release v2.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
slezica committed Nov 12, 2021
1 parent 64a820d commit 58d843a
Show file tree
Hide file tree
Showing 249 changed files with 73,760 additions and 1,108 deletions.
Binary file added .DS_Store
Binary file not shown.
9 changes: 8 additions & 1 deletion BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,11 @@ the binaries we provide, you need to:
3. Verify that the printed checksums match those of the downloaded versions, using `sha256sum`
as in the `Makefile`.

We use Docker for these builds to ensure they are reproducible.
We use Docker for these builds to ensure they are reproducible.

### Note on MacOS

For the 2.2 release, we had to disable reproducible builds for MacOS. The inclusion of C code for
the musig implementation made building the tool inside a Linux container extremely difficult. We'll
be moving the process to GitHub actions soon, which can be easily audited and can build natively on
MacOS.
44 changes: 35 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,54 @@
# You need to pass 3 parameters via --build-arg:
# 1. `os` : the GOOS env var -- `linux`, `windows` or `darwin`.
# 2. `arch`: the GOARCH env var -- `386` or `amd64` (note that darwin/386 is not a thing).
# 3. `out` : the name of the resulting executable, placed in the output directory on the host.
# 3. `cc` : the CC env var -- a C compiler for CGO to use, empty to use the default.
# 4. `out` : the name of the resulting executable, placed in the output directory on the host.

# For example, to build a linux/386 binary into `bin/rt`:
# docker build . --output bin --build-arg os=linux --build-arg arch=386 --build-arg out=rt

# Note that the --output <dir> flag refers to the host, outside the container.

FROM golang:1.16.0-alpine3.13 AS build
ARG os
ARG arch
# --------------------------------------------------------------------------------------------------

FROM ubuntu:20.04 AS rtool-build-base

RUN apk add --no-cache build-base=0.5-r2
# Avoid prompts during package installation:
ENV DEBIAN_FRONTEND="noninteractive"

# Upgrade indices:
RUN apt-get update

# Install the various compilers we're going to use, with specific versions:
RUN apt-get install -y \
golang-1.16-go=1.16.2-0ubuntu1~20.04 \
gcc-mingw-w64=9.3.0-7ubuntu1+22~exp1ubuntu4 \
gcc-multilib=4:9.3.0-1ubuntu2

# Copy the source code into the container:
WORKDIR /src
COPY . .

ENV CGO_ENABLED=0
RUN env GOOS=${os} GOARCH=${arch} go build -mod=vendor -a -trimpath -o /out .
RUN /bin/bash

# --------------------------------------------------------------------------------------------------

FROM rtool-build-base AS rtool-build
ARG os
ARG arch
ARG cc

# Enable and configure C support:
ENV CGO_ENABLED=1
ENV GO386=softfloat

# Do the thing:
RUN env GOOS=${os} GOARCH=${arch} CC=${cc} /usr/lib/go-1.16/bin/go build -mod=vendor -a -trimpath -o /out .

# ---
# --------------------------------------------------------------------------------------------------

FROM scratch
ARG out

COPY --from=build /out ${out}
# Copy the resulting executable back to the host:
COPY --from=rtool-build /out ${out}
41 changes: 34 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,50 @@ build-checksum-all:
go mod vendor -v

# Linux 32-bit:
docker build . -o bin --build-arg os=linux --build-arg arch=386 --build-arg out=recovery-tool-linux32
docker build . -o bin \
--build-arg os=linux \
--build-arg arch=386 \
--build-arg out=recovery-tool-linux32

/bin/echo -n '✓ Linux 32-bit ' && sha256sum "bin/recovery-tool-linux32"

# Linux 64-bit:
docker build . -o bin --build-arg os=linux --build-arg arch=amd64 --build-arg out=recovery-tool-linux64
docker build . -o bin \
--build-arg os=linux \
--build-arg arch=amd64 \
--build-arg out=recovery-tool-linux64

/bin/echo -n '✓ Linux 64-bit ' && sha256sum "bin/recovery-tool-linux64"

# Windows 32-bit:
docker build . -o bin --build-arg os=windows --build-arg arch=386 --build-arg out=recovery-tool-windows32.exe
docker build . -o bin \
--build-arg os=windows \
--build-arg arch=386 \
--build-arg cc=i686-w64-mingw32-gcc \
--build-arg out=recovery-tool-windows32.exe

/bin/echo -n '✓ Windows 32-bit ' && sha256sum "bin/recovery-tool-windows32.exe"

# Windows 64-bit:
docker build . -o bin --build-arg os=windows --build-arg arch=amd64 --build-arg out=recovery-tool-windows64.exe
docker build . -o bin \
--build-arg os=windows \
--build-arg arch=amd64 \
--build-arg cc=x86_64-w64-mingw32-gcc \
--build-arg out=recovery-tool-windows64.exe

/bin/echo -n '✓ Windows 64-bit ' && sha256sum "bin/recovery-tool-windows64.exe"

# NOTE:
# Darwin reproducible builds are disabled for now, since the inclusion of C code in the latest
# release made building the tool inside a Linux container extremely difficult. We'll be moving the
# process to GitHub actions, where we can build on MacOS.

# Darwin 64-bit:
docker build . -o bin --build-arg os=darwin --build-arg arch=amd64 --build-arg out=recovery-tool-macos64
/bin/echo -n '✓ MacOS 64-bit ' && sha256sum "bin/recovery-tool-macos64"
# docker build . -o bin \
# --build-arg os=darwin \
# --build-arg arch=amd64 \
# --build-arg out=recovery-tool-macos64

# /bin/echo -n '✓ MacOS 64-bit ' && sha256sum "bin/recovery-tool-macos64"

.SILENT:
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ and follow the instructions below.

| System | Checksum | Link |
| --- | --- | --- |
| Linux 32-bit | `9dd403807dc7bcec0d38ff4168b66c468b1ad71e80c55eebfb0affb159e68549` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-linux32) |
| Linux 64-bit | `4d583fa4220c91409a3bb96ec3c72b9b4914bbe38f1a2e26fda234d498c0de04` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-linux64) |
| Windows 32-bit | `ce1631bbab868b2089455be93604ebb81e3f19b52cdaed439086f44e2df01682` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-windows32.exe) |
| Windows 64-bit | `10be8600e7fa524e35ec1e00ce516f598462520a688c243c84ae5b696ba57ee9` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-windows64.exe) |
| MacOS 64-bit | `1336c6814b6f040a027a593e437e1665c233ed6af9d5d3ab5b5323efe233cf05` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-macos64) |
| Linux 32-bit | `7b6de37a2c05635ddeaa77654805e6a94e7a596d4de7b54e3906d1dfe881f0de` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-linux32) |
| Linux 64-bit | `dd298ce92e05660363959ee1e5a1af5fa7f111957764623b8a187e07b1c86159` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-linux64) |
| Windows 32-bit | `2388bf2d6d024c81fc99245ed79cfd2edb3118ad926d07129c8d3fadebe44f91` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-windows32.exe) |
| Windows 64-bit | `6050d6226b26516365206e012acd1e6edc0365b29d4c585e0fc8968a7fcd06b6` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-windows64.exe) |
| MacOS 64-bit | `b842569fb380aa64a3ba5696f097f0512c2c4c8a8ea5da0ef9128eea80404af8` | [Download](https://raw.githubusercontent.com/muun/recovery/master/bin/recovery-tool-macos64) |

### Windows

Expand All @@ -41,8 +41,6 @@ chmod +x recovery-tool-macos64
./recovery-tool-macos64 <path to your Emergency Kit PDF>
```

If you attempt to open the file directly, MacOS will block you from using it.

#### Security Warnings

MacOS may prevent you from running the downloaded tool, depending on the active security settings. If it
Expand Down
8 changes: 8 additions & 0 deletions address_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,13 @@ func (g *AddressGenerator) deriveTree(rootUserKey, rootMuunKey *libwallet.HDPriv
log.Printf("failed to generate %v v4 for %v due to %v", name, i, err)
}

addrV5, err := libwallet.CreateAddressV5(userKey.PublicKey(), muunKey.PublicKey())
if err == nil {
g.addrs[addrV5.Address()] = signingDetails{
Address: addrV5,
}
} else {
log.Printf("failed to generate %v v5 for %v due to %v", name, i, err)
}
}
}
Binary file modified bin/recovery-tool-linux32
Binary file not shown.
Binary file modified bin/recovery-tool-linux64
Binary file not shown.
Binary file modified bin/recovery-tool-macos64
Binary file not shown.
Binary file modified bin/recovery-tool-windows32.exe
Binary file not shown.
Binary file modified bin/recovery-tool-windows64.exe
Binary file not shown.
62 changes: 31 additions & 31 deletions cmd/survey/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,50 @@ package main

import (
"fmt"
"time"

"github.com/muun/recovery/electrum"
"github.com/muun/recovery/scanner"
"github.com/muun/recovery/survey"
)

var failedToConnect []string
var withBatching []string
var withoutBatching []string

func main() {
client := electrum.NewClient()

for _, server := range scanner.PublicElectrumServers {
surveyServer(client, server)
config := &survey.Config{
InitialServers: electrum.PublicServers,
Workers: 30,
SpeedTestDuration: time.Second * 20,
SpeedTestBatchSize: 100,
}

fmt.Println("// With batch support:")
for _, server := range withBatching {
fmt.Printf("\"%s\"\n", server)
}
survey := survey.NewSurvey(config)
results := survey.Run()

fmt.Println("// Without batch support:")
for _, server := range withoutBatching {
fmt.Printf("\"%s\"\n", server)
fmt.Println("\n\n// Worthy servers:")
for _, result := range results {
if result.IsWorthy {
fmt.Println(toCodeLine(result))
}
}

fmt.Println("// Unclassified:")
for _, server := range failedToConnect {
fmt.Printf("\"%s\"\n", server)
fmt.Println("\n\n// Unworthy servers:")
for _, result := range results {
if !result.IsWorthy {
fmt.Println(toCodeLine(result))
}
}
}

func surveyServer(client *electrum.Client, server string) {
fmt.Println("Surveyng", server)
err := client.Connect(server)

if err != nil {
failedToConnect = append(failedToConnect, server)
return
func toCodeLine(r *survey.Result) string {
if r.Err != nil {
return fmt.Sprintf("\"%s\", // %v", r.Server, r.Err)
}

if client.SupportsBatching() {
withBatching = append(withBatching, server)
} else {
withoutBatching = append(withoutBatching, server)
}
return fmt.Sprintf(
"\"%s\", // impl: %s, batching: %v, ttc: %.2f, speed: %d, from: %s",
r.Server,
r.Impl,
r.BatchSupport,
r.TimeToConnect.Seconds(),
r.Speed,
r.FromPeer,
)
}
102 changes: 102 additions & 0 deletions electrum/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,30 @@ type ServerVersionResponse struct {
Result []string `json:"result"`
}

// ServerFeaturesResponse models the structure of a `server.features` response.
type ServerFeaturesResponse struct {
ID int `json:"id"`
Result ServerFeatures `json:"result"`
}

// ServerPeersResponse models the structure (or lack thereof) of a `server.peers.subscribe` response
type ServerPeersResponse struct {
ID int `json:"id"`
Result []interface{} `json:"result"`
}

// ListUnspentResponse models a `blockchain.scripthash.listunspent` response.
type ListUnspentResponse struct {
ID int `json:"id"`
Result []UnspentRef `json:"result"`
}

// GetTransactionResponse models the structure of a `blockchain.transaction.get` response.
type GetTransactionResponse struct {
ID int `json:"id"`
Result string `json:"result"`
}

// BroadcastResponse models the structure of a `blockchain.transaction.broadcast` response.
type BroadcastResponse struct {
ID int `json:"id"`
Expand All @@ -75,6 +93,17 @@ type UnspentRef struct {
Height int `json:"height"`
}

// ServerFeatures contains the relevant information from `ServerFeatures` results.
type ServerFeatures struct {
ID int `json:"id"`
GenesisHash string `json:"genesis_hash"`
HashFunction string `json:"hash_function"`
ServerVersion string `json:"server_version"`
ProcotolMin string `json:"protocol_min"`
ProtocolMax string `json:"protocol_max"`
Pruning int `json:"pruning"`
}

// Param is a convenience type that models an item in the `Params` array of an Request.
type Param = interface{}

Expand Down Expand Up @@ -157,6 +186,62 @@ func (c *Client) ServerVersion() ([]string, error) {
return response.Result, nil
}

// ServerFeatures calls the `server.features` method and returns the relevant part of the result.
func (c *Client) ServerFeatures() (*ServerFeatures, error) {
request := Request{
Method: "server.features",
Params: []Param{},
}

var response ServerFeaturesResponse

err := c.call(&request, &response)
if err != nil {
return nil, c.log.Errorf("ServerFeatures failed: %w", err)
}

return &response.Result, nil
}

// ServerPeers calls the `server.peers.subscribe` method and returns a list of server addresses.
func (c *Client) ServerPeers() ([]string, error) {
res, err := c.rawServerPeers()
if err != nil {
return nil, err // note that, besides I/O errors, some servers close the socket on this request
}

var peers []string

for _, entry := range res {
// Get ready for some hot casting action. Not for the faint of heart.
addr := entry.([]interface{})[1].(string)
port := entry.([]interface{})[2].([]interface{})[1].(string)[1:]

peers = append(peers, addr+":"+port)
}

return peers, nil
}

// rawServerPeers calls the `server.peers.subscribe` method and returns this monstrosity:
// [ "<ip>", "<domain>", ["<version>", "s<SSL port>", "t<TLS port>"] ]
// Ports can be in any order, or absent if the protocol is not supported
func (c *Client) rawServerPeers() ([]interface{}, error) {
request := Request{
Method: "server.peers.subscribe",
Params: []Param{},
}

var response ServerPeersResponse

err := c.call(&request, &response)
if err != nil {
return nil, c.log.Errorf("rawServerPeers failed: %w", err)
}

return response.Result, nil
}

// Broadcast calls the `blockchain.transaction.broadcast` endpoint and returns the transaction hash.
func (c *Client) Broadcast(rawTx string) (string, error) {
request := Request{
Expand All @@ -174,6 +259,23 @@ func (c *Client) Broadcast(rawTx string) (string, error) {
return response.Result, nil
}

// GetTransaction calls the `blockchain.transaction.get` endpoint and returns the transaction hex.
func (c *Client) GetTransaction(txID string) (string, error) {
request := Request{
Method: "blockchain.transaction.get",
Params: []Param{txID},
}

var response GetTransactionResponse

err := c.call(&request, &response)
if err != nil {
return "", c.log.Errorf("GetTransaction failed: %w", err)
}

return response.Result, nil
}

// ListUnspent calls `blockchain.scripthash.listunspent` and returns the UTXO results.
func (c *Client) ListUnspent(indexHash string) ([]UnspentRef, error) {
request := Request{
Expand Down
Loading

0 comments on commit 58d843a

Please sign in to comment.