Skip to content

Commit 555737b

Browse files
feat: format known addresses for consensus txes
test: cover consensusaccounts.Undelegate PrettyPrint
1 parent c54a9ef commit 555737b

5 files changed

Lines changed: 135 additions & 9 deletions

File tree

client-sdk/go/modules/consensusaccounts/types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ type Undelegate struct {
6565
}
6666

6767
// PrettyPrint writes a pretty-printed representation of the transaction to the given writer.
68-
func (ud *Undelegate) PrettyPrint(_ context.Context, prefix string, w io.Writer) {
69-
_, _ = fmt.Fprintf(w, "%sFrom: %s\n", prefix, ud.From)
68+
func (ud *Undelegate) PrettyPrint(ctx context.Context, prefix string, w io.Writer) {
69+
_, _ = fmt.Fprintf(w, "%sFrom: %s\n", prefix, types.FormatNamedAddress(ctx, ud.From))
7070
_, _ = fmt.Fprintf(w, "%sShares: %s\n", prefix, ud.Shares)
7171
}
7272

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package consensusaccounts
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/hex"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/types"
12+
13+
"github.com/oasisprotocol/oasis-core/go/common/quantity"
14+
)
15+
16+
func TestUndelegatePrettyPrintFromNamedAddress(t *testing.T) {
17+
require := require.New(t)
18+
19+
ethHex := "0x60a6321ea71d37102dbf923aae2e08d005c4e403"
20+
ethBytes, err := hex.DecodeString(ethHex[2:])
21+
require.NoError(err)
22+
23+
addr := types.NewAddressFromEth(ethBytes)
24+
native := addr.String()
25+
26+
shares := *quantity.NewFromUint64(1234)
27+
28+
ctx := context.Background()
29+
ctx = context.WithValue(ctx, types.ContextKeyAccountNames, types.AccountNames{native: "my"})
30+
ctx = context.WithValue(ctx, types.ContextKeyAccountEthMap, map[string]string{native: ethHex})
31+
32+
ud := Undelegate{
33+
From: addr,
34+
Shares: shares,
35+
}
36+
37+
var buf bytes.Buffer
38+
ud.PrettyPrint(ctx, "", &buf)
39+
40+
require.Equal("From: my ("+ethHex+")\nShares: "+shares.String()+"\n", buf.String())
41+
}

client-sdk/go/types/address.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ type contextKey string
4444
// ContextKeyAccountNames is the key to retrieve the public key to account name map from context.
4545
const ContextKeyAccountNames = contextKey("runtime/account-names")
4646

47+
// ContextKeyAccountEthMap is the key to retrieve a mapping from native (Bech32)
48+
// Oasis addresses to their corresponding Ethereum hex addresses.
49+
const ContextKeyAccountEthMap = contextKey("runtime/account-eth-map")
50+
4751
// AccountNames maps public key or address to user-defined account name for pretty printing.
4852
type AccountNames map[string]string
4953

client-sdk/go/types/token.go

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,57 @@ func NewBaseUnits(amount quantity.Quantity, denomination Denomination) BaseUnits
8787
}
8888
}
8989

90+
// FormatNamedAddress returns a human-friendly representation of an address.
91+
//
92+
// It prints the name (if known) followed by the preferred form of the address
93+
// in parentheses. If an Ethereum hex address mapping is provided for the native
94+
// address, it is used; otherwise the native Bech32 address is used.
95+
func FormatNamedAddress(ctx context.Context, addr Address) string {
96+
var (
97+
names AccountNames
98+
ethMap map[string]string
99+
)
100+
if v, ok := ctx.Value(ContextKeyAccountNames).(AccountNames); ok {
101+
names = v
102+
}
103+
if v, ok := ctx.Value(ContextKeyAccountEthMap).(map[string]string); ok {
104+
ethMap = v
105+
}
106+
return FormatNamedAddressWith(names, ethMap, addr)
107+
}
108+
109+
// FormatNamedAddressWith is a pure helper for address formatting to make testing easier.
110+
func FormatNamedAddressWith(names AccountNames, ethMap map[string]string, addr Address) string {
111+
native := addr.String()
112+
113+
name := ""
114+
if names != nil {
115+
name = names[native]
116+
}
117+
if name == "" {
118+
return native
119+
}
120+
121+
preferred := native
122+
if ethMap != nil {
123+
if hex := ethMap[native]; hex != "" {
124+
preferred = hex
125+
}
126+
}
127+
128+
// Guard against redundant "name (name)" output.
129+
if name == preferred {
130+
return preferred
131+
}
132+
133+
return fmt.Sprintf("%s (%s)", name, preferred)
134+
}
135+
90136
// PrettyPrintToAmount is a helper for printing To-Amount transaction bodies (e.g. transfer, deposit, withdraw).
91137
func PrettyPrintToAmount(ctx context.Context, prefix string, w io.Writer, to *Address, amount BaseUnits) {
92138
toStr := "Self"
93139
if to != nil {
94-
toStr = to.String()
95-
an, ok := ctx.Value(ContextKeyAccountNames).(AccountNames)
96-
if ok {
97-
if name, ok := an[to.String()]; ok {
98-
toStr = fmt.Sprintf("%s (%s)", name, to)
99-
}
100-
}
140+
toStr = FormatNamedAddress(ctx, *to)
101141
}
102142
_, _ = fmt.Fprintf(w, "%sTo: %s\n", prefix, toStr)
103143
_, _ = fmt.Fprintf(w, "%sAmount: ", prefix)

client-sdk/go/types/token_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,44 @@ func TestPrettyPrintToAmount(t *testing.T) {
8282
PrettyPrintToAmount(ctx, "", &buf, &to, amt)
8383
require.Equal("To: test:dave (oasis1qrk58a6j2qn065m6p06jgjyt032f7qucy5wqeqpt)\nAmount: <error: ParaTime information not available>\n", buf.String())
8484
}
85+
86+
func TestFormatNamedAddressWith(t *testing.T) {
87+
require := require.New(t)
88+
89+
ethHex := "0x60a6321ea71d37102dbf923aae2e08d005c4e403"
90+
ethBytes, err := hex.DecodeString(ethHex[2:])
91+
require.NoError(err)
92+
93+
addr := NewAddressFromEth(ethBytes)
94+
native := addr.String()
95+
96+
t.Run("unknown returns native", func(_ *testing.T) {
97+
require.Equal(native, FormatNamedAddressWith(nil, nil, addr))
98+
require.Equal(native, FormatNamedAddressWith(AccountNames{}, map[string]string{}, addr))
99+
})
100+
101+
t.Run("native fallback when eth unknown", func(_ *testing.T) {
102+
names := AccountNames{native: "my"}
103+
require.Equal("my ("+native+")", FormatNamedAddressWith(names, nil, addr))
104+
require.Equal("my ("+native+")", FormatNamedAddressWith(names, map[string]string{}, addr))
105+
require.Equal("my ("+native+")", FormatNamedAddressWith(names, map[string]string{native: ""}, addr))
106+
})
107+
108+
t.Run("eth preferred when known", func(_ *testing.T) {
109+
names := AccountNames{native: "my"}
110+
ethMap := map[string]string{native: ethHex}
111+
require.Equal("my ("+ethHex+")", FormatNamedAddressWith(names, ethMap, addr))
112+
})
113+
114+
t.Run("name equals preferred yields preferred", func(_ *testing.T) {
115+
names := AccountNames{native: native}
116+
require.Equal(native, FormatNamedAddressWith(names, nil, addr))
117+
})
118+
119+
t.Run("ctx wrapper reads maps", func(_ *testing.T) {
120+
ctx := context.Background()
121+
ctx = context.WithValue(ctx, ContextKeyAccountNames, AccountNames{native: "my"})
122+
ctx = context.WithValue(ctx, ContextKeyAccountEthMap, map[string]string{native: ethHex})
123+
require.Equal("my ("+ethHex+")", FormatNamedAddress(ctx, addr))
124+
})
125+
}

0 commit comments

Comments
 (0)