Open geoknee opened 11 months ago
The watchtower can be smart:
checkpoint
in response, thereby allowing the channel to continue executingchallenge
in response and also "cancel running objectives" to bring the channel safely to a closeThe decision about whether a counterparty is "bad" should arguably not be decided per channel. If 0xDave is bad, let's act accordingly in all channels we have with them.
Initially, we will build this into the node
(maybe it lives inside the chain service).
Longer term, we may want to extract it into a separate service that could be run remotely by a 3rd party.
See suggestion above about the API which might make this easier (the watchtower never needs to contact the node, the node just keeps the watchtower up to date all the time).
it can decide if it is economically sensible to spend gas on responding to a given challenge
Even if it's not economically feasible I think there may also be motivation to "punish" the counterparty. Let's say we have the following two states between Alice and Bob:
s0
:Turn: 0 Alice Bal: 100 Bob Bal: 0
s1
:Turn: 1 Alice Bal: 90 Bob Bal: 10
Let's say Bob sees a challenge from Alice with s0
. If Bob responds with s1
he prevents himself from losing 10
and Alice from holding onto 10
. Bob might be willing to pay 12
in gas to "punish" Alice
and force her to pay the 10
she promised.
Economic feasibility will also vary over time due to fluctuations in gas prices. If the network is busy now it might not make economic sense to respond to a challenge now. However it's possible that in the future (before the challenge timeout) gas prices drop and it becomes economically sensible to respond. It can also depend on how long we'd be willing to wait for our tx to be confirmed. We may be able to use a lower gas price and just wait longer (slightly related to https://github.com/statechannels/go-nitro/issues/1502) to keep things feasible.
However these are kind of edge cases, so for the initial implementation it's probably OK for the watchtower
to perform a simple calculation based on whatever the gas oracle returns(when transactions are submitted to geth without a gas price, it uses the gas oracle to determine them). Alternatively we could just always respond ignoring whether or not it's economically feasible for now. Or have both options available controlled by a config value.
it risks an unfavourable outcome finalizing (that is to say, I have the ability to challenge with a more favourable outcome).
Right now with just the ConsensusApp
and VirtualPaymentApp
I think we're OK to do this, since the Outcome
is all we care about. However I don't think this would work for any arbitrary IForceMoveApp
since their rules may mean it makes sense to respond even if the Outcome
hasn't changed in our favour. IE: There could be a tic-tac-toe IForceMoveApp
where the outcome doesn't change until the game is completed, but we still want to respond with our winning move.
Not something we'll run into soon, but it's probably worth designing the watchtower so it'll be easy to expand or add to these checks for different IForceMoveApp
implementations.
or challenge in response and also "cancel running objectives" to bring the channel safely to a close
Could the node
handle cancelling objectives when it detects a challenge event from it's own SC address? Otherwise in a future where the watchtower
is separate, it's going to have to call anode
API to cancel the objectives.
I also just realized that calling challenge
means the watchtower
must have a copy of the SC key, so it can sign the challenger sig. With checkpoint
we don't need to sign anything special, we just submit the signed states that the watchtower
has been given.
I also just realized that calling challenge means the watchtower must have a copy of the SC key, so it can sign the challenger sig. With checkpoint we don't need to sign anything special, we just submit the signed states that the watchtower has been given.
Good point!
I think probably out of scope for now but maybe we could allow participants to nominate a 3rd party watchtower account which can sign challenge messages on its behalf. Or, the node could provide the relevant signature to the watchtower in anticipation of it needing to send a challenge.
https://docs.statechannels.org/protocol-tutorial/0070-finalizing-a-channel/#call-challenge
Or, the node could provide the relevant signature to the watchtower in anticipation of it needing to send a challenge.
That could be a nice solution! Where you provide a supported state to the watchtower
you could provide the challenge signature for that supported state, meaning the watchtower
is always capable of challenging but doesn't have to own the SC key.
Regardless of the execution environment, the go-nitro
node
needs to call this method whenever a new state becomes supported in its store. Then, the watchtower will automatically watch the chain and respond to challenges.
One thing I just realized is currently a go-nitro
node
doesn't really know when a state becomes supported; we only check if state is supported within our protocols. So we'll need to add some kind of mechanism to catch this. We'll also need to trigger RegisterSupportedState
whenever we add a voucher to our voucher manager.
I think if we:
o.Crank
for any new supported states(by checking if the LatestSupportedStatTurnNum has been increased). This will catch any states that become supported by our protocols signing a state.o.Update
for any new supported states. This will catch any states that become supported by receiving a signed state from a peer.RegisterSupportedState
when we receive a voucher.This should let the engine
catch any change to a supported state and call RegisterSupportedState
appropriately.
From the grant proposal:
The on-chain challenge method (and associated go-nitro module in Milestone 3) gives power to participants to “take each other to court”.
This is an important protection, but one that invites malicious / false claims. Although the protocol will swiftly reject any challenge that does not have sufficient “support” — for example, a unanimous round of signatures from all parties — there is still a risk. A malicious or faulty peer can submit stale challenges (meaning for a “supported state” which has been superseded by more recent agreements) and thereby lay claim to funds “already spent” off-chain.
We will enhance the go-nitro client to detect challenges on chain and respond with a proof of the newer agreement. This will lead to the stale challenge being cancelled (”thrown out of court”).
Deliverables:
A new protocol defend which is spawned automatically by go-nitro when: a challenge goes on chain it was not generated by this client it risks an unfavourable outcome finalizing (that is to say, I have the ability to challenge with a more favourable outcome). An integration test running in testground which shows a go-nitro client responding appropriately to a malicious challenge
An extension to this would be to extract this module out into a sidecar / process with a very simple RPC API.:
Regardless of the execution environment, the go-nitro
node
needs to call this method whenever a new state becomes supported in its store. Then, the watchtower will automatically watch the chain and respond to challenges.