Skip to content
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

Contracts SATP compatability #427

Merged
merged 4 commits into from
Apr 7, 2022

Conversation

nksazonov
Copy link
Contributor

@nksazonov nksazonov commented Mar 29, 2022

Contracts changes:

  • appDefinition and challengeDuration are now used in channelId computations (which includes chainID, participants, channelNonce, appDefinition, challengeDuration)
  • State struct representation has changed: bytes appPart and bytes outcome instead of bytes32 appPartHash and bytes32 outcomeHash
  • in ForceMove State is encoded in the same way as in go-nitro - not as a struct, but as a set of State's fields. Also order of arguments has changed.
  • function signatures, EmbeddedApplication.sol were updated to support changes above
  • additional step of decoding added to CountingApp to correspond to go-nitro
  • tests updated to support changes above. Also some ts types fixed and imports updates to support newer packages versions

TODO:

  • change validTransition to latestSupportedState in ForceMoveApp.sol
  • update function signatures and tests to support that update

@nksazonov nksazonov force-pushed the feature/contracts-satp branch 3 times, most recently from 5bc8660 to e378615 Compare March 29, 2022 17:27
@nksazonov
Copy link
Contributor Author

Trying to clarify what is need to be changed related to latestSupportedState implementation. As I understand from SATP paper, all state transition checks should be done in it in ForceMoveApp. If so, should latestSupportedState replace _requireStateSupportedBy logic currently implemented in ForceMove?

@andrewgordstewart
Copy link
Contributor

As I understand from SATP paper, all state transition checks should be done in it in ForceMoveApp. If so, should latestSupportedState replace _requireStateSupportedBy logic currently implemented in ForceMove?

Yes, that's correct.

In the current nitro-protocol, the adjudicator holds a strong opinion about what makes a state "supported". "Supported" should mean: the adjudicator is willing

SATP suggests delegating that responsibility latestSupportedState. This removes a guard rail built into the protocol, but is a lot more flexible. We can extract the current code into a library that different latestSupportedState implementations can use, if that's useful.

As an, imagine a 3-party channel between Alice, Irene and Bob. The current rules say that at any point in time, either Alice can force an update on chain or Bob can; it's not possible to allow both of them to force an update (which is useful in a channel when Alice and Bob are trading between themselves, as opposed to Alice always giving assets to Bob).

Here's an example of how these kinds of rules might be written (replace validSupport with latestSupportedState). Is this example clear? (You can leave comments here if you like!)

@nksazonov
Copy link
Contributor Author

The example you have sent is pretty clear, thanks.

Copy link
Contributor

@geoknee geoknee left a comment

Choose a reason for hiding this comment

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

Thanks for this @nksazonov! I'm still working through the changes but thought it was a good idea to share a couple of points early on :-)

Comment on lines +1 to +28
//SPDX-License-Identifier: MIT License
pragma solidity 0.7.4;

library ECRecovery {

/**
* @dev Recover signer address from a message by using his signature
* @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
* @param v v part of a signature
* @param r r part of a signature
* @param s s part of a signature
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {

// Version of signature should be 27 or 28, but 0 and 1 are also possible versions
if (v < 27) {
v += 27;
}

// If the version is correct return the signer address
if (v != 27 && v != 28) {
return (address(0));
} else {
return ecrecover(hash, v, r, s);
}
}

}
Copy link
Contributor

Choose a reason for hiding this comment

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

This file is new, but doesn't appear to be used anywhere?

It looks to me like a wrapper for ecrecover which may add some extra safety. Is that right? Is there something equivalent in the openzeppelin library that we could use https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Edit: I see that it is used inside ForceMove.sol (didn't spot it because GitHub hides large diffs!). Still I would like to understand the thinking behind introducing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is a wrapper for ecrecover indeed. The reason this file was introduced is inconsistency of returned v signature value in go-ethereum (as described here) and its difference with what pure ecrecover solidity function expects (i.e. v to be either 27 or 28).

We thought of using fault-proof OpenZeppelin analogy, but it has a lot of unnecessary tweaks and, more importantly, requires modification to support what we need. Thus it was simpler and clearer to make a library with the same modifications that should have been done to OpenZeppelin contract.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. I wonder if it is better to fix this problem in the go-nitro client code, rather than on chain? That seems to be the advice from the go-ethereum team. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. We will try to fix it from go-nitro client code ASAP.

nitro-protocol/contracts/interfaces/IForceMove.sol Outdated Show resolved Hide resolved
"types": ["node", "jest"],
"target": "es2018",
"module": "commonjs",
"strict": true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Good to see "strict": true. Does tsc . work, or are there errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tsc --project ./ from root folder produces

Errors  Files
     7  node_modules/@types/jest/index.d.ts:35
     2  scripts/postdeploy.ts:6

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe outside the scope of this work, but it might be nice to get to zero errors. Also, we want to gitignore the resulting dist folder.

Comment on lines 263 to 270
await (await ForceMove.setStatus(getChannelId(twoPartyFixedPart), HashZero)).wait();
});
// FIX: even if dropping channel status before each test, turn nums from prev tests are saved and can cause reverts
it.each`
description | appData | outcome | turnNums | challenger
${'challenge(0,1) accepted'} | ${[0, 0]} | ${[]} | ${[0, 1]} | ${1}
${'challenge(1,2) accepted'} | ${[0, 0]} | ${[]} | ${[1, 2]} | ${0}
${'challenge(1,2) accepted, MAX_OUTCOME_ITEMS'} | ${[0, 0]} | ${largeOutcome(MAX_OUTCOME_ITEMS)} | ${[1, 2]} | ${0}
${'challenge(3,4) accepted, MAX_OUTCOME_ITEMS'} | ${[0, 1]} | ${largeOutcome(MAX_OUTCOME_ITEMS)} | ${[3, 4]} | ${0}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to understand this a little better. What's going wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The last test fails due to transaction revert with reason turnNumRecord not increased, which corresponds to CountingApp.sol. Both changing turnNums to [3, 4] and removing middle test help to avoid this revert. Unfortunately, I was not able to debug CountingApp.sol (via console.log) behavior with this tests because ganache is created dynamically and even setting env args to enable logging does not help.

Comment on lines +5 to +12
/**
* @dev The IForceMoveApp interface calls for its children to implement an application-specific validTransition function, defining the state machine of a ForceMove state channel DApp.
*/
interface IForceMoveApp {
struct VariablePart {
bytes outcome;
bytes appData;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@nksazonov This struct needs updating, too, I think?

Take a look at 744dbbe and 3b4bfba

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I've also realized it needs updating to support latestSupportedState.

@geoknee
Copy link
Contributor

geoknee commented Apr 6, 2022

@nksazonov thanks a lot for all these contributions! We would like to propose the following workflow:

  1. We will merge this PR as is very soon.
  2. We will make issues with the tag "on-chain" to manage any immediate issues arising from this PR (i.e. the comments above).
  3. We will add you and any other openware devs as external collaborators so you can make branches and raise pull requests in this repo.
  4. We can assign issues to prevent duplicating work.
  5. We will setup continuous integration (see Add node.js github action #487) as soon as possible. One of the outstanding pieces of work will be to get the tests passing 100% (I think we are very close).
  6. We may add some other CI checks such as linting, prettier and so on.

How does this sound to you? Please let me know your thoughts.

@geoknee
Copy link
Contributor

geoknee commented Apr 6, 2022

There is also another thing I would like to resolve before merging!

  1. We need to resolve an issue with the readme files. Most of our team use Mac OSX, which has a case insensitive file system. When I check out this branch on a Mac, I immediately see some unstaged changes to ./nitro-protocol/README.md. These changes are reverting the readme back to the old nitro-protocol readme from the statechannels monorepo. If I restore the file (reject the changes), I immediately get some unstaged changes to ./nitro-protocol/readme.md, changing that file to your new readme. It's hard to tell on my system, but I guess there are two files there which differ only in the casing? In that case, deleting one of them ought to solve the problem?

@nksazonov
Copy link
Contributor Author

How does this sound to you? Please let me know your thoughts.

@geoknee sounds like an amazing collaboration start!
Before merging I propose we resolve all minor problems we have discussed (e.g. comments and gas limit).

@geoknee
Copy link
Contributor

geoknee commented Apr 6, 2022

Before merging I propose we resolve all minor problems we have discussed (e.g. comments and gas limit).

Sounds good to me!

@nksazonov
Copy link
Contributor Author

It's hard to tell on my system, but I guess there are two files there which differ only in the casing?

Yes, this is the reason. Deleting ours should solve the problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants