UpstreamData / pyasic

A simplified and standardized interface for Bitcoin ASICs.
https://docs.pyasic.org
Apache License 2.0
98 stars 53 forks source link

Reboot Command on Whatsminer #112

Closed patrick-patel closed 8 months ago

patrick-patel commented 8 months ago

Describe the bug Hi,

I setup a python script that pulls data from my whatsminer M31S+ (successfully), but have an issue running the reboot command.

I have been attempting to issue a reboot command and keep getting False returned. I have heard that you need to update the password on the whatsminer for the api to allow for control, so I did this and updated the default password in pyasic settings to match, but still no luck. Any idea what I may be missing, or is this a bug others are experiencing?

Thanks, Patrick

Desktop (please complete the following information):

Miner Information (If applicable):

b-rowan commented 8 months ago

Can you factory reset that miner to remove the password setting (or just set it back to admin), then use the default setting on the latest version of whatsminer? We recently put up wmt.pyasic.org, which should be able to auto unlock your miner as part of any privileged command, so anything newer than 0.53.0 should work with the default settings.

b-rowan commented 8 months ago

It's also entirely possible its a persistent bug from an earlier version of pyasic if that isn't the issue, if this doesn't work, I have some other ideas, since the way whatsminers return data on a few commands is really buggy. I think we were actually running into an issue where it didn't send the command at all, but I have no idea why.

patrick-patel commented 8 months ago

Can you factory reset that miner to remove the password setting (or just set it back to admin), then use the default setting on the latest version of whatsminer? We recently put up wmt.pyasic.org, which should be able to auto unlock your miner as part of any privileged command, so anything newer than 0.53.0 should work with the default settings.

Updated to the latest pyasic, upgraded miner firmware, changed password back to admin and still not working.

Is wmt.pyasic.org being used in the pyasic code base, or is it something I need to implement independently? If so, how do I go about implementing it?

b-rowan commented 8 months ago

You don't need to implement it, it is automatic if it finds a privilege issue.

This seems like it might be the failing to send issue I mentioned, which is quite a bit more serious and very complex. Some of the other devs on this project are looking into it to see if we have any ideas, but right now this is a pretty gnarly bug.

patrick-patel commented 8 months ago

Not sure if this helps, but the get_token command is working properly (returns salt and new salt), but self._send_bytes is returning a byte string containing an empty dictionary. So _send_bytes is either not working or enc_command is formatted improperly.

Screenshot 2024-02-21 at 10 18 14 AM
b-rowan commented 8 months ago

Not sure if this helps, but the get_token command is working properly (returns salt and new salt), but self._send_bytes is returning a byte string containing an empty dictionary. So _send_bytes is either not working or enc_command is formatted improperly.

Close, but not quite. What seems to be happening is that the miner closes/refuses the remote side of the connection before _send_bytes can initialize the writer. This is happening in this block - https://github.com/UpstreamData/pyasic/blob/a582ee63a0ac54e68c3ae0e51f2ffa25848a9e68/pyasic/rpc/base.py#L205-L214

I cannot for the life of me figure out why it refuses connection seemingly only when some commands are sent, EG reboot, while others that should have a very similar call stack (set_power_limit for example, which does get_token then adjust_power_limit, as opposed to reboot which calls get_token then reboot). Why there is any difference in these commands, I have no idea, because they both run through send_privileged_command for the BTMinerRPCAPI, which should handle all this the same way.

I am not a debugging expert by any means, so if you have any ideas or want to play with it, that would be helpful. Like I said, I have talked to some other devs who are trying to look at it as well, but I'm stumped.

b-rowan commented 8 months ago

Literally any of these functions should function the exact same, they are all basically calling the same stuff - https://github.com/UpstreamData/pyasic/blob/a582ee63a0ac54e68c3ae0e51f2ffa25848a9e68/pyasic/miners/backends/btminer.py#L135-L200

patrick-patel commented 8 months ago

None of them are working for me, I just tested stop_mining for example and self._send_bytes is returning the same byte string containing an empty dictionary.

b-rowan commented 8 months ago

Ok, so an issue with the call stack somehow. I wonder if you were to re-implement part of that somehow with timeouts or something if it would work? This function for example I find usually works - https://github.com/UpstreamData/pyasic/blob/a582ee63a0ac54e68c3ae0e51f2ffa25848a9e68/pyasic/miners/backends/btminer.py#L273-L280

patrick-patel commented 8 months ago

Ok, so an issue with the call stack somehow. I wonder if you were to re-implement part of that somehow with timeouts or something if it would work? This function for example I find usually works -

https://github.com/UpstreamData/pyasic/blob/a582ee63a0ac54e68c3ae0e51f2ffa25848a9e68/pyasic/miners/backends/btminer.py#L273-L280

That command did return true, but I don't think WM have a power limit command and self._send_bytes is returning the same byte string containing an empty dictionary.

b-rowan commented 8 months ago

So it may still be failing. Your miner does support power limit setpoints, if you run the whatsminer tool you should see it being set to whatever value you sent if it succeeded, but it still appears to be failing.

So a general issue with refusal across RPC commands, maybe its an issue with the send_privileged_command implementation...

@jpcomps Maybe a good starting point? Wireshark shows packets consistent with this, it never manages to open the second connection, maybe we are re-attempting too fast or something?

patrick-patel commented 8 months ago

Narrowed down the byte string containing an empty dictionary issue to the _send_bytes func, here is the output

_send_bytes trying asyncio.open_connection _send_bytes reader, writer error: [Errno 61] Connect call failed ('192.168.1.38', 10) data: b'{}'

patrick-patel commented 8 months ago

Found it!

In _send_privileged_bytes

data = await self._send_bytes(enc_command, timeout)

needs to include the port

data = await self._send_bytes(enc_command, self.port, timeout)

b-rowan commented 8 months ago

There is no way I'm that dumb. Please tell me I'm not that dumb. 🤦‍♂️

I'll do some testing...

b-rowan commented 8 months ago

If that really is the issue, feel free to open a PR for that, can get you contributor status for fixing this. If that is what it is, I can't believe I missed that...

patrick-patel commented 8 months ago

If that really is the issue, feel free to open a PR for that, can get you contributor status for fixing this. If that is what it is, I can't believe I missed that...

Sounds good!

My reboot commands are going through now so I believe that solved it. It was passing the timeout at port, so it was trying to use port 10.

b-rowan commented 8 months ago

I found it. Passed timeout as positional rather than keyword... This is what it should be -

            data = await self._send_bytes(enc_command, timeout=timeout)
jpcomps commented 8 months ago

can confirm, fixes the commands. great catch there @patrick-patel

b-rowan commented 8 months ago

Fixed in 0.53.2. Thanks for finding this, I was looking at this just last week and was so confused about it. So glad a fresh set of eyes was able to figure this out, so huge thanks for that!

patrick-patel commented 8 months ago

Happy to help. Great work on this, this is a game changer for enabling plebs to build miner control software.

Patrick Patel