Browse Source

Require confirmation before sending

tags/v1.0.0
codesoap 8 months ago
parent
commit
7d5311ca3c
5 changed files with 61 additions and 13 deletions
  1. 13
    6
      README.md
  2. 1
    0
      go.mod
  3. 1
    0
      go.sum
  4. 17
    4
      main.go
  5. 29
    3
      send.go

+ 13
- 6
README.md View File

@@ -43,8 +43,11 @@ $ # decentralized.
$ pass nano | atto representative nano_1jr699mk1fi6mxy1y76fmuyf3dgms8s5pzcsge5cyt1az93x4n18uxjenx93
Creating change block... done

$ # Careful with the send subcommand: No confirmation is required!
$ pass nano | atto send 0.1 nano_11zdqnjpisos53uighoaw95satm4ptdruck7xujbjcs44pbkkbw1h3zomns5
$ # To avoid accidental loss of funds, the send command is interactive,
$ # unless the -y flag is given:
$ atto send 0.1 nano_11zdqnjpisos53uighoaw95satm4ptdruck7xujbjcs44pbkkbw1h3zomns5
Seed: D420296F5FEF486175FAA8F649DED00A5B0A096DB8D03972937542C51A7F296C
Send 0.001 NANO to nano_3igsu9wop4zktr9ups8gct6zzr73nxr9yuetui6qj6x4xec9uazyr9zoaz4g? [y/N]: y
Creating send block... done

$ atto -h
@@ -53,16 +56,20 @@ Usage:
atto [-a ACCOUNT_INDEX] a[ddress]
atto [-a ACCOUNT_INDEX] b[alance]
atto [-a ACCOUNT_INDEX] r[epresentative] REPRESENTATIVE
atto [-a ACCOUNT_INDEX] s[end] AMOUNT RECEIVER
atto [-a ACCOUNT_INDEX] [-y] s[end] AMOUNT RECEIVER

The new subcommand generates a new seed, which can later be used with
the other subcommands.

The address, balance, representative and send subcommands will expect
a seed as as the first line of their standard input. Showing the first
address of a newly generated key could work like this:
The address, balance and representative subcommands expect a seed as as
the first line of their standard input. Showing the first address of a
newly generated key could work like this:
atto new | tee seed.txt | atto address

The send subcommand also expects a seed as the first line of input, if
the -y flag is given. If the -y flag is not given, the send subcommand
interactively asks for the seed and confirmation.

The address subcommand displays addresses for a seed, the balance
subcommand receives pending sends and shows the balance of an account,
the representative subcommand changes the account's representative and

+ 1
- 0
go.mod View File

@@ -5,4 +5,5 @@ go 1.15
require (
filippo.io/edwards25519 v1.0.0-beta.3
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
)

+ 1
- 0
go.sum View File

@@ -5,6 +5,7 @@ golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 17
- 4
main.go View File

@@ -4,6 +4,8 @@ import (
"flag"
"fmt"
"os"

"golang.org/x/term"
)

var usage = `Usage:
@@ -11,16 +13,20 @@ var usage = `Usage:
atto [-a ACCOUNT_INDEX] a[ddress]
atto [-a ACCOUNT_INDEX] b[alance]
atto [-a ACCOUNT_INDEX] r[epresentative] REPRESENTATIVE
atto [-a ACCOUNT_INDEX] s[end] AMOUNT RECEIVER
atto [-a ACCOUNT_INDEX] [-y] s[end] AMOUNT RECEIVER

The new subcommand generates a new seed, which can later be used with
the other subcommands.

The address, balance, representative and send subcommands will expect
a seed as as the first line of their standard input. Showing the first
address of a newly generated key could work like this:
The address, balance and representative subcommands expect a seed as as
the first line of their standard input. Showing the first address of a
newly generated key could work like this:
atto new | tee seed.txt | atto address

The send subcommand also expects a seed as the first line of input, if
the -y flag is given. If the -y flag is not given, the send subcommand
interactively asks for the seed and confirmation.

The address subcommand displays addresses for a seed, the balance
subcommand receives pending sends and shows the balance of an account,
the representative subcommand changes the account's representative and
@@ -32,10 +38,12 @@ the same seed. By default the account with index 0 is chosen.
`

var accountIndexFlag uint
var yFlag bool

func init() {
flag.Usage = func() { fmt.Fprint(os.Stderr, usage) }
flag.UintVar(&accountIndexFlag, "a", 0, "")
flag.BoolVar(&yFlag, "y", false, "")
flag.Parse()
if flag.NArg() < 1 {
flag.Usage()
@@ -52,6 +60,11 @@ func init() {
case "r":
ok = flag.NArg() == 2
case "s":
if !yFlag && !term.IsTerminal(int(os.Stdin.Fd())) {
msg := "Error: input is not a terminal. Did you forget to use the -y flag?"
fmt.Fprintln(os.Stderr, msg)
os.Exit(1)
}
ok = flag.NArg() == 3
}
if !ok {

+ 29
- 3
send.go View File

@@ -7,10 +7,14 @@ import (
"os"
"regexp"
"strings"

"golang.org/x/term"
)

func sendFunds() error {
seed, err := getSeed()
amount := flag.Arg(1)
recipient := flag.Arg(2)
seed, err := getSeedForSending(amount, recipient)
if err != nil {
return err
}
@@ -26,8 +30,6 @@ func sendFunds() error {
if info.Frontier == "0000000000000000000000000000000000000000000000000000000000000000" {
return fmt.Errorf("account has not yet been opened")
}
amount := flag.Arg(1)
recipient := flag.Arg(2)
fmt.Fprintf(os.Stderr, "Creating send block... ")
err = sendFundsToAccount(info, amount, recipient, privateKey)
if err != nil {
@@ -37,6 +39,30 @@ func sendFunds() error {
return nil
}

func getSeedForSending(amount, recipient string) (*big.Int, error) {
if yFlag {
return getSeed()
}
fmt.Print("Seed: ")
seedBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println("")
if err != nil {
return nil, err
}
seed, ok := big.NewInt(0).SetString(strings.TrimSpace(string(seedBytes)), 16)
if !ok {
return nil, fmt.Errorf("could not parse seed")
}
fmt.Printf("Send %s NANO to %s? [y/N]: ", amount, recipient)
var confirmation string
fmt.Scanln(&confirmation)
if confirmation != "y" && confirmation != "Y" {
fmt.Fprintln(os.Stderr, "Send aborted.")
os.Exit(0)
}
return seed, nil
}

func sendFundsToAccount(info accountInfo, amount, recipient string, privateKey *big.Int) error {
address, err := getAddress(privateKey)
if err != nil {

Loading…
Cancel
Save