From 73c4f02e40a566f65ea8c28db9c29646c48a258f Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 28 Mar 2022 13:22:57 -0400 Subject: [PATCH 1/4] Add ParticipationUpdates to BlockHeader. (#306) --- types/block.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/types/block.go b/types/block.go index c2f5dfc3..88322db3 100644 --- a/types/block.go +++ b/types/block.go @@ -92,6 +92,21 @@ type ( // transactions have ever been committed (since TxnCounter // started being supported). TxnCounter uint64 `codec:"tc"` + + // ParticipationUpdates contains the information needed to mark + // certain accounts offline because their participation keys expired + ParticipationUpdates + } + + // ParticipationUpdates represents participation account data that + // needs to be checked/acted on by the network + ParticipationUpdates struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // ExpiredParticipationAccounts contains a list of online accounts + // that needs to be converted to offline since their + // participation key expired. + ExpiredParticipationAccounts []Address `codec:"partupdrmv"` } // RewardsState represents the global parameters controlling the rate From ab5dd33ccc38d3cdcc8f961a78e3b8663105701b Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 28 Mar 2022 16:12:02 -0400 Subject: [PATCH 2/4] Avoid client response failure on unknown field. (#307) --- client/v2/common/common.go | 9 ++--- encoding/json/json.go | 27 ++++++++++++++ encoding/json/json_test.go | 60 ++++++++++++++++++++++++++++++++ encoding/msgpack/msgpack.go | 17 +++++++++ encoding/msgpack/msgpack_test.go | 60 ++++++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 encoding/json/json_test.go create mode 100644 encoding/msgpack/msgpack_test.go diff --git a/client/v2/common/common.go b/client/v2/common/common.go index 4354216b..8e6068e9 100644 --- a/client/v2/common/common.go +++ b/client/v2/common/common.go @@ -3,13 +3,14 @@ package common import ( "bytes" "context" - "encoding/json" + "fmt" "io" "io/ioutil" "net/http" "net/url" + "github.com/algorand/go-algorand-sdk/encoding/json" "github.com/algorand/go-algorand-sdk/encoding/msgpack" "github.com/google/go-querystring/query" ) @@ -123,7 +124,7 @@ func (client *Client) submitFormRaw(ctx context.Context, path string, body inter queryURL.RawQuery = mergeRawQueries(queryURL.RawQuery, v.Encode()) if encodeJSON { - jsonValue, _ := json.Marshal(body) + jsonValue := json.Encode(body) bodyReader = bytes.NewBuffer(jsonValue) } } @@ -182,7 +183,7 @@ func (client *Client) submitForm(ctx context.Context, response interface{}, path } // Attempt to unmarshal a response regardless of whether or not there was an error. - err = json.Unmarshal(bodyBytes, response) + err = json.LenientDecode(bodyBytes, response) if responseErr != nil { // Even if there was an unmarshalling error, return the HTTP error first if there was one. return responseErr @@ -230,7 +231,7 @@ func (client *Client) GetRawMsgpack(ctx context.Context, response interface{}, p return extractError(resp.StatusCode, bodyBytes) } - dec := msgpack.NewDecoder(resp.Body) + dec := msgpack.NewLenientDecoder(resp.Body) return dec.Decode(&response) } diff --git a/encoding/json/json.go b/encoding/json/json.go index d44ba753..a150694e 100644 --- a/encoding/json/json.go +++ b/encoding/json/json.go @@ -10,6 +10,9 @@ import ( // with our settings (canonical, paranoid about decoding errors) var CodecHandle *codec.JsonHandle +// LenientCodecHandle is used to instantiate msgpack encoders for the REST API. +var LenientCodecHandle *codec.JsonHandle + // init configures our json encoder and decoder func init() { CodecHandle = new(codec.JsonHandle) @@ -19,6 +22,14 @@ func init() { CodecHandle.RecursiveEmptyCheck = true CodecHandle.Indent = 2 CodecHandle.HTMLCharsAsIs = true + + LenientCodecHandle = new(codec.JsonHandle) + LenientCodecHandle.ErrorIfNoField = false + LenientCodecHandle.ErrorIfNoArrayExpand = true + LenientCodecHandle.Canonical = true + LenientCodecHandle.RecursiveEmptyCheck = true + LenientCodecHandle.Indent = 2 + LenientCodecHandle.HTMLCharsAsIs = true } // Encode returns a json-encoded byte buffer for a given object @@ -40,7 +51,23 @@ func Decode(b []byte, objptr interface{}) error { return nil } +// LenientDecode attempts to decode a json-encoded byte buffer into an +// object instance pointed to by objptr +func LenientDecode(b []byte, objptr interface{}) error { + dec := codec.NewDecoderBytes(b, LenientCodecHandle) + err := dec.Decode(objptr) + if err != nil { + return err + } + return nil +} + // NewDecoder returns a json decoder func NewDecoder(r io.Reader) *codec.Decoder { return codec.NewDecoder(r, CodecHandle) } + +// NewLenientDecoder returns a json decoder +func NewLenientDecoder(r io.Reader) *codec.Decoder { + return codec.NewDecoder(r, LenientCodecHandle) +} diff --git a/encoding/json/json_test.go b/encoding/json/json_test.go new file mode 100644 index 00000000..8a677716 --- /dev/null +++ b/encoding/json/json_test.go @@ -0,0 +1,60 @@ +package json + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +type object struct { + subsetObject + Name string `codec:"name"` +} + +type subsetObject struct { + Data string `codec:"data"` +} + +func TestDecode(t *testing.T) { + obj := object{ + subsetObject: subsetObject{Data: "data"}, + Name: "name", + } + encodedOb := Encode(obj) + + t.Run("basic encode/decode test", func(t *testing.T) { + // basic encode/decode test. + var decoded object + err := Decode(encodedOb, &decoded) + assert.NoError(t, err) + assert.Equal(t, obj, decoded) + }) + + t.Run("strict decode, pass", func(t *testing.T) { + // strict decode test + decoder := NewDecoder(bytes.NewReader(encodedOb)) + var decoded object + err := decoder.Decode(&decoded) + assert.NoError(t, err) + assert.Equal(t, obj, decoded) + }) + + t.Run("strict decode subset, fail", func(t *testing.T) { + // strict decode test + decoder := NewDecoder(bytes.NewReader(encodedOb)) + var decoded subsetObject + err := decoder.Decode(&decoded) + assert.Error(t, err) + assert.Contains(t, err.Error(), "no matching struct field found when decoding stream map with key name") + }) + + t.Run("lenient decode subset, pass", func(t *testing.T) { + // strict decode test + decoder := NewLenientDecoder(bytes.NewReader(encodedOb)) + var decoded subsetObject + err := decoder.Decode(&decoded) + assert.NoError(t, err) + assert.Equal(t, obj.subsetObject, decoded) + }) +} diff --git a/encoding/msgpack/msgpack.go b/encoding/msgpack/msgpack.go index 54ef64f7..f54af545 100644 --- a/encoding/msgpack/msgpack.go +++ b/encoding/msgpack/msgpack.go @@ -10,6 +10,9 @@ import ( // with our settings (canonical, paranoid about decoding errors) var CodecHandle *codec.MsgpackHandle +// LenientCodecHandle is used to instantiate msgpack encoders for the REST API. +var LenientCodecHandle *codec.MsgpackHandle + // init configures our msgpack encoder and decoder func init() { CodecHandle = new(codec.MsgpackHandle) @@ -19,6 +22,15 @@ func init() { CodecHandle.RecursiveEmptyCheck = true CodecHandle.WriteExt = true CodecHandle.PositiveIntUnsigned = true + + LenientCodecHandle = new(codec.MsgpackHandle) + // allow unknown fields to ensure forward compatibility. + LenientCodecHandle.ErrorIfNoField = false + LenientCodecHandle.ErrorIfNoArrayExpand = true + LenientCodecHandle.Canonical = true + LenientCodecHandle.RecursiveEmptyCheck = true + LenientCodecHandle.WriteExt = true + LenientCodecHandle.PositiveIntUnsigned = true } // Encode returns a msgpack-encoded byte buffer for a given object @@ -44,3 +56,8 @@ func Decode(b []byte, objptr interface{}) error { func NewDecoder(r io.Reader) *codec.Decoder { return codec.NewDecoder(r, CodecHandle) } + +// NewLenientDecoder returns a msgpack decoder +func NewLenientDecoder(r io.Reader) *codec.Decoder { + return codec.NewDecoder(r, LenientCodecHandle) +} diff --git a/encoding/msgpack/msgpack_test.go b/encoding/msgpack/msgpack_test.go new file mode 100644 index 00000000..120fdad6 --- /dev/null +++ b/encoding/msgpack/msgpack_test.go @@ -0,0 +1,60 @@ +package msgpack + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +type object struct { + subsetObject + Name string `codec:"name"` +} + +type subsetObject struct { + Data string `codec:"data"` +} + +func TestDecode(t *testing.T) { + obj := object{ + subsetObject: subsetObject{Data: "data"}, + Name: "name", + } + encodedOb := Encode(obj) + + t.Run("basic encode/decode test", func(t *testing.T) { + // basic encode/decode test. + var decoded object + err := Decode(encodedOb, &decoded) + assert.NoError(t, err) + assert.Equal(t, obj, decoded) + }) + + t.Run("strict decode, pass", func(t *testing.T) { + // strict decode test + decoder := NewDecoder(bytes.NewReader(encodedOb)) + var decoded object + err := decoder.Decode(&decoded) + assert.NoError(t, err) + assert.Equal(t, obj, decoded) + }) + + t.Run("strict decode subset, fail", func(t *testing.T) { + // strict decode test + decoder := NewDecoder(bytes.NewReader(encodedOb)) + var decoded subsetObject + err := decoder.Decode(&decoded) + assert.Error(t, err) + assert.Contains(t, err.Error(), "no matching struct field found when decoding stream map with key name") + }) + + t.Run("lenient decode subset, pass", func(t *testing.T) { + // strict decode test + decoder := NewLenientDecoder(bytes.NewReader(encodedOb)) + var decoded subsetObject + err := decoder.Decode(&decoded) + assert.NoError(t, err) + assert.Equal(t, obj.subsetObject, decoded) + }) +} From cf526a875a31f726f8b5ac5fb68c69c77ba67464 Mon Sep 17 00:00:00 2001 From: Jack Smith Date: Mon, 28 Mar 2022 22:33:53 +0200 Subject: [PATCH 3/4] Updated CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25ddd2a9..2f7804df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.14.1 +- Avoid client response failure on unknown field +- Add ParticipationUpdates to BlockHeader # 1.14.0 - Unlimited assets changes (#294) - Update abi impl from go-algorand (#303) From ff1bfcb66c20b4b68671c693e38642673ed4cdba Mon Sep 17 00:00:00 2001 From: Jack <87339414+algojack@users.noreply.github.com> Date: Mon, 28 Mar 2022 16:40:03 -0400 Subject: [PATCH 4/4] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f7804df..954e61dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # 1.14.1 -- Avoid client response failure on unknown field -- Add ParticipationUpdates to BlockHeader +- Avoid client response failure on unknown field (#307) +- Add ParticipationUpdates to BlockHeader (#306) # 1.14.0 - Unlimited assets changes (#294) - Update abi impl from go-algorand (#303)