Skip to content

Commit 7bd1a49

Browse files
authored
state: Make state.Keys JSON human readable (#1661)
Signed-off-by: nathan haim <[email protected]>
1 parent c592a1d commit 7bd1a49

File tree

2 files changed

+113
-20
lines changed

2 files changed

+113
-20
lines changed

state/keys.go

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ package state
66
import (
77
"encoding/hex"
88
"encoding/json"
9+
"errors"
910
"fmt"
11+
"strings"
1012

13+
"github.com/ava-labs/hypersdk/codec"
1114
"github.com/ava-labs/hypersdk/keys"
1215
)
1316

17+
var errInvalidHexadecimalString = errors.New("invalid hexadecimal string")
18+
1419
const (
1520
Read Permissions = 1
1621
Allocate = 1<<1 | Read
@@ -56,40 +61,66 @@ func (k Keys) ChunkSizes() ([]uint16, bool) {
5661
return chunks, true
5762
}
5863

59-
type permsJSON []string
60-
61-
type keysJSON struct {
62-
Perms [8]permsJSON
63-
}
64+
type keysJSON map[string]Permissions
6465

66+
// MarshalJSON marshals Keys as readable JSON.
67+
// Keys are hex encoded strings and permissions
68+
// are either valid named strings or unknown hex encoded strings.
6569
func (k Keys) MarshalJSON() ([]byte, error) {
66-
var keysJSON keysJSON
70+
kJSON := make(keysJSON)
6771
for key, perm := range k {
68-
keysJSON.Perms[perm] = append(keysJSON.Perms[perm], hex.EncodeToString([]byte(key)))
72+
hexKey, err := codec.Bytes(key).MarshalText()
73+
if err != nil {
74+
return nil, err
75+
}
76+
kJSON[string(hexKey)] = perm
6977
}
70-
return json.Marshal(keysJSON)
78+
return json.Marshal(kJSON)
7179
}
7280

81+
// UnmarshalJSON unmarshals readable JSON.
7382
func (k *Keys) UnmarshalJSON(b []byte) error {
7483
var keysJSON keysJSON
7584
if err := json.Unmarshal(b, &keysJSON); err != nil {
7685
return err
7786
}
78-
for perm, keyList := range keysJSON.Perms {
79-
if perm < int(None) || perm > int(All) {
80-
return fmt.Errorf("invalid permission encoded in json %d", perm)
87+
for hexKey, perm := range keysJSON {
88+
var key codec.Bytes
89+
err := key.UnmarshalText([]byte(hexKey))
90+
if err != nil {
91+
return err
8192
}
82-
for _, encodedKey := range keyList {
83-
key, err := hex.DecodeString(encodedKey)
84-
if err != nil {
85-
return err
86-
}
87-
(*k)[string(key)] = Permissions(perm)
93+
(*k)[string(key)] = perm
94+
}
95+
return nil
96+
}
97+
98+
func (p *Permissions) UnmarshalText(in []byte) error {
99+
switch str := strings.ToLower(string(in)); str {
100+
case "read":
101+
*p = Read
102+
case "write":
103+
*p = Write
104+
case "allocate":
105+
*p = Allocate
106+
case "all":
107+
*p = All
108+
case "none":
109+
*p = None
110+
default:
111+
res, err := hex.DecodeString(str)
112+
if err != nil || len(res) != 1 {
113+
return fmt.Errorf("permission %s: %w", str, errInvalidHexadecimalString)
88114
}
115+
*p = Permissions(res[0])
89116
}
90117
return nil
91118
}
92119

120+
func (p Permissions) MarshalText() ([]byte, error) {
121+
return []byte(p.String()), nil
122+
}
123+
93124
// Has returns true if [p] has all the permissions that are contained in require
94125
func (p Permissions) Has(require Permissions) bool {
95126
return require&^p == 0
@@ -108,6 +139,6 @@ func (p Permissions) String() string {
108139
case None:
109140
return "none"
110141
default:
111-
return "unknown"
142+
return hex.EncodeToString([]byte{byte(p)})
112143
}
113144
}

state/keys_test.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/binary"
99
"math/rand"
1010
"slices"
11+
"strconv"
1112
"testing"
1213

1314
"github.com/stretchr/testify/require"
@@ -135,7 +136,7 @@ func TestKeysMarshalingSimple(t *testing.T) {
135136
require.True(keys.Add("key1", Read))
136137
bytes, err := keys.MarshalJSON()
137138
require.NoError(err)
138-
require.Equal([]byte{0x7b, 0x22, 0x50, 0x65, 0x72, 0x6d, 0x73, 0x22, 0x3a, 0x5b, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x5b, 0x22, 0x36, 0x62, 0x36, 0x35, 0x37, 0x39, 0x33, 0x31, 0x22, 0x5d, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x5d, 0x7d}, bytes)
139+
require.Equal(`{"6b657931":"read"}`, string(bytes))
139140
keys = Keys{}
140141
require.NoError(keys.UnmarshalJSON(bytes))
141142
require.Len(keys, 1)
@@ -146,7 +147,7 @@ func TestKeysMarshalingSimple(t *testing.T) {
146147
require.True(keys.Add("key2", Read|Write))
147148
bytes, err = keys.MarshalJSON()
148149
require.NoError(err)
149-
require.Equal([]byte{0x7b, 0x22, 0x50, 0x65, 0x72, 0x6d, 0x73, 0x22, 0x3a, 0x5b, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x5b, 0x22, 0x36, 0x62, 0x36, 0x35, 0x37, 0x39, 0x33, 0x32, 0x22, 0x5d, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x5d, 0x7d}, bytes)
150+
require.Equal(`{"6b657932":"write"}`, string(bytes))
150151
keys = Keys{}
151152
require.NoError(keys.UnmarshalJSON(bytes))
152153
require.Len(keys, 1)
@@ -181,3 +182,64 @@ func TestKeysMarshalingFuzz(t *testing.T) {
181182
require.True(keys.compare(decodedKeys))
182183
}
183184
}
185+
186+
func TestNewPermissionFromString(t *testing.T) {
187+
tests := []struct {
188+
strPerm string
189+
perm Permissions
190+
expectedErr error
191+
}{
192+
{
193+
strPerm: "read",
194+
perm: Read,
195+
},
196+
{
197+
strPerm: "write",
198+
perm: Write,
199+
},
200+
{
201+
strPerm: "allocate",
202+
perm: Allocate,
203+
},
204+
{
205+
strPerm: "all",
206+
perm: All,
207+
},
208+
{
209+
strPerm: "none",
210+
perm: None,
211+
},
212+
{
213+
strPerm: "09",
214+
perm: Permissions(9),
215+
},
216+
{
217+
strPerm: "010A",
218+
expectedErr: errInvalidHexadecimalString,
219+
},
220+
}
221+
222+
for i, test := range tests {
223+
t.Run(strconv.Itoa(i), func(t *testing.T) {
224+
require := require.New(t)
225+
var perm Permissions
226+
err := perm.UnmarshalText([]byte(test.strPerm))
227+
if test.expectedErr != nil {
228+
require.ErrorIs(err, test.expectedErr)
229+
} else {
230+
require.NoError(err)
231+
require.Equal(test.perm, perm)
232+
}
233+
})
234+
}
235+
}
236+
237+
func TestPermissionStringer(t *testing.T) {
238+
require := require.New(t)
239+
require.Equal("read", Read.String())
240+
require.Equal("write", Write.String())
241+
require.Equal("allocate", Allocate.String())
242+
require.Equal("all", All.String())
243+
require.Equal("none", None.String())
244+
require.Equal("09", Permissions(9).String())
245+
}

0 commit comments

Comments
 (0)