Skip to content

Commit a42709a

Browse files
committed
b64 encode the BoxRef names in a txn when marshaling to json
1 parent b22172d commit a42709a

File tree

5 files changed

+348
-0
lines changed

5 files changed

+348
-0
lines changed

data/basics/fields_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ func TestBlockFields(t *testing.T) {
172172
typePath{}.addField("Payset").addValue().addField("SignedTxnWithAD").addField("ApplyData").addField("EvalDelta").addField("LocalDeltas").addValue().addMapKey(),
173173
typePath{}.addField("Payset").addValue().addField("SignedTxnWithAD").addField("ApplyData").addField("EvalDelta").addField("LocalDeltas").addValue().addValue().addField("Bytes"),
174174
typePath{}.addField("Payset").addValue().addField("SignedTxnWithAD").addField("ApplyData").addField("EvalDelta").addField("Logs").addValue(),
175+
176+
// This exception is for a NEW string field. See transactions/json.go to see how we get b64 output.
177+
typePath{}.addField("Payset").addValue().addField("SignedTxnWithAD").addField("SignedTxn").addField("Txn").addField("ApplicationCallTxnFields").addField("Boxes").addValue().addField("Name"),
175178
}
176179

177180
seen := make(map[reflect.Type]bool)

data/transactions/json.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (C) 2019-2022 Algorand, Inc.
2+
// This file is part of go-algorand
3+
//
4+
// go-algorand is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// go-algorand is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
16+
17+
/*
18+
This file contains code to adjust the json serialization of
19+
Transaction. Currently the only change from the standard serialization
20+
provided by the codec package is to cause BoxRefs to be serialized with b64
21+
names, rather than the default for string, which would not properly encode
22+
their binary nature in JSON.
23+
24+
Had we done it from the beginning, it would be reasonable to do the same for
25+
some other fields that are declared as string, but can contain arbitrary
26+
binary data, such as AssetParams fields like unitname and url. We can't do
27+
that now, to preserve the shape of our REST APIs. But perhaps we can do it
28+
for a v3 REST API.
29+
30+
*/
31+
32+
package transactions
33+
34+
import "github.com/algorand/go-algorand/protocol"
35+
36+
type boxRefStringly struct {
37+
_struct struct{} `codec:",omitempty,omitemptyarray"`
38+
Index uint64 `codec:"i"`
39+
Name []byte `codec:"n"`
40+
}
41+
42+
// MarshalJSON makes it so BoxRefs emit b64 encoded names
43+
func (br BoxRef) MarshalJSON() ([]byte, error) {
44+
return protocol.EncodeJSON(boxRefStringly{
45+
Index: br.Index,
46+
Name: []byte(br.Name),
47+
}), nil
48+
}
49+
50+
// UnmarshalJSON makes it so BoxRefs read u64 names
51+
func (br *BoxRef) UnmarshalJSON(data []byte) error {
52+
var x boxRefStringly
53+
protocol.DecodeJSON(data, &x)
54+
br.Index = x.Index
55+
br.Name = string(x.Name)
56+
return nil
57+
}

data/transactions/json_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (C) 2019-2022 Algorand, Inc.
2+
// This file is part of go-algorand
3+
//
4+
// go-algorand is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// go-algorand is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package transactions_test
18+
19+
import (
20+
"strings"
21+
"testing"
22+
23+
"github.com/algorand/go-algorand/data/basics"
24+
"github.com/algorand/go-algorand/data/transactions"
25+
"github.com/algorand/go-algorand/data/txntest"
26+
"github.com/algorand/go-algorand/protocol"
27+
"github.com/stretchr/testify/require"
28+
)
29+
30+
func decode(data string, v interface{}) error {
31+
err := protocol.DecodeJSON([]byte(data), v)
32+
if err != nil {
33+
panic(err)
34+
}
35+
return err
36+
}
37+
38+
func compact(data []byte) string {
39+
return strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
40+
}
41+
42+
// TestJsonMarshal ensures that BoxRef names are b64 encoded, since they may not be characters.
43+
func TestJsonMarshal(t *testing.T) {
44+
marshal := protocol.EncodeJSON(transactions.BoxRef{Index: 4, Name: "joe"})
45+
require.Equal(t, `{"i":4,"n":"am9l"}`, compact(marshal))
46+
47+
marshal = protocol.EncodeJSON(transactions.BoxRef{Index: 0, Name: "joe"})
48+
require.Equal(t, `{"n":"am9l"}`, compact(marshal))
49+
50+
marshal = protocol.EncodeJSON(transactions.BoxRef{Index: 1, Name: ""})
51+
require.Equal(t, `{"i":1}`, compact(marshal))
52+
53+
marshal = protocol.EncodeJSON(transactions.BoxRef{Index: 0, Name: ""})
54+
require.Equal(t, `{}`, compact(marshal))
55+
}
56+
57+
// TestJsonUnmarshal ensures that BoxRef unmarshaling expects b64 names
58+
func TestJsonUnmarshal(t *testing.T) {
59+
var br transactions.BoxRef
60+
61+
decode(`{"i":4,"n":"am9l"}`, &br)
62+
require.Equal(t, transactions.BoxRef{Index: 4, Name: "joe"}, br)
63+
64+
decode(`{"n":"am9l"}`, &br)
65+
require.Equal(t, transactions.BoxRef{Index: 0, Name: "joe"}, br)
66+
67+
decode(`{"i":4}`, &br)
68+
require.Equal(t, transactions.BoxRef{Index: 4, Name: ""}, br)
69+
70+
decode(`{}`, &br)
71+
require.Equal(t, transactions.BoxRef{Index: 0, Name: ""}, br)
72+
}
73+
74+
// TestTxnJson tests a few more things about how our Transactions get JSON
75+
// encoded. These things could change without breaking the protocol, should stay
76+
// the same for the sake of REST API compatibility.
77+
func TestTxnJson(t *testing.T) {
78+
txn := txntest.Txn{
79+
Sender: basics.Address{0x01, 0x02, 0x03},
80+
}
81+
marshal := protocol.EncodeJSON(txn.Txn())
82+
require.Contains(t, compact(marshal), `"snd":"AEBA`)
83+
84+
txn = txntest.Txn{
85+
Boxes: []transactions.BoxRef{{Index: 3, Name: "john"}},
86+
}
87+
marshal = protocol.EncodeJSON(txn.Txn())
88+
require.Contains(t, compact(marshal), `"apbx":[{"i":3,"n":"am9obg=="}]`)
89+
90+
marshal = protocol.EncodeJSON(txn.Txn())
91+
}

data/transactions/msgp_gen.go

Lines changed: 137 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

data/transactions/msgp_gen_test.go

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)