8000 consensus: rename (*PeerState).ToJSON to MarshalJSON by mark-rushakoff · Pull Request #524 · cometbft/cometbft · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

consensus: rename (*PeerState).ToJSON to MarshalJSON #524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 14, 2023

Conversation

mark-rushakoff
Copy link
Contributor
@mark-rushakoff mark-rushakoff commented Mar 13, 2023

In (*PeerState).PickSendVote, there is a Debug-level log that includes the PeerState value as a logging field. By default, zerolog json-encodes a struct passed as a log field (when the struct doesn't implement zerolog.LogObjectMarshaler).

Because PeerState didn't have a MarshalJSON method, the JSON encoder fell back to reflection to encode the PeerState value. Reflection did not acquire the lock, and there were data races resulting from an unsynchronized read while logging the PeerState, and concurrent (locked) writes at least during (*PeerState).SetHasProposal and (*PeerState).SetHasVote.

Given that there was only one call to (*PeerState).ToJSON in the cometbft repo, it seemed appropriate to just rename ToJSON to MarshalJSON, as opposed to leaving ToJSON for backwards compatibility. Any third party calls to ToJSON should be able to easily change the method being called.

Example data race (which is no longer reproducible with this change):

==================
WARNING: DATA RACE
Read at 0x00c0004a4870 by goroutine 131:
  reflect.Value.Bool()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/reflect/value.go:288 +0x7c
  encoding/json.boolEncoder()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:539 +0x88
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.ptrEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:943 +0x2a4
  encoding/json.ptrEncoder.encode-fm()
      <autogenerated>:1 +0x6c
  encoding/json.(*encodeState).reflectValue()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:358 +0x74
  encoding/json.(*encodeState).marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:330 +0x1a0
  encoding/json.Marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:161 +0xa0
  github.com/rs/zerolog.init.1.func1()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/encoder_json.go:21 +0x4c
  github.com/rs/zerolog/internal/json.Encoder.AppendInterface()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/internal/json/types.go:366 +0x5c
  github.com/rs/zerolog.appendFieldList()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:273 +0x2b8c
  github.com/rs/zerolog.appendFields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:21 +0x160
  github.com/rs/zerolog.(*Event).Fields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/event.go:165 +0x90
  cosmossdk.io/log.zeroLogWrapper.Debug()
      /Users/hh/go/pkg/mod/cosmossdk.io/log@v0.0.0-20230313123454-0fe816b71a62/logger.go:89 +0x18
  github.com/cosmos/cosmos-sdk/server/log.(*CometZeroLogWrapper).Debug()
      <autogenerated>:1 +0x74
  github.com/cometbft/cometbft/consensus.(*PeerState).PickSendVote()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1138 +0x1bc
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesForHeight()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:794 +0x260
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:724 +0x2cc
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func2()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x58

Previous write at 0x00c0004a4870 by goroutine 130:
  github.com/cometbft/cometbft/consensus.(*PeerState).SetHasProposal()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1096 +0x118
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipDataRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:617 +0xab8
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x58

Goroutine 131 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x240
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34

Goroutine 130 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x164
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34
==================

PR checklist

  • Tests written/updated
  • Changelog entry added in .changelog (we use unclog to manage our changelog)
  • Updated relevant documentation (docs/ or spec/) and code comments

In (*PeerState).PickSendVote, there is a Debug-level log that includes
the PeerState value as a logging field. By default, zerolog json-encodes
a struct passed as a log field (when the struct doesn't implement
zerolog.LogObjectMarshaler).

Because PeerState didn't have a MarshalJSON method, the JSON encoder
fell back to reflection to encode the PeerState value. Reflection did
not acquire the lock, and there were data races resulting from an
unsynchronized read while logging the PeerState, and concurrent (locked)
writes at least during (*PeerState).SetHasProposal and
(*PeerState).SetHasVote.

Given that there was only one call to (*PeerState).ToJSON in the
cometbft repo, it seemed appropriate to just rename ToJSON to
MarshalJSON, as opposed to leaving ToJSON for backwards compatibility.
Any third party calls to ToJSON should be able to easily change the
method being called.
@mark-rushakoff mark-rushakoff requested a review from a team as a code owner March 13, 2023 21:01
Copy link
Contributor
@cason cason left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great find.

It appears that the renamed method is not used anywhere else, except in the RPC code addressed by this PR. So I would say that is not a breaking change.

@mark-rushakoff
Copy link
Contributor Author

Thanks @cason, I moved the changelog entry from breaking changes to a bug fix in 0ed6fbf. Since this changed an exported method, I considered it arguably a breaking change, but bug fix also seems appropriate given the scope.

@thanethomson thanethomson added backport-to-v0.34.x Tell Mergify to backport the PR to v0.34.x backport-to-v0.37.x Tell Mergify to backport the PR to v0.37.x labels Mar 14, 2023
Copy link
Contributor
@thanethomson thanethomson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this @mark-rushakoff! I'd say that this should probably be safe to backport to v0.37 and v0.34, given the scope of the change. If folks complain about it, we could always introduce a shim ToJSON method that just implicitly calls MarshalJSON.

@mergify mergify bot merged commit 587bc0b into cometbft:main Mar 14, 2023
mergify bot pushed a commit that referenced this pull request Mar 14, 2023
In (*PeerState).PickSendVote, there is a Debug-level log that includes the PeerState value as a logging field. By default, zerolog json-encodes a struct passed as a log field (when the struct doesn't implement zerolog.LogObjectMarshaler).

Because PeerState didn't have a MarshalJSON method, the JSON encoder fell back to reflection to encode the PeerState value. Reflection did not acquire the lock, and there were data races resulting from an unsynchronized read while logging the PeerState, and concurrent (locked) writes at least during (*PeerState).SetHasProposal and (*PeerState).SetHasVote.

Given that there was only one call to (*PeerState).ToJSON in the cometbft repo, it seemed appropriate to just rename ToJSON to MarshalJSON, as opposed to leaving ToJSON for backwards compatibility. Any third party calls to ToJSON should be able to easily change the method being called.

Example data race (which is no longer reproducible with this change):

```
==================
WARNING: DATA RACE
Read at 0x00c0004a4870 by goroutine 131:
  reflect.Value.Bool()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/reflect/value.go:288 +0x7c
  encoding/json.boolEncoder()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:539 +0x88
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.ptrEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:943 +0x2a4
  encoding/json.ptrEncoder.encode-fm()
      <autogenerated>:1 +0x6c
  encoding/json.(*encodeState).reflectValue()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:358 +0x74
  encoding/json.(*encodeState).marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:330 +0x1a0
  encoding/json.Marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:161 +0xa0
  github.com/rs/zerolog.init.1.func1()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/encoder_json.go:21 +0x4c
  github.com/rs/zerolog/internal/json.Encoder.AppendInterface()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/internal/json/types.go:366 +0x5c
  github.com/rs/zerolog.appendFieldList()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:273 +0x2b8c
  github.com/rs/zerolog.appendFields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:21 +0x160
  github.com/rs/zerolog.(*Event).Fields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/event.go:165 +0x90
  cosmossdk.io/log.zeroLogWrapper.Debug()
      /Users/hh/go/pkg/mod/cosmossdk.io/log@v0.0.0-20230313123454-0fe816b71a62/logger.go:89 +0x18
  github.com/cosmos/cosmos-sdk/server/log.(*CometZeroLogWrapper).Debug()
      <autogenerated>:1 +0x74
  github.com/cometbft/cometbft/consensus.(*PeerState).PickSendVote()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1138 +0x1bc
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesForHeight()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:794 +0x260
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:724 +0x2cc
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func2()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x58

Previous write at 0x00c0004a4870 by goroutine 130:
  github.com/cometbft/cometbft/consensus.(*PeerState).SetHasProposal()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1096 +0x118
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipDataRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:617 +0xab8
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x58

Goroutine 131 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x240
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34

Goroutine 130 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x164
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34
==================
```

---

#### PR checklist

- [ ] Tests written/updated
- [x] Changelog entry added in `.changelog` (we use [unclog](https://github.com/informalsystems/unclog) to manage our changelog)
- [ ] Updated relevant documentation (`docs/` or `spec/`) and code comments

(cherry picked from commit 587bc0b)
mergify bot pushed a commit that referenced this pull request Mar 14, 2023
In (*PeerState).PickSendVote, there is a Debug-level log that includes the PeerState value as a logging field. By default, zerolog json-encodes a struct passed as a log field (when the struct doesn't implement zerolog.LogObjectMarshaler).

Because PeerState didn't have a MarshalJSON method, the JSON encoder fell back to reflection to encode the PeerState value. Reflection did not acquire the lock, and there were data races resulting from an unsynchronized read while logging the PeerState, and concurrent (locked) writes at least during (*PeerState).SetHasProposal and (*PeerState).SetHasVote.

Given that there was only one call to (*PeerState).ToJSON in the cometbft repo, it seemed appropriate to just rename ToJSON to MarshalJSON, as opposed to leaving ToJSON for backwards compatibility. Any third party calls to ToJSON should be able to easily change the method being called.

Example data race (which is no longer reproducible with this change):

```
==================
WARNING: DATA RACE
Read at 0x00c0004a4870 by goroutine 131:
  reflect.Value.Bool()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/reflect/value.go:288 +0x7c
  encoding/json.boolEncoder()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:539 +0x88
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.ptrEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:943 +0x2a4
  encoding/json.ptrEncoder.encode-fm()
      <autogenerated>:1 +0x6c
  encoding/json.(*encodeState).reflectValue()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:358 +0x74
  encoding/json.(*encodeState).marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:330 +0x1a0
  encoding/json.Marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:161 +0xa0
  github.com/rs/zerolog.init.1.func1()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/encoder_json.go:21 +0x4c
  github.com/rs/zerolog/internal/json.Encoder.AppendInterface()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/internal/json/types.go:366 +0x5c
  github.com/rs/zerolog.appendFieldList()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:273 +0x2b8c
  github.com/rs/zerolog.appendFields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:21 +0x160
  github.com/rs/zerolog.(*Event).Fields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/event.go:165 +0x90
  cosmossdk.io/log.zeroLogWrapper.Debug()
      /Users/hh/go/pkg/mod/cosmossdk.io/log@v0.0.0-20230313123454-0fe816b71a62/logger.go:89 +0x18
  github.com/cosmos/cosmos-sdk/server/log.(*CometZeroLogWrapper).Debug()
      <autogenerated>:1 +0x74
  github.com/cometbft/cometbft/consensus.(*PeerState).PickSendVote()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1138 +0x1bc
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesForHeight()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:794 +0x260
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:724 +0x2cc
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func2()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x58

Previous write at 0x00c0004a4870 by goroutine 130:
  github.com/cometbft/cometbft/consensus.(*PeerState).SetHasProposal()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1096 +0x118
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipDataRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:617 +0xab8
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x58

Goroutine 131 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x240
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34

Goroutine 130 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x164
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34
==================
```

---

#### PR checklist

- [ ] Tests written/updated
- [x] Changelog entry added in `.changelog` (we use [unclog](https://github.com/informalsystems/unclog) to manage our changelog)
- [ ] Updated relevant documentation (`docs/` or `spec/`) and code comments

(cherry picked from commit 587bc0b)

# Conflicts:
#	.changelog/v0.37.0/bug-fixes/524-rename-peerstate-tojson.md
thanethomson added a commit that referenced this pull request Mar 15, 2023
…532)

* consensus: rename (*PeerState).ToJSON to MarshalJSON (#524)

In (*PeerState).PickSendVote, there is a Debug-level log that includes the PeerState value as a logging field. By default, zerolog json-encodes a struct passed as a log field (when the struct doesn't implement zerolog.LogObjectMarshaler).

Because PeerState didn't have a MarshalJSON method, the JSON encoder fell back to reflection to encode the PeerState value. Reflection did not acquire the lock, and there were data races resulting from an unsynchronized read while logging the PeerState, and concurrent (locked) writes at least during (*PeerState).SetHasProposal and (*PeerState).SetHasVote.

Given that there was only one call to (*PeerState).ToJSON in the cometbft repo, it seemed appropriate to just rename ToJSON to MarshalJSON, as opposed to leaving ToJSON for backwards compatibility. Any third party calls to ToJSON should be able to easily change the method being called.

Example data race (which is no longer reproducible with this change):

```
==================
WARNING: DATA RACE
Read at 0x00c0004a4870 by goroutine 131:
  reflect.Value.Bool()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/reflect/value.go:288 +0x7c
  encoding/json.boolEncoder()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:539 +0x88
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.ptrEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:943 +0x2a4
  encoding/json.ptrEncoder.encode-fm()
      <autogenerated>:1 +0x6c
  encoding/json.(*encodeState).reflectValue()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:358 +0x74
  encoding/json.(*encodeState).marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:330 +0x1a0
  encoding/json.Marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:161 +0xa0
  github.com/rs/zerolog.init.1.func1()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/encoder_json.go:21 +0x4c
  github.com/rs/zerolog/internal/json.Encoder.AppendInterface()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/internal/json/types.go:366 +0x5c
  github.com/rs/zerolog.appendFieldList()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:273 +0x2b8c
  github.com/rs/zerolog.appendFields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:21 +0x160
  github.com/rs/zerolog.(*Event).Fields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/event.go:165 +0x90
  cosmossdk.io/log.zeroLogWrapper.Debug()
      /Users/hh/go/pkg/mod/cosmossdk.io/log@v0.0.0-20230313123454-0fe816b71a62/logger.go:89 +0x18
  github.com/cosmos/cosmos-sdk/server/log.(*CometZeroLogWrapper).Debug()
      <autogenerated>:1 +0x74
  github.com/cometbft/cometbft/consensus.(*PeerState).PickSendVote()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1138 +0x1bc
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesForHeight()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:794 +0x260
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:724 +0x2cc
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func2()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x58

Previous write at 0x00c0004a4870 by goroutine 130:
  github.com/cometbft/cometbft/consensus.(*PeerState).SetHasProposal()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1096 +0x118
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipDataRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:617 +0xab8
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x58

Goroutine 131 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x240
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34

Goroutine 130 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x164
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34
==================
```

---

#### PR checklist

- [ ] Tests written/updated
- [x] Changelog entry added in `.changelog` (we use [unclog](https://github.com/informalsystems/unclog) to manage our changelog)
- [ ] Updated relevant documentation (`docs/` or `spec/`) and code comments

(cherry picked from commit 587bc0b)

# Conflicts:
#	.changelog/v0.37.0/bug-fixes/524-rename-peerstate-tojson.md

* Move changelog entry to correct folder

Signed-off-by: Thane Thomson <connect@thanethomson.com>

---------

Signed-off-by: Thane Thomson <connect@thanethomson.com>
Co-authored-by: Mark Rushakoff <mark.rushakoff@gmail.com>
Co-authored-by: Thane Thomson <connect@thanethomson.com>
thanethomson added a commit that referenced this pull request Mar 16, 2023
In (*PeerState).PickSendVote, there is a Debug-level log that includes the PeerState value as a logging field. By default, zerolog json-encodes a struct passed as a log field (when the struct doesn't implement zerolog.LogObjectMarshaler).

Because PeerState didn't have a MarshalJSON method, the JSON encoder fell back to reflection to encode the PeerState value. Reflection did not acquire the lock, and there were data races resulting from an unsynchronized read while logging the PeerState, and concurrent (locked) writes at least during (*PeerState).SetHasProposal and (*PeerState).SetHasVote.

Given that there was only one call to (*PeerState).ToJSON in the cometbft repo, it seemed appropriate to just rename ToJSON to MarshalJSON, as opposed to leaving ToJSON for backwards compatibility. Any third party calls to ToJSON should be able to easily change the method being called.

Example data race (which is no longer reproducible with this change):

```
==================
WARNING: DATA RACE
Read at 0x00c0004a4870 by goroutine 131:
  reflect.Value.Bool()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/reflect/value.go:288 +0x7c
  encoding/json.boolEncoder()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:539 +0x88
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.ptrEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:943 +0x2a4
  encoding/json.ptrEncoder.encode-fm()
      <autogenerated>:1 +0x6c
  encoding/json.(*encodeState).reflectValue()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:358 +0x74
  encoding/json.(*encodeState).marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:330 +0x1a0
  encoding/json.Marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:161 +0xa0
  github.com/rs/zerolog.init.1.func1()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/encoder_json.go:21 +0x4c
  github.com/rs/zerolog/internal/json.Encoder.AppendInterface()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/internal/json/types.go:366 +0x5c
  github.com/rs/zerolog.appendFieldList()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:273 +0x2b8c
  github.com/rs/zerolog.appendFields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:21 +0x160
  github.com/rs/zerolog.(*Event).Fields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/event.go:165 +0x90
  cosmossdk.io/log.zeroLogWrapper.Debug()
      /Users/hh/go/pkg/mod/cosmossdk.io/log@v0.0.0-20230313123454-0fe816b71a62/logger.go:89 +0x18
  github.com/cosmos/cosmos-sdk/server/log.(*CometZeroLogWrapper).Debug()
      <autogenerated>:1 +0x74
  github.com/cometbft/cometbft/consensus.(*PeerState).PickSendVote()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1138 +0x1bc
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesForHeight()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:794 +0x260
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:724 +0x2cc
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func2()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x58

Previous write at 0x00c0004a4870 by goroutine 130:
  github.com/cometbft/cometbft/consensus.(*PeerState).SetHasProposal()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1096 +0x118
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipDataRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:617 +0xab8
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x58

Goroutine 131 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x240
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34

Goroutine 130 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x164
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34
==================
```

---

#### PR checklist

- [ ] Tests written/updated
- [x] Changelog entry added in `.changelog` (we use [unclog](https://github.com/informalsystems/unclog) to manage our changelog)
- [ ] Updated relevant documentation (`docs/` or `spec/`) and code comments

(cherry picked from commit 587bc0b)

Co-authored-by: Mark Rushakoff <mark.rushakoff@gmail.com>
Co-authored-by: Thane Thomson <connect@thanethomson.com>
@cason
Copy link
Contributor
cason commented Jun 12, 2023

Hey, this fix potentially introduced this problem: #865

roy-dydx pushed a commit to dydxprotocol/cometbft that referenced this pull request Jul 11, 2023
…ft#524) (cometbft#532)

* consensus: rename (*PeerState).ToJSON to MarshalJSON (cometbft#524)

In (*PeerState).PickSendVote, there is a Debug-level log that includes the PeerState value as a logging field. By default, zerolog json-encodes a struct passed as a log field (when the struct doesn't implement zerolog.LogObjectMarshaler).

Because PeerState didn't have a MarshalJSON method, the JSON encoder fell back to reflection to encode the PeerState value. Reflection did not acquire the lock, and there were data races resulting from an un
6D47
synchronized read while logging the PeerState, and concurrent (locked) writes at least during (*PeerState).SetHasProposal and (*PeerState).SetHasVote.

Given that there was only one call to (*PeerState).ToJSON in the cometbft repo, it seemed appropriate to just rename ToJSON to MarshalJSON, as opposed to leaving ToJSON for backwards compatibility. Any third party calls to ToJSON should be able to easily change the method being called.

Example data race (which is no longer reproducible with this change):

```
==================
WARNING: DATA RACE
Read at 0x00c0004a4870 by goroutine 131:
  reflect.Value.Bool()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/reflect/value.go:288 +0x7c
  encoding/json.boolEncoder()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:539 +0x88
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.structEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:759 +0x1bc
  encoding/json.structEncoder.encode-fm()
      <autogenerated>:1 +0x94
  encoding/json.ptrEncoder.encode()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:943 +0x2a4
  encoding/json.ptrEncoder.encode-fm()
      <autogenerated>:1 +0x6c
  encoding/json.(*encodeState).reflectValue()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:358 +0x74
  encoding/json.(*encodeState).marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:330 +0x1a0
  encoding/json.Marshal()
      /opt/homebrew/Cellar/go/1.20.1/libexec/src/encoding/json/encode.go:161 +0xa0
  github.com/rs/zerolog.init.1.func1()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/encoder_json.go:21 +0x4c
  github.com/rs/zerolog/internal/json.Encoder.AppendInterface()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/internal/json/types.go:366 +0x5c
  github.com/rs/zerolog.appendFieldList()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:273 +0x2b8c
  github.com/rs/zerolog.appendFields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/fields.go:21 +0x160
  github.com/rs/zerolog.(*Event).Fields()
      /Users/hh/go/pkg/mod/github.com/rs/zerolog@v1.29.0/event.go:165 +0x90
  cosmossdk.io/log.zeroLogWrapper.Debug()
      /Users/hh/go/pkg/mod/cosmossdk.io/log@v0.0.0-20230313123454-0fe816b71a62/logger.go:89 +0x18
  github.com/cosmos/cosmos-sdk/server/log.(*CometZeroLogWrapper).Debug()
      <autogenerated>:1 +0x74
  github.com/cometbft/cometbft/consensus.(*PeerState).PickSendVote()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1138 +0x1bc
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesForHeight()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:794 +0x260
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipVotesRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:724 +0x2cc
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func2()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x58

Previous write at 0x00c0004a4870 by goroutine 130:
  github.com/cometbft/cometbft/consensus.(*PeerState).SetHasProposal()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:1096 +0x118
  github.com/cometbft/cometbft/consensus.(*Reactor).gossipDataRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:617 +0xab8
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x58

Goroutine 131 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:199 +0x240
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34

Goroutine 130 (running) created at:
  github.com/cometbft/cometbft/consensus.(*Reactor).AddPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/consensus/reactor.go:198 +0x164
  github.com/cometbft/cometbft/p2p.(*Switch).addPeer()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:855 +0x7b4
  github.com/cometbft/cometbft/p2p.(*Switch).acceptRoutine()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:707 +0x704
  github.com/cometbft/cometbft/p2p.(*Switch).OnStart.func1()
      /Users/hh/go/src/github.com/cometbft/cometbft/p2p/switch.go:241 +0x34
==================
```

---

#### PR checklist

- [ ] Tests written/updated
- [x] Changelog entry added in `.changelog` (we use [unclog](https://github.com/informalsystems/unclog) to manage our changelog)
- [ ] Updated relevant documentation (`docs/` or `spec/`) and code comments

(cherry picked from commit 587bc0b)

# Conflicts:
#	.changelog/v0.37.0/bug-fixes/524-rename-peerstate-tojson.md

* Move changelog entry to correct folder

Signed-off-by: Thane Thomson <connect@thanethomson.com>

---------

Signed-off-by: Thane Thomson <connect@thanethomson.com>
Co-authored-by: Mark Rushakoff <mark.rushakoff@gmail.com>
Co-authored-by: Thane Thomson <connect@thanethomson.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge backport-to-v0.34.x Tell Mergify to backport the PR to v0.34.x backport-to-v0.37.x Tell Mergify to backport the PR to v0.37.x
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants
0