| 106 | |
| 107 | template<RandomNumberGenerator R> |
| 108 | void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDataProvider& provider) |
| 109 | { |
| 110 | // Simulation test with two Transport objects, which send messages to each other, with |
| 111 | // sending and receiving fragmented into multiple pieces that may be interleaved. It primarily |
| 112 | // verifies that the sending and receiving side are compatible with each other, plus a few |
| 113 | // sanity checks. It does not attempt to introduce errors in the communicated data. |
| 114 | |
| 115 | // Put the transports in an array for by-index access. |
| 116 | const std::array<Transport*, 2> transports = {&initiator, &responder}; |
| 117 | |
| 118 | // Two vectors representing in-flight bytes. inflight[i] is from transport[i] to transport[!i]. |
| 119 | std::array<std::vector<uint8_t>, 2> in_flight; |
| 120 | |
| 121 | // Two queues with expected messages. expected[i] is expected to arrive in transport[!i]. |
| 122 | std::array<std::deque<CSerializedNetMsg>, 2> expected; |
| 123 | |
| 124 | // Vectors with bytes last returned by GetBytesToSend() on transport[i]. |
| 125 | std::array<std::vector<uint8_t>, 2> to_send; |
| 126 | |
| 127 | // Last returned 'more' values (if still relevant) by transport[i]->GetBytesToSend(), for |
| 128 | // both have_next_message false and true. |
| 129 | std::array<std::optional<bool>, 2> last_more, last_more_next; |
| 130 | |
| 131 | // Whether more bytes to be sent are expected on transport[i], before and after |
| 132 | // SetMessageToSend(). |
| 133 | std::array<std::optional<bool>, 2> expect_more, expect_more_next; |
| 134 | |
| 135 | // Function to consume a message type. |
| 136 | auto msg_type_fn = [&]() { |
| 137 | uint8_t v = provider.ConsumeIntegral<uint8_t>(); |
| 138 | if (v == 0xFF) { |
| 139 | // If v is 0xFF, construct a valid (but possibly unknown) message type from the fuzz |
| 140 | // data. |
| 141 | std::string ret; |
| 142 | while (ret.size() < CMessageHeader::MESSAGE_TYPE_SIZE) { |
| 143 | char c = provider.ConsumeIntegral<char>(); |
| 144 | // Match the allowed characters in CMessageHeader::IsMessageTypeValid(). Any other |
| 145 | // character is interpreted as end. |
| 146 | if (c < ' ' || c > 0x7E) break; |
| 147 | ret += c; |
| 148 | } |
| 149 | return ret; |
| 150 | } else { |
| 151 | // Otherwise, use it as index into the list of known messages. |
| 152 | return g_all_messages[v % g_all_messages.size()]; |
| 153 | } |
| 154 | }; |
| 155 | |
| 156 | // Function to construct a CSerializedNetMsg to send. |
| 157 | auto make_msg_fn = [&](bool first) { |
| 158 | CSerializedNetMsg msg; |
| 159 | if (first) { |
| 160 | // Always send a "version" message as first one. |
| 161 | msg.m_type = "version"; |
| 162 | } else { |
| 163 | msg.m_type = msg_type_fn(); |
| 164 | } |
| 165 | // Determine size of message to send (limited to 75 kB for performance reasons). |
no test coverage detected