Skip to content

Conversation

jonatack
Copy link
Member

@jonatack jonatack commented Oct 21, 2020

The goal of this PR is to be able to depend on m_inbound_onion in AttemptToEvictConnection in #20197:

  • asserts CNode::m_inbound_onion is inbound in the CNode ctor to have a validity check at the class boundary
  • fixes a unit test and a fuzz utility that were passing invalid inbound onion values to the CNode ctor
  • drops an unneeded check in CNode::ConnectedThroughNetwork() for its inbound status
  • adds a public getter IsInboundOnion() that also allows unit testing it
  • adds unit test coverage

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

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

ACK 39286b3, tested on Linux Mint 20 (x86_64).

@fanquake fanquake added the P2P label Oct 21, 2020
@practicalswift
Copy link
Contributor

Concept ACK

Thanks for improving testing!

@DrahtBot
Copy link
Contributor

DrahtBot commented Nov 12, 2020

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@jonatack jonatack force-pushed the CNode-IsInboundOnion branch from 39286b3 to 0a30f5d Compare November 13, 2020 17:26
@jonatack
Copy link
Member Author

Rebased.

Copy link
Contributor

@theStack theStack left a comment

Choose a reason for hiding this comment

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

ACK 0a30f5d 🏞️

@@ -1095,7 +1095,7 @@ class CNode
CService addrLocal GUARDED_BY(cs_addrLocal);
mutable RecursiveMutex cs_addrLocal;

//! Whether this peer connected via our Tor onion service.
//! Whether this peer is an inbound onion, e.g. connected via our Tor onion service.
const bool m_inbound_onion{false};
Copy link
Contributor

@theStack theStack Nov 22, 2020

Choose a reason for hiding this comment

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

yocto-nit (not directly related to this PR though, feel free to ignore): not sure if we have any guidelines (or dominant opinions) on this subject, but isn't it overkill to have two initializations for a class member: once via in-class initialization and once via initializer list in the ctor? (And yes, I'm aware that initializer lists have higher priority in C++). As reader of the class interface I'd not assume an in-class initialization is overruled in the ctor. Also, without the in-class initialization the compiler has the chance to throw an error if the const member was forgot to be initialized in the ctor.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm, good eye.

-> In this case, ISTM we could remove the default false argument in the constructor declaration

-> As to your last point, it suggests the default member m_inbound_onion initializer not set a value, since we are doing it dynamically in the ctor. It's true that I see a build error in this case, but not without the change in net.h:1099

/src/net.cpp
-    nMyStartingHeight(nMyStartingHeightIn),
-    m_inbound_onion(inbound_onion && conn_type_in == ConnectionType::INBOUND)
+    nMyStartingHeight(nMyStartingHeightIn)

/src/net.h
     //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service.
-    const bool m_inbound_onion{false};
+    const bool m_inbound_onion;
net.cpp:2947:1: error: uninitialized const member in ‘const bool’ [-fpermissive]
 2947 | CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion)
      | ^~~~~
In file included from net.cpp:10:
./net.h:1099:16: note: ‘const bool CNode::m_inbound_onion’ should be initialized
 1099 |     const bool m_inbound_onion;
      |                ^~~~~~~~~~~~~~~

I don't mind pulling in a commit from your branch if you'd like to adjust this.


More generally, from what I can gather, the ideas behind in-class default member initializers added in C++11 were (a) to allow a non-static data member to be initialized where it is declared, e.g. in its class. A constructor can then use the initializer when run-time initialization is needed, which can then (b) tidy up the code up by avoiding doing it in the member initializer list of the constructor. This provides extra benefits in classes with multiple constructors.

I guess one can see the default member initializer as the general default, particularly useful for constant initializers, that can be overridden by any specific ones in member initializer lists in the constructors.

Some resources (send me any good ones you might suggest; my understanding on this is evolving):

Copy link
Member Author

Choose a reason for hiding this comment

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

not sure if we have any guidelines (or dominant opinions) on this subject, but isn't it overkill to have two initializations for a class member: once via in-class initialization and once via initializer list in the ctor? (And yes, I'm aware that initializer lists have higher priority in C++). As reader of the class interface I'd not assume an in-class initialization is overruled in the ctor. Also, without the in-class initialization the compiler has the chance to throw an error if the const member was forgot to be initialized in the ctor.

@theStack: done in #21167

src/net.cpp Outdated
@@ -2957,7 +2957,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
m_conn_type(conn_type_in),
nLocalServices(nLocalServicesIn),
nMyStartingHeight(nMyStartingHeightIn),
m_inbound_onion(inbound_onion)
m_inbound_onion(inbound_onion && conn_type_in == ConnectionType::INBOUND)
Copy link
Member

@laanwj laanwj Dec 3, 2020

Choose a reason for hiding this comment

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

I might be misunderstanding but in what case would the caller pass inbound_onion parameter as true but lie about this, as it is an outbound connection? Is this a code bug?
Wouldn't this be better as an assertion?

Copy link
Member

Choose a reason for hiding this comment

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

Or an Assume, if Assert is too strong and risky

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea, inbound_onion should only be true when a new CNode is instantiated from CConnman::AcceptConnection(), so this should only fail if a bug is added. Made it an assert.

Copy link
Member Author

Choose a reason for hiding this comment

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

in what case would the caller pass inbound_onion parameter as true but lie about this

So, adding the assertion found one! Our net_tests had a case that lied; updating it.

Copy link
Member Author

@jonatack jonatack Dec 17, 2020

Choose a reason for hiding this comment

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

Hm, a couple of fuzzers need to be updated, too. Edit: done

@jonatack jonatack force-pushed the CNode-IsInboundOnion branch from 0a30f5d to dd01a23 Compare December 17, 2020 11:52
@jonatack jonatack changed the title net: ensure CNode::m_inbound_onion is inbound, add getter, unit tests net: assert CNode::m_inbound_onion is inbound in ctor, add getter, unit tests Dec 17, 2020
@jonatack jonatack force-pushed the CNode-IsInboundOnion branch from dd01a23 to 84b91db Compare December 17, 2020 12:48
@jonatack jonatack force-pushed the CNode-IsInboundOnion branch 2 times, most recently from 390c735 to af18de1 Compare December 17, 2020 17:41
maflcko pushed a commit that referenced this pull request Dec 17, 2020
…nsumeNode()

23d8f34 fuzz: replace CNode code with fuzz/util.h::ConsumeNode() (Jon Atack)

Pull request description:

  Noticed this while updating the CNode fuzzing in #20210.

  - cc26fab created `test/fuzz/net.cpp` in May 2020

  - 79ef832 created a CNode factory utility `test/fuzz/util.h::ConsumeNode()` in October 2020

  This PR updates `fuzz/net.cpp` from the first commit to use `ConsumeNode()` from the second commit.

ACKs for top commit:
  MarcoFalke:
    ACK 23d8f34

Tree-SHA512: 26f7685395b3d48fcf40dde0d479d5c2fb4e953ec9371940b19eee16bb30aee4840b081e1a918b924a9704c1bef484302ea3e8fe63819a3bba73e7eb805164f1
as CNode ctor should only be passed inbound_onion = true
when the connection is inbound
and drop an unneeded check in CNode::ConnectedThroughNetwork()
@jonatack jonatack force-pushed the CNode-IsInboundOnion branch from af18de1 to 86c4952 Compare December 17, 2020 18:59
@jonatack
Copy link
Member Author

Rebased/simplified after merge of #20686.

sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Dec 17, 2020
…l.h::ConsumeNode()

23d8f34 fuzz: replace CNode code with fuzz/util.h::ConsumeNode() (Jon Atack)

Pull request description:

  Noticed this while updating the CNode fuzzing in bitcoin#20210.

  - cc26fab created `test/fuzz/net.cpp` in May 2020

  - 79ef832 created a CNode factory utility `test/fuzz/util.h::ConsumeNode()` in October 2020

  This PR updates `fuzz/net.cpp` from the first commit to use `ConsumeNode()` from the second commit.

ACKs for top commit:
  MarcoFalke:
    ACK 23d8f34

Tree-SHA512: 26f7685395b3d48fcf40dde0d479d5c2fb4e953ec9371940b19eee16bb30aee4840b081e1a918b924a9704c1bef484302ea3e8fe63819a3bba73e7eb805164f1
@sipa
Copy link
Member

sipa commented Dec 27, 2020

utACK 86c4952

Copy link
Contributor

@LarryRuane LarryRuane left a comment

Choose a reason for hiding this comment

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

ACK 86c4952
Compiled, ran unit tests.

Copy link
Contributor

@vasild vasild left a comment

Choose a reason for hiding this comment

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

ACK 86c4952

Two minor comments below, feel free to ignore.

@@ -298,7 +298,7 @@ CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept
const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider);
const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH});
const bool inbound_onion = fuzzed_data_provider.ConsumeBool();
const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: same as ConnectionType::INBOUND && fuzzed_data_provider.ConsumeBool()

Copy link
Member Author

Choose a reason for hiding this comment

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

indeed, will update if need to retouch or rebase

Copy link
Member

Choose a reason for hiding this comment

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

difference is that it needs to store one additional redundant byte for every non-inbound fuzz input file, so I think this can be kept as is

Copy link
Member

@maflcko maflcko left a comment

Choose a reason for hiding this comment

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

review ACK 86c4952 🐍

Show signature and timestamp

Signature:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

review ACK 86c495223f048e5ca2cf0d8730af7db3b76f7aba 🐍
-----BEGIN PGP SIGNATURE-----

iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUjcBAwAyH/ZAIoBmnAriDbYf2IdudHCi55YS3Gb2Viv4ujVLCCt+dTDj6OOg6RR
fsTKJe3f0g3eHdss1KkMxTGQm1WP6MDl7Iqpkuuar7eH4F7WslFSamnnRStDZXcG
vUQvrGSKNakc20PT0wT8bP7UxA2+z8/gVS9V40+lKma8VmwZgYE0EcKztdfUiPL5
0v+whP/aUPxjEleeG4aec0FkOEwELAdoC6gTtn2+hBBv2x+Kj6KmeheusTNBpP7P
jYbMqz5K+qEd10yFc+4yH7gdVyKmAuNunToURLz2SZcQmla2iMinRIXboLEhRcvm
pS4YuY62sQ+3bOxxTi3Td7vRMKFmDJfy92vkXgDcQvvpHw72M+k+3ieuzYNvJFUq
maOT4trYzF79M5F4Pd18ywIkgz8v50uddPVEVhl4LGeZyAN47WOH0vIoSZBhmuNN
IdGom1GuBOMsztoXEZDLYEPr9X9HDB8CgGW5t02g4cf/EoqbtZHzIM2VOXDkiVYe
Y5IbMXDV
=O80S
-----END PGP SIGNATURE-----

Timestamp of file with hash 683c8f1c6754ede192ead999e54c0440e14b98b058b5f69d6e0b9817be2c17bb -

@@ -1234,6 +1234,9 @@ class CNode
void MaybeSetAddrName(const std::string& addrNameIn);

std::string ConnectionTypeAsString() const;

/** Whether this peer is an inbound onion, e.g. connected via our Tor onion service. */
Copy link
Member

Choose a reason for hiding this comment

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

nit question: should this say "i.e."? I mean are there any other ways to establish an inbound onion other than via the tor onion service?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't mind updating this and taking @vasild's suggestion at the same time.

Copy link
Member

Choose a reason for hiding this comment

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

Can do as a follow-up. The merge script is already running and this should land in master in the next couple of minutes.

Copy link
Member Author

@jonatack jonatack Jan 2, 2021

Choose a reason for hiding this comment

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

Oh ok, will do in #20197.

Copy link
Member Author

Choose a reason for hiding this comment

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

nit question: should this say "i.e."? I mean are there any other ways to establish an inbound onion other than via the tor onion service?

Done in #21167

@maflcko maflcko merged commit ae8f797 into bitcoin:master Jan 2, 2021
@jonatack jonatack deleted the CNode-IsInboundOnion branch January 2, 2021 09:00
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Jan 2, 2021
… ctor, add getter, unit tests

86c4952 net: add CNode::IsInboundOnion() public getter and unit tests (Jon Atack)
6609eb8 net: assert CNode::m_inbound_onion is inbound in ctor (Jon Atack)
993d1ec test, fuzz: fix constructing CNode with invalid inbound_onion (Jon Atack)

Pull request description:

  The goal of this PR is to be able to depend on `m_inbound_onion` in AttemptToEvictConnection in bitcoin#20197:

  - asserts `CNode::m_inbound_onion` is inbound in the CNode ctor to have a validity check at the class boundary
  - fixes a unit test and a fuzz utility that were passing invalid inbound onion values to the CNode ctor
  - drops an unneeded check in `CNode::ConnectedThroughNetwork()` for its inbound status
  - adds a public getter `IsInboundOnion()` that also allows unit testing it
  - adds unit test coverage

ACKs for top commit:
  sipa:
    utACK 86c4952
  LarryRuane:
    ACK 86c4952
  vasild:
    ACK 86c4952
  MarcoFalke:
    review ACK 86c4952 🐍

Tree-SHA512: 21109105bc4e5e03076fadd489204be00eac710c9de0127708ca2d0a10a048ff81f640f589a7429967ac3eb51d35fe24bb2b12e53e7aa3efbc47aaff6396d204
@jnewbery
Copy link
Contributor

Is there any benefit to adding getter boilerplate code to the header class instead of just making this const member public?

@jonatack
Copy link
Member Author

@jnewbery Unless additional semantics will be needed for future changes or refactoring, no, probably not. I was being prudent.

@jnewbery
Copy link
Contributor

oooh thanks for referencing the cpp core guidelines! I'd say that any getter of a const value is trivial :)

@jonatack
Copy link
Member Author

oooh thanks for referencing the cpp core guidelines! I'd say that any getter of a const value is trivial :)

@jnewbery thanks! done in #21167

maflcko pushed a commit to bitcoin-core/gui that referenced this pull request Feb 15, 2021
…licitly

2ee4a7a net: remove CNode::m_inbound_onion defaults for explicitness (Jon Atack)
24bda56 net: make CNode::m_inbound_onion public, drop getter, update tests (Jon Atack)

Pull request description:

  Refactoring only, no change in behavior. This is a quick follow-up to #20210 to address these review comments:

  - bitcoin/bitcoin#20210 (comment)
  - bitcoin/bitcoin#20210 (comment)
  - bitcoin/bitcoin#20210 (comment)

  Changes:
  - make the `CNode::m_inbound_onion class` member public, update the Doxygen comment, drop the getter, and update the tests
  - remove the `CNode::m_inbound_onion` default value initialization in the ctor declaration and the member initializer in favor of always passing it explicitly to the ctor where we initialize it dynamically, to both clarify the caller code and to allow the compiler to warn if it is uninitialized in the ctor or omitted in the caller

ACKs for top commit:
  MarcoFalke:
    review ACK 2ee4a7a 🏀
  vasild:
    ACK 2ee4a7a

Tree-SHA512: 72961c91168885a9d881756b10bad9d587f5ce297d5a6493c23e267b7178ff22b697bc6868e7761d6304e372d2781453d30011a020afd506b1e308b4ffa5feee
laanwj added a commit that referenced this pull request Mar 30, 2021
…eviction protection test coverage

0cca08a Add unit test coverage for our onion peer eviction protection (Jon Atack)
caa21f5 Protect onion+localhost peers in ProtectEvictionCandidatesByRatio() (Jon Atack)
8f1a53e Use EraseLastKElements() throughout SelectNodeToEvict() (Jon Atack)
8b1e156 Add m_inbound_onion to AttemptToEvictConnection() (Jon Atack)
72e30e8 Add unit tests for ProtectEvictionCandidatesByRatio() (Jon Atack)
ca63b53 Use std::unordered_set instead of std::vector in IsEvicted() (Jon Atack)
41f84d5 Move peer eviction tests to a separate test file (Jon Atack)
f126cbd Extract ProtectEvictionCandidatesByRatio from SelectNodeToEvict (Jon Atack)

Pull request description:

  Now that #19991 and #20210 have been merged, we can determine inbound onion peers using `CNode::m_inbound_onion` and add it to the localhost peers protection in `AttemptToEvictConnection`, which was added in #19670 to address issue #19500.

  Update 28 February 2021: I've updated this to follow gmaxwell's suggestion in #20197 (comment).

  This branch now protects up to 1/4 onion peers (connected via our tor control service), if any, sorted by longest uptime. If any (or all) onion slots remain after that operation, they are then allocated to protect localhost peers, or a minimum of 2 localhost peers in the case that no onion slots remain and 2 or more onion peers were protected, sorted as before by longest uptime.

  This patch also adds test coverage for the longest uptime, localhost, and onion peer eviction protection logic to build on the welcome initial unit testing of #20477.

  Suggest reviewing the commits that move code with `colorMoved = dimmed-zebra` and `colorMovedWs = allow-indentation-change`.

  Closes #11537.

ACKs for top commit:
  laanwj:
    Code review ACK 0cca08a
  vasild:
    ACK 0cca08a

Tree-SHA512: 2f5a63f942acaae7882920fc61f0185dcd51da85e5b736df9d1fc72343726dd17da740e02f30fa5dc5eb3b2d8345707aed96031bec143d48a2497a610aa19abd
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Feb 3, 2022
…it tests

Summary:
> test: fix constructing CNode with invalid inbound_onion
>
> as CNode ctor should only be passed inbound_onion = true
> when the connection is inbound

> net: assert CNode::m_inbound_onion is inbound in ctor
>
> and drop an unneeded check in CNode::ConnectedThroughNetwork()

> net: add CNode::IsInboundOnion() public getter and unit tests

This is a backport of [[bitcoin/bitcoin#20210 | core#20210]]

Test Plan: `ninja all check-all`

Reviewers: #bitcoin_abc, Fabien

Reviewed By: #bitcoin_abc, Fabien

Differential Revision: https://reviews.bitcoinabc.org/D10971
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.