Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
574 changes: 574 additions & 0 deletions cmd/dero-wallet-cli/anonymity.go

Large diffs are not rendered by default.

556 changes: 556 additions & 0 deletions cmd/dero-wallet-cli/anonymity_test.go

Large diffs are not rendered by default.

70 changes: 50 additions & 20 deletions cmd/dero-wallet-cli/easymenu_post_open.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,24 @@ func display_easymenu_post_open_command(l *readline.Instance) {
io.WriteString(w, "\n")
} else { // hide some commands, if view only wallet
io.WriteString(w, "\t\033[1m4\033[0m\tDisplay wallet pool\n")
io.WriteString(w, "\t\033[1m5\033[0m\tTransfer (send DERO) to Another Wallet\n")
io.WriteString(w, "\t\033[1m5\033[0m\tTransfer (send DERO) to Another Wallet"+transferLabelSuffix()+"\n")
io.WriteString(w, "\t\033[1m6\033[0m\tToken transfer to another wallet\n")
io.WriteString(w, "\t\033[1m7\033[0m\tAdvanced Privacy Options (extra sender cover)\n")
io.WriteString(w, "\n")
}

io.WriteString(w, "\t\033[1m7\033[0m\tChange wallet password\n")
io.WriteString(w, "\t\033[1m8\033[0m\tClose Wallet\n")
io.WriteString(w, "\t\033[1m8\033[0m\tChange wallet password\n")
io.WriteString(w, "\t\033[1m10\033[0m\tClose Wallet\n")
if wallet.IsRegistered() {
io.WriteString(w, "\t\033[1m12\033[0m\tTransfer all balance (send DERO) To Another Wallet\n")
io.WriteString(w, "\t\033[1m13\033[0m\tShow transaction history\n")
io.WriteString(w, "\t\033[1m14\033[0m\tRescan transaction history\n")
io.WriteString(w, "\t\033[1m15\033[0m\tExport all transaction history in json format\n")
io.WriteString(w, "\t\033[1m11\033[0m\tTransfer all balance (send DERO) To Another Wallet\n")
io.WriteString(w, "\t\033[1m12\033[0m\tShow transaction history\n")
io.WriteString(w, "\t\033[1m13\033[0m\tRescan transaction history\n")
io.WriteString(w, "\t\033[1m14\033[0m\tExport all transaction history in json format\n")
if xswd_server == nil {
io.WriteString(w, "\t\033[1m16\033[0m\tStart XSWD Server\n")
io.WriteString(w, "\t\033[1m15\033[0m\tStart XSWD Server\n")
} else {
io.WriteString(w, "\t\033[1m16\033[0m\tStop XSWD Server\n")
io.WriteString(w, "\t\033[1m17\033[0m\tList XSWD Applications\n")
io.WriteString(w, "\t\033[1m15\033[0m\tStop XSWD Server\n")
io.WriteString(w, "\t\033[1m16\033[0m\tList XSWD Applications\n")
}
}

Expand Down Expand Up @@ -216,8 +217,15 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
break // invalid amount provided, bail out
}

opts, _, members := applySessionPrivacy(a.String())

logger.Info("Review token transfer",
"scid", scid.String(),
"to", a.String(),
"amount", globals.FormatMoney(amount_to_transfer),
"ringsize", wallet.GetRingSize())
if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") {
tx, err := wallet.TransferPayload0([]rpc.Transfer{{SCID: scid, Amount: amount_to_transfer, Destination: a.String()}}, 0, false, rpc.Arguments{}, 0, false) // empty SCDATA
tx, err := wallet.TransferPayload0WithOptions([]rpc.Transfer{{SCID: scid, Amount: amount_to_transfer, Destination: a.String()}}, 0, false, rpc.Arguments{}, 0, false, opts) // empty SCDATA

if err != nil {
logger.Error(err, "Error while building Transaction")
Expand All @@ -228,6 +236,10 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
break
}
logger.Info("Dispatched tx", "txid", tx.GetHash().String())
// post-send count is read off the BUILT ring, not the prompt-time request:
// Strict:false drops unregistered curated decoys silently (O8).
reportAttribution(l, opts, curatedDecoysInTx(tx, members, a.String()), "landed in ring") // truthful post-send confirmation (O2/O9: count only the recipient-delivering payload's ring)
resetTransferBuildToDefaults() // set-first-then-fire: build settings do not persist into the next send
}

case "5":
Expand Down Expand Up @@ -378,11 +390,17 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
return
}

opts, _, members := applySessionPrivacy(a.String())

logger.Info("Review transfer",
"to", a.String(),
"amount", globals.FormatMoney(amount_to_transfer),
"ringsize", wallet.GetRingSize())
if ConfirmYesNoDefaultNo(l, "Confirm Transaction (y/N)") {

//src_port := uint64(0xffffffffffffffff)

tx, err := wallet.TransferPayload0([]rpc.Transfer{{Amount: amount_to_transfer, Destination: a.String(), Payload_RPC: arguments}}, 0, false, rpc.Arguments{}, 0, false) // empty SCDATA
tx, err := wallet.TransferPayload0WithOptions([]rpc.Transfer{{Amount: amount_to_transfer, Destination: a.String(), Payload_RPC: arguments}}, 0, false, rpc.Arguments{}, 0, false, opts) // empty SCDATA

if err != nil {
logger.Error(err, "Error while building Transaction")
Expand All @@ -394,10 +412,14 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
break
}
logger.Info("Dispatched tx", "txid", tx.GetHash().String())
// post-send count is read off the BUILT ring, not the prompt-time request:
// Strict:false drops unregistered curated decoys silently (O8).
reportAttribution(l, opts, curatedDecoysInTx(tx, members, a.String()), "landed in ring") // truthful post-send confirmation (O2/O9: count only the recipient-delivering payload's ring)
resetTransferBuildToDefaults() // set-first-then-fire: build settings do not persist into the next send
//fmt.Printf("queued tx err %s\n")
}

case "12":
case "11": // transfer all balance
if !valid_registration_or_display_error(l, wallet) {
break
}
Expand All @@ -406,6 +428,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
break
}

// note: transfer-all + curated TransferOptions is unsupported by the engine; do not wire opts here.
logger.Error(err, "Not supported ")

/*
Expand Down Expand Up @@ -437,7 +460,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce

//PressAnyKey(l, wallet) // wait for a key press

case "7": // change password
case "8": // change password
if ConfirmYesNoDefaultNo(l, "Change wallet password (y/N)") &&
ValidateCurrentPassword(l, wallet) {

Expand All @@ -450,7 +473,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
}
}

case "8": // close and discard user key
case "10": // close and discard user key

wallet.Close_Encrypted_Wallet()
prompt_mutex.Lock()
Expand All @@ -472,14 +495,14 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
fmt.Fprintf(l.Stderr(), color_yellow+"Wallet closed"+color_white)
fmt.Fprintf(l.Stderr(), color_yellow+"Exiting"+color_white)

case "13":
case "12": // show transaction history
var zeroscid crypto.Hash
show_transfers(l, wallet, zeroscid, 100)

case "14":
case "13": // rescan transaction history
logger.Info("Rescanning wallet history")
rescan_bc(wallet)
case "15":
case "14": // export transaction history as json
if !ValidateCurrentPassword(l, wallet) {
logger.Error(fmt.Errorf("Invalid password"), "")
break
Expand Down Expand Up @@ -507,7 +530,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
logger.Info("successfully exported history", "file", filename)
}
}
case "16": // start/stop xswd server
case "15": // start/stop xswd server
if xswd_server != nil {
xswd_server.Stop()
xswd_server = nil
Expand All @@ -526,7 +549,7 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
if !xswd_server.IsRunning() {
xswd_server = nil
}
case "17":
case "16": // list xswd applications
if xswd_server == nil {
logger.Error(nil, "XSWD server is not running")
break
Expand All @@ -544,6 +567,13 @@ func handle_easymenu_post_open_command(l *readline.Instance, line string) (proce
}
}

case "7": // Advanced Privacy Options — opt-in extra sender cover
if !wallet.IsRegistered() {
logger.Error(nil, "Register the account first")
break
}
handleTransactionBuildMenu(l)

default:
processed = false // just loop

Expand Down
22 changes: 22 additions & 0 deletions cmd/dero-wallet-cli/easymenu_pre_open.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package main

import "io"
import "os"
import "fmt"
import "time"
import "strconv"
Expand Down Expand Up @@ -249,6 +250,27 @@ func common_processing(wallet *walletapi.Wallet_Disk) {

wallet.SetNetwork(!globals.Arguments["--testnet"].(bool))

// capture the wallet's ring size as the post-send reset target ("set first, then fire":
// a one-off per-tx ring size set in the Transaction Build Options menu reverts here).
default_ringsize = wallet.GetRingSize()

if globals.Arguments["--anonymous"] != nil && globals.Arguments["--anonymous"].(bool) {
attribution_mode = walletapi.AttributionAnonymous
// console-only (NOT the on-disk log): never persist anonymize-intent to disk (O3).
// --anonymous only seeds ANONYMOUS; SELF is never reachable from a flag (it is a
// deliberate, warned, in-session choice only).
fmt.Fprintf(os.Stderr, "Extra sender privacy ON for this session (configure in the Transaction Build Options menu)\n")
}
if globals.Arguments["--decoys"] != nil && globals.Arguments["--decoys"].(string) != "" {
for _, d := range strings.Split(globals.Arguments["--decoys"].(string), ",") {
if d = strings.TrimSpace(d); d != "" {
decoys_default = append(decoys_default, d)
}
}
// console-only (NOT the on-disk log): never persist the curated decoy set to disk (O3).
fmt.Fprintf(os.Stderr, "Loaded preferred decoys: %d\n", len(decoys_default))
}

// start rpc server if requested
if globals.Arguments["--rpc-server"].(bool) == true {
rpc_address := "127.0.0.1:" + fmt.Sprintf("%d", config.Mainnet.Wallet_RPC_Default_Port)
Expand Down
2 changes: 2 additions & 0 deletions cmd/dero-wallet-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ Usage:
--allow-rpc-password-change RPC server will change password if you send "Pass" header with new password
--scan-top-n-blocks=<100000> Only scan top N blocks
--save-every-x-seconds=<300> Save wallet every x seconds
--anonymous Turn on extra sender privacy for this session (see menu: Advanced Privacy Options). DERO already hides amount/sender/receiver by default; this adds an extra layer.
--decoys=<csv> Comma-separated base addresses you choose as ring decoys for the session (bad entries skipped)
`
var menu_mode bool = true // default display menu mode
// var account_valid bool = false // if an account has been opened, do not allow to create new account in this session
Expand Down
25 changes: 11 additions & 14 deletions cmd/dero-wallet-cli/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,19 +450,9 @@ func handle_set_command(l *readline.Instance, line string) {
help := false
switch command {
case "help":
case "ringsize":
if len(line_parts) != 3 {
logger.Info("Wrong number of arguments, see help eg", "")
help = true
break
}
s, err := strconv.ParseUint(line_parts[2], 10, 64)
if err != nil {
logger.Error(err, "Error parsing ringsize")
return
}
wallet.SetRingSize(int(s))
logger.Info("New Ring size", "ringsize", wallet.GetRingSize())
// ring size is configured in the Transaction Build Options menu (option 7), the
// single consistent place to define how a transfer's ring is built. The old
// `set ringsize` command was removed to avoid a second, colliding config path.

case "priority":
if len(line_parts) != 3 {
Expand All @@ -482,6 +472,10 @@ func handle_set_command(l *readline.Instance, line string) {
language := choose_seed_language(l)
logger.Info("Setting seed language", "language", wallet.SetSeedLanguage(language))

// extra sender privacy is configured in the Transaction Build Options menu (option 7),
// the single consistent place to define a transfer's ring build. The old
// `set anonymous` command was removed to avoid a second, colliding config path.

default:
help = true
}
Expand All @@ -490,13 +484,15 @@ func handle_set_command(l *readline.Instance, line string) {

fmt.Fprintf(l.Stderr(), color_extra_white+"Current settings"+color_extra_white+"\n")
fmt.Fprintf(l.Stderr(), color_normal+"Seed Language: "+color_extra_white+"%s\t"+color_normal+"eg. "+color_extra_white+"set seed language\n"+color_normal, wallet.GetSeedLanguage())
fmt.Fprintf(l.Stderr(), color_normal+"Ringsize: "+color_extra_white+"%d\t"+color_normal+"eg. "+color_extra_white+"set ringsize 16\n"+color_normal, wallet.GetRingSize())
fmt.Fprintf(l.Stderr(), color_normal+"Ring size: "+color_extra_white+"%d\t"+color_normal+"set in the "+color_extra_white+"Transaction Build Options"+color_normal+" menu (option 7)\n"+color_normal, wallet.GetRingSize())
fmt.Fprintf(l.Stderr(), color_normal+"Save Every : "+color_extra_white+"%s \t"+color_normal+"eg. "+color_extra_white+"default value:0 (set using command line)\n"+color_normal, wallet.SetSaveDuration(-1))
fmt.Fprintf(l.Stderr(), color_normal+"Track Recent Blocks : "+color_extra_white+"%d \t"+color_normal+"eg. "+color_extra_white+"default value:0 means track all blocks (set using command line)\n"+color_normal, wallet.SetTrackRecentBlocks(-1))

fmt.Fprintf(l.Stderr(), color_normal+"Priority: "+color_extra_white+"%0.2f\t"+color_normal+"eg. "+color_extra_white+"set priority 4.0\t"+color_normal+"Transaction priority on DERO network \n", wallet.GetFeeMultiplier())
fmt.Fprintf(l.Stderr(), "\t\tMinimum priority is 1.00. High priority = high fees\n")

fmt.Fprintf(l.Stderr(), color_normal+"Sender attribution: "+color_extra_white+"%s\t"+color_normal+"set in the "+color_extra_white+"Transaction Build Options"+color_normal+" menu (option 7). ANONYMOUS needs ring size >= 4.\n", attributionModeLabel(attribution_mode))

}
}

Expand Down Expand Up @@ -1034,6 +1030,7 @@ var completer = readline.NewPrefixCompleter(
readline.PcItem("mixin"),
readline.PcItem("seed"),
readline.PcItem("priority"),
readline.PcItem("anonymous"),
),
readline.PcItem("show_transfers"),
readline.PcItem("spendkey"),
Expand Down
Loading