Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .changelog/6453.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/staking/api: Format known addresses in staking transaction PrettyPrint
12 changes: 6 additions & 6 deletions go/staking/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ type Transfer struct {
// PrettyPrint writes a pretty-printed representation of Transfer to the given
// writer.
func (t Transfer) PrettyPrint(ctx context.Context, prefix string, w io.Writer) {
fmt.Fprintf(w, "%sTo: %s\n", prefix, t.To)
fmt.Fprintf(w, "%sTo: %s\n", prefix, FormatAddress(ctx, t.To))

fmt.Fprintf(w, "%sAmount: ", prefix)
token.PrettyPrintAmount(ctx, t.Amount, w)
Expand Down Expand Up @@ -477,7 +477,7 @@ type Escrow struct {
// PrettyPrint writes a pretty-printed representation of Escrow to the given
// writer.
func (e Escrow) PrettyPrint(ctx context.Context, prefix string, w io.Writer) {
fmt.Fprintf(w, "%sTo: %s\n", prefix, e.Account)
fmt.Fprintf(w, "%sTo: %s\n", prefix, FormatAddress(ctx, e.Account))

fmt.Fprintf(w, "%sAmount: ", prefix)
token.PrettyPrintAmount(ctx, e.Amount, w)
Expand All @@ -503,8 +503,8 @@ type ReclaimEscrow struct {

// PrettyPrint writes a pretty-printed representation of ReclaimEscrow to the
// given writer.
func (re ReclaimEscrow) PrettyPrint(_ context.Context, prefix string, w io.Writer) {
fmt.Fprintf(w, "%sFrom: %s\n", prefix, re.Account)
func (re ReclaimEscrow) PrettyPrint(ctx context.Context, prefix string, w io.Writer) {
fmt.Fprintf(w, "%sFrom: %s\n", prefix, FormatAddress(ctx, re.Account))

fmt.Fprintf(w, "%sShares: %s\n", prefix, re.Shares)
}
Expand Down Expand Up @@ -552,7 +552,7 @@ type Allow struct {

// PrettyPrint writes a pretty-printed representation of Allow to the given writer.
func (aw Allow) PrettyPrint(ctx context.Context, prefix string, w io.Writer) {
fmt.Fprintf(w, "%sBeneficiary: %s\n", prefix, aw.Beneficiary)
fmt.Fprintf(w, "%sBeneficiary: %s\n", prefix, FormatAddress(ctx, aw.Beneficiary))

sign := "+"
if aw.Negative {
Expand Down Expand Up @@ -582,7 +582,7 @@ type Withdraw struct {

// PrettyPrint writes a pretty-printed representation of Withdraw to the given writer.
func (wt Withdraw) PrettyPrint(ctx context.Context, prefix string, w io.Writer) {
fmt.Fprintf(w, "%sFrom: %s\n", prefix, wt.From)
fmt.Fprintf(w, "%sFrom: %s\n", prefix, FormatAddress(ctx, wt.From))

fmt.Fprintf(w, "%sAmount: ", prefix)
token.PrettyPrintAmount(ctx, wt.Amount, w)
Expand Down
37 changes: 37 additions & 0 deletions go/staking/api/prettyprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,43 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/quantity"
)

type contextKey string

// ContextKeyAccountNames is the key to retrieve native (Bech32) account names from context.
var ContextKeyAccountNames = contextKey("staking/account-names")

// AccountNames maps native (Bech32) addresses to user-defined account names for pretty printing.
type AccountNames map[string]string

// FormatAddress is like FormatAddressWith but reads names from ctx.
func FormatAddress(ctx context.Context, addr Address) string {
var names AccountNames
if v, ok := ctx.Value(ContextKeyAccountNames).(AccountNames); ok {
names = v
}

return FormatAddressWith(names, addr)
}

// FormatAddressWith formats a staking address for display.
//
// Output cases:
// - Named address: "name (oasis1...)"
// - Unknown address: "oasis1..."
func FormatAddressWith(names AccountNames, addr Address) string {
native := addr.String()
if names == nil {
return native
}

name := names[native]
if name == "" {
return native
}

return fmt.Sprintf("%s (%s)", name, native)
}

// PrettyPrintCommissionRatePercentage returns the string representing the
// commission rate (bound) in percentage for the given commission rate (bound)
// numerator.
Expand Down
125 changes: 125 additions & 0 deletions go/staking/api/prettyprint_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"bytes"
"context"
"testing"

Expand All @@ -10,6 +11,14 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/quantity"
)

func mustAddress(t *testing.T, raw string) Address {
t.Helper()

var addr Address
require.NoError(t, addr.UnmarshalText([]byte(raw)))
return addr
}

func TestPrettyPrintCommissionRatePercentage(t *testing.T) {
require := require.New(t)

Expand Down Expand Up @@ -54,3 +63,119 @@ func TestPrettyPrintCommissionScheduleIndexInfixes(t *testing.T) {
require.Equal(t.expectedEmptyInfix, emptyInfix, "obtained empty infix didn't match expected value")
}
}

func TestFormatAddressWith(t *testing.T) {
require := require.New(t)

addr := mustAddress(t, "oasis1qrydpazemvuwtnp3efm7vmfvg3tde044qg6cxwzx")
native := addr.String()

for _, tc := range []struct {
name string
names AccountNames
expected string
}{
{
name: "nil names",
names: nil,
expected: native,
},
{
name: "empty names",
names: AccountNames{},
expected: native,
},
{
name: "unknown name",
names: AccountNames{"oasis1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpkfh7w": "ignored"},
expected: native,
},
{
name: "named address",
names: AccountNames{native: "test:bob"},
expected: "test:bob (" + native + ")",
},
} {
t.Run(tc.name, func(t *testing.T) {
require.Equal(tc.expected, FormatAddressWith(tc.names, addr))
})
}
}

func TestFormatAddress(t *testing.T) {
require := require.New(t)

addr := mustAddress(t, "oasis1qrydpazemvuwtnp3efm7vmfvg3tde044qg6cxwzx")
native := addr.String()

t.Run("without context names", func(t *testing.T) {
require.Equal(native, FormatAddress(context.Background(), addr))
})

t.Run("with context names", func(t *testing.T) {
ctx := context.WithValue(context.Background(), ContextKeyAccountNames, AccountNames{
native: "test:bob",
})
require.Equal("test:bob ("+native+")", FormatAddress(ctx, addr))
})
}

func TestStakingTxPrettyPrintUsesNamedAddresses(t *testing.T) {
require := require.New(t)

addr := mustAddress(t, "oasis1qrydpazemvuwtnp3efm7vmfvg3tde044qg6cxwzx")
native := addr.String()
amt := *quantity.NewFromUint64(1)

ctx := context.WithValue(context.Background(), ContextKeyAccountNames, AccountNames{
native: "test:bob",
})

for _, tc := range []struct {
name string
pretty func(context.Context, *bytes.Buffer)
expected string
}{
{
name: "transfer to",
pretty: func(ctx context.Context, buf *bytes.Buffer) {
Transfer{To: addr, Amount: amt}.PrettyPrint(ctx, "", buf)
},
expected: "To: test:bob (" + native + ")",
},
{
name: "escrow account",
pretty: func(ctx context.Context, buf *bytes.Buffer) {
Escrow{Account: addr, Amount: amt}.PrettyPrint(ctx, "", buf)
},
expected: "To: test:bob (" + native + ")",
},
{
name: "reclaim escrow from",
pretty: func(ctx context.Context, buf *bytes.Buffer) {
ReclaimEscrow{Account: addr, Shares: amt}.PrettyPrint(ctx, "", buf)
},
expected: "From: test:bob (" + native + ")",
},
{
name: "allow beneficiary",
pretty: func(ctx context.Context, buf *bytes.Buffer) {
Allow{Beneficiary: addr, AmountChange: amt}.PrettyPrint(ctx, "", buf)
},
expected: "Beneficiary: test:bob (" + native + ")",
},
{
name: "withdraw from",
pretty: func(ctx context.Context, buf *bytes.Buffer) {
Withdraw{From: addr, Amount: amt}.PrettyPrint(ctx, "", buf)
},
expected: "From: test:bob (" + native + ")",
},
} {
t.Run(tc.name, func(t *testing.T) {
var buf bytes.Buffer
tc.pretty(ctx, &buf)
require.Contains(buf.String(), tc.expected)
})
}
}
Loading