-
Notifications
You must be signed in to change notification settings - Fork 125
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
PoolSv2
integration tests
#1066
base: main
Are you sure you want to change the base?
Conversation
Bencher
Click to view all benchmark results
Bencher - Continuous Benchmarking View Public Perf Page Docs | Repo | Chat | Help |
Bencher
Click to view all benchmark results
Bencher - Continuous Benchmarking View Public Perf Page Docs | Repo | Chat | Help |
Bencher
Click to view all benchmark results
Bencher - Continuous Benchmarking View Public Perf Page Docs | Repo | Chat | Help |
0dde2c8
to
9dc8a6c
Compare
8ace1ac
to
e90b991
Compare
c0dbde4
to
fbb8464
Compare
56c26c0
to
6cfc1c7
Compare
a3f154b
to
0be5cc6
Compare
async fn success_pool_template_provider_connection() { | ||
let sniffer_addr = common::get_available_address(); | ||
let tp_addr = common::get_available_address(); | ||
let pool_addr = common::get_available_address(); | ||
let _tp = common::start_template_provider(tp_addr.port()).await; | ||
let sniffer = common::start_sniffer(tp_addr, sniffer_addr).await; | ||
let _ = common::start_pool(Some(pool_addr), Some(sniffer_addr)).await; | ||
assert!(sniffer.expect_downstream_message(MESSAGE_TYPE_SETUP_CONNECTION)); | ||
assert!(sniffer.expect_upstream_message(MESSAGE_TYPE_SETUP_CONNECTION_SUCCESS)); | ||
assert!(sniffer.expect_downstream_message(MESSAGE_TYPE_COINBASE_OUTPUT_DATA_SIZE)); | ||
assert!(sniffer.expect_upstream_message(MESSAGE_TYPE_NEW_TEMPLATE)); | ||
assert!(sniffer.expect_upstream_message(MESSAGE_TYPE_SET_NEW_PREV_HASH)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's also use next_downstream_message
and next_upstream_message
here so we can get a feeling of how the user experience is going to be like
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is worth doing that in a test where it is needed and not here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok here's a very simple test where I think this makes sense.
I couldn't find it as a MG test but honestly we should have had this in our CI for a long time.
- start TP
- start Pool (connected to TP)
- start SV2 CPU Miner (connected to Pool)
we can assert that:
- Pool connects to TP and receives template (basically what's already being done on
success_pool_template_provider_connection
) - SV2 CPU Miner connects to Pool (
SetupConnection
/SetupConnection.Success
) - SV2 CPU Miner opens standard channel with Pool (
OpenStandardMiningChannel
/OpenStandardMiningChannel.Success
) - Pool sends
SetTarget
to SV2 CPU Miner (SetTarget
) - Pool sends Standard Job to SV2 CPU Miner (
NewMiningJob
) - SV2 CPU Miner sends share to Pool (
SubmitSharesStandard
) - Pool confirms receiving share (
SubmitShares.Success
)
we can assert things like:channel ids and job ids
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I was able to test the above but I think it requires some discussion/changes in mining-device
crate. Currently mining-device
calculates the difficulty in a way that does not always work. On my machine, the difficulty target was never hit when trying to send shares, even after running the test for 60 seconds. When lowering the difficulty target manually the mining-device sends the SubmitShare
message succesfully, but it send many of them, and it will probably behave differently on different machines. I don't have a good solution for this yet, need to sleep on it.
To try to unblock this pr, made the following changes 338cc02#diff-2a0b80af72100f3ef6475d64910e57bfa2194a668b3a7692391a53f964340fb4R21 to give an example of how to use next_x_message
. Also made small doc/var name changes here e99e36b, will fixup this one once it is reviewed.
Edit: opened #1188
} | ||
} | ||
|
||
impl Drop for Sniffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe worth adding a comment to this impl
to highlight the fact that every test needs to drain the entire queue before it finishes?
2047b03
to
c0b499f
Compare
Addressed above comments. |
I believe this will also close #106 |
Handle errors in `start` function for better user experience and to be able to catch errors in test environment, for example without introducing error handling, we do not get a proper response if the provided `coinbase_output` in the config is valid.
Sniffer allows to intercept messages sent between two roles. The sniffer sets between the downstream and upstream roles, aggregates messages sent by the downstream and upstream roles in two separate FIFO queues. The messages are then forwarded to the target role without any modification. It is useful for testing purposes, as it allows to assert that the roles have sent specific messages in a specific order and to inspect the messages details.
A utility struct that wraps the original `PoolSv2` and allows to start the pool role in testing env.
An abstraction above the `TemplateProvider` struct to start TP and mine needed blocks in a single function call.
Stopps the TP running instance in case of test failure.
c0b499f
to
d3fa7c3
Compare
Starts a Template Provider and Pool and checks that both roles send the corrects messages on first connection.
Align `tests-integration` rust version with the other roles inside the `roles` workspace, otherwise weird problems pops in CI.
d3fa7c3
to
e99e36b
Compare
e99e36b
to
f4e1cb0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
This is a great PR, I'm excited to see how many tests we will add thanks to this.
Just left some minor comments
}; | ||
use tar::Archive; | ||
|
||
// prevents get_available_port from ever returning the same port twice | ||
static UNIQUE_PORTS: Lazy<Mutex<HashSet<u16>>> = Lazy::new(|| Mutex::new(HashSet::new())); | ||
|
||
const VERSION_TP: &str = "0.1.7"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use the latest version release, which seems the most stable, 0.1.9
: https://github.com/Sjors/bitcoin/releases/tag/sv2-tp-0.1.9
tokio::task::spawn(async move { | ||
assert!(pool_clone.start().await.is_ok()); | ||
}); | ||
tokio::time::sleep(std::time::Duration::from_secs(1)).await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why there's this sleep
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To give the pool a second to send/receive the messages, otherwise we start asserting for messages before the pool received a response on some of them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a comment to make it clear?
|
||
/// Allows to intercept messages sent between two roles. | ||
/// | ||
/// The downstream(or client) role connects to the [`Sniffer`] `listening_address` and the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The downstream(or client) role connects to the [`Sniffer`] `listening_address` and the | |
/// The downstream (or client) role connects to the [`Sniffer`] `listening_address` and the |
/// the upstream role. And when a response is received it is saved in `upstream_messages` and | ||
/// forwareded to the downstream role. Both `downstream_messages` and `upstream_messages` can be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// the upstream role. And when a response is received it is saved in `upstream_messages` and | |
/// forwareded to the downstream role. Both `downstream_messages` and `upstream_messages` can be | |
/// the upstream role. When a response is received it is saved in `upstream_messages` and | |
/// forwarded to the downstream role. Both `downstream_messages` and `upstream_messages` can be |
#[derive(Debug, Clone)] | ||
pub struct Sniffer { | ||
listening_address: SocketAddr, | ||
upstream: SocketAddr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not call this upstream_address
? For consistency
if let Ok((stream, _)) = listner.accept().await { | ||
stream | ||
} else { | ||
panic!("Impossible to accept dowsntream connetion") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
panic!("Impossible to accept dowsntream connetion") | |
panic!("Impossible to accept dowsntream connection") |
// This is useful to ensure that the test has checked all exchanged messages between the roles. | ||
impl Drop for Sniffer { | ||
fn drop(&mut self) { | ||
// Dont print backtrace on panic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Dont print backtrace on panic | |
// Don't print backtrace on panic |
})); | ||
if !self.downstream_messages.is_empty() { | ||
println!( | ||
"You didnt handle all downstream messages: {:?}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"You didnt handle all downstream messages: {:?}", | |
"You didn't handle all downstream messages: {:?}", |
} | ||
if !self.upstream_messages.is_empty() { | ||
println!( | ||
"You didnt handle all upstream messages: {:?}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"You didnt handle all upstream messages: {:?}", | |
"You didn't handle all upstream messages: {:?}", |
parsers::{CommonMessages, PoolMessages}, | ||
}; | ||
|
||
#[tokio::test] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we add a description of the test? I think it's a good practice since we are going to have a lot of them
@pavlenex hmm not really as we start addressing #1121 we might add a few unit tests to pool, which will indeed fall under #106 but that doesn't necessarily mean that the scope of #106 will be fully addressed, since MG tests are not comprehensive ideally we should eventually jump into #106 as a task on its own... but I'd leave that for some time in the future |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good at first glance. I'll provide a detailed review by tomorrow.
@@ -1,13 +1,24 @@ | |||
[package] | |||
name = "integration-test" | |||
version = "0.1.0" | |||
edition = "2021" | |||
edition = "2018" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
async fn create_upstream( | ||
stream: TcpStream, | ||
) -> Option<(Receiver<MessageFrame>, Sender<MessageFrame>)> { | ||
let initiator = Initiator::without_pk().expect("This fn call can not fail"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any specific reason for without_pk?
.safe_lock(|messages| messages.is_empty()) | ||
.unwrap() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a front method as well. Because sometime we might not want to consume the element to refer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm can you elaborate?
under which circumstances do you see it being useful to look at the message without popping it from the FIFO?
let _tp = common::start_template_provider(tp_addr.port()).await; | ||
let sniffer = common::start_sniffer(sniffer_addr, tp_addr).await; | ||
let _ = common::start_pool(Some(pool_addr), Some(sniffer_addr)).await; | ||
match sniffer.next_downstream_message() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this is the first ever test, we will be using it as a reference to write new tests in the future
so it makes sense to add some extra comments to guide us in the future
match sniffer.next_downstream_message() { | |
// assert message type as well as specific message fields | |
match sniffer.next_downstream_message() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we discussed something along these lines on PR Review call last week:
I wonder if we can encapsulate this match logic into some reusable pattern via macro magic?
just to avoid all this match
verbosity inside the tests
just a high level suggestion though, I'm not very good with macros so I'm not sure what would be really possible here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First thing which comes in my mind is to have a function like:
expect_downstream_message_field
which gets msg_type
, msg_field
, and msg_field_value
as parameters, and then it compares the next message got from the queue with the value passed as parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First thing which comes in my mind is to have a function like:
expect_downstream_message_field
which getsmsg_type
,msg_field
, andmsg_field_value
as parameters, and then it compares the next message got from the queue with the value passed as parameter.
this will probably be hard to achieve since we don't know the type for msg_field_value
before hand
that's why I believe macro is the only viable option
anyways my suggestion above doesn't need to be implemented on this PR, I can live with the match statements for now
as @jbesraa proposed, we should aim to merge this and move on to #1121
as we make progress on #1121, we can still come back and make adjustments to #1122
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally agree. To me this one is good to go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before merging, I still thing it would be good to add the comments on the suggestion that started this thread
} | ||
_ => panic!("Pool did not send a message to template provider"), | ||
}; | ||
assert!(sniffer.expect_upstream_message(MESSAGE_TYPE_SETUP_CONNECTION_SUCCESS)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert!(sniffer.expect_upstream_message(MESSAGE_TYPE_SETUP_CONNECTION_SUCCESS)); | |
// only assert message type | |
assert!(sniffer.expect_upstream_message(MESSAGE_TYPE_SETUP_CONNECTION_SUCCESS)); |
Blocked by #1155 and #1156
for more context: