rpc: fix encoding of block_results responses (backport #8593) (#8594)

The block results include validator updates in ABCI protobuf format, which does
not encode "correctly" according to the expected Amino style RPC clients expect.

- Write a regression test for this issue.
- Add JSON marshaling overrides for ABCI ValidatorUpdate messages.

Patches for v0.35.x:

- Replace jsontypes with tmjson (removed in v0.36)
- Regress test data for BeginBlock / EndBlock
This commit is contained in:
M. J. Fromberger
2022-05-24 07:21:28 -07:00
committed by GitHub
parent ad9e875376
commit 87763a3d6a
3 changed files with 107 additions and 0 deletions

View File

@@ -12,6 +12,8 @@ Special thanks to external contributors on this release:
- CLI/RPC/Config
- [rpc] \#8594 fix encoding of block_results responses (@creachadair)
- Apps
- P2P Protocol

View File

@@ -5,6 +5,9 @@ import (
"encoding/json"
"github.com/gogo/protobuf/jsonpb"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/encoding"
tmjson "github.com/tendermint/tendermint/libs/json"
)
const (
@@ -102,6 +105,48 @@ func (r *EventAttribute) UnmarshalJSON(b []byte) error {
return jsonpbUnmarshaller.Unmarshal(reader, r)
}
// validatorUpdateJSON is the JSON encoding of a validator update.
//
// It handles translation of public keys from the protobuf representation to
// the legacy Amino-compatible format expected by RPC clients.
type validatorUpdateJSON struct {
PubKey json.RawMessage `json:"pub_key,omitempty"`
Power int64 `json:"power,string"`
}
func (v *ValidatorUpdate) MarshalJSON() ([]byte, error) {
key, err := encoding.PubKeyFromProto(v.PubKey)
if err != nil {
return nil, err
}
jkey, err := tmjson.Marshal(key)
if err != nil {
return nil, err
}
return json.Marshal(validatorUpdateJSON{
PubKey: jkey,
Power: v.GetPower(),
})
}
func (v *ValidatorUpdate) UnmarshalJSON(data []byte) error {
var vu validatorUpdateJSON
if err := json.Unmarshal(data, &vu); err != nil {
return err
}
var key crypto.PubKey
if err := tmjson.Unmarshal(vu.PubKey, &key); err != nil {
return err
}
pkey, err := encoding.PubKeyToProto(key)
if err != nil {
return err
}
v.PubKey = pkey
v.Power = vu.Power
return nil
}
// Some compile time assertions to ensure we don't
// have accidental runtime surprises later on.

View File

@@ -1,9 +1,18 @@
package coretypes
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
pbcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto"
"github.com/tendermint/tendermint/types"
)
@@ -32,3 +41,54 @@ func TestStatusIndexer(t *testing.T) {
assert.Equal(t, tc.expected, status.TxIndexEnabled())
}
}
// A regression test for https://github.com/tendermint/tendermint/issues/8583.
func TestResultBlockResults_regression8583(t *testing.T) {
const keyData = "0123456789abcdef0123456789abcdef" // 32 bytes
wantKey := base64.StdEncoding.EncodeToString([]byte(keyData))
rsp := &ResultBlockResults{
ValidatorUpdates: []abci.ValidatorUpdate{{
PubKey: pbcrypto.PublicKey{
Sum: &pbcrypto.PublicKey_Ed25519{Ed25519: []byte(keyData)},
},
Power: 400,
}},
}
// Use compact here so the test data remain legible. The output from the
// marshaler will have whitespace folded out so we need to do that too for
// the comparison to be valid.
var buf bytes.Buffer
require.NoError(t, json.Compact(&buf, []byte(fmt.Sprintf(`
{
"height": 0,
"txs_results": null,
"total_gas_used": 0,
"begin_block_events": null,
"end_block_events": null,
"validator_updates": [
{
"pub_key":{"type": "tendermint/PubKeyEd25519", "value": "%s"},
"power": "400"
}
],
"consensus_param_updates": null
}`, wantKey))))
bits, err := json.Marshal(rsp)
if err != nil {
t.Fatalf("Encoding block result: %v", err)
}
if diff := cmp.Diff(buf.String(), string(bits)); diff != "" {
t.Errorf("Marshaled result (-want, +got):\n%s", diff)
}
back := new(ResultBlockResults)
if err := json.Unmarshal(bits, back); err != nil {
t.Fatalf("Unmarshaling: %v", err)
}
if diff := cmp.Diff(rsp, back); diff != "" {
t.Errorf("Unmarshaled result (-want, +got):\n%s", diff)
}
}