Skip to content
This repository has been archived by the owner on Oct 14, 2024. It is now read-only.

Starknetv0.11 Ready for review #21

Merged
merged 23 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a8c077a
poseidon test
FawadHa1der Mar 22, 2023
9a6ec46
initial migration to use the new commitment scheme
FawadHa1der Mar 31, 2023
812f5db
update the abis and move to new deployed l1resolver/verifier contract
FawadHa1der Apr 4, 2023
fdc9ece
unit test updates
FawadHa1der Apr 4, 2023
c1eca9c
deploying poseidon only for local deployment
FawadHa1der Apr 5, 2023
5a98fb6
update core contract root used for testing
FawadHa1der Apr 5, 2023
75a7139
move the gateway abi interface to l1resolver filde
FawadHa1der Apr 5, 2023
f39e618
remove docker compose file
FawadHa1der Apr 5, 2023
21a40b0
update the test work area
FawadHa1der Apr 5, 2023
e2b70e9
update the abis
FawadHa1der Apr 5, 2023
79157f6
readme update
FawadHa1der Apr 5, 2023
6a29cf1
upgrading l2resolver from cairo0 to cairo1
FawadHa1der Apr 5, 2023
68c55e6
readme update
FawadHa1der Apr 5, 2023
2ed31fd
move StarknetCoreContractStub.sol to mocks folder
FawadHa1der Apr 6, 2023
772fa14
move StarknetCoreContractStub.sol to mocks folder
FawadHa1der Apr 6, 2023
48e462d
performance improvment for poseidon array, calldata to memory functio…
FawadHa1der Apr 6, 2023
e68f642
Add more comments to make explicit the puprose of the starknetcoercon…
FawadHa1der Apr 6, 2023
1135443
adding more poseidon array tests
FawadHa1der Apr 6, 2023
855a361
adding goerli deployment constants
FawadHa1der Apr 6, 2023
2a66654
readme updates with links to etherscan and voyager
FawadHa1der Apr 6, 2023
4502104
updated deployment address for new verifier/resolver on goerli
FawadHa1der Apr 7, 2023
d2bf5f0
updated deployment address for new verifier/resolver on goerli
FawadHa1der Apr 7, 2023
f58aa9f
update the new abis
FawadHa1der Apr 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ yarn install
cp .env.example .env
npx hardhat run <--network yournetwork > scripts/deploy.ts
```
SNL1ResolverStub.sol inherits from SNStateProofVerifier.sol. On goerli Pedersen hash contract is already deployed at 0x1a1eB562D2caB99959352E40a03B52C00ba7a5b1

SNL1ResolverStub.sol inherits from SNStateProofVerifier.sol and has been deployed to Goerli at 0xBB49c34D4d92aC3207d589657fAC14186a470116

On goerli Pedersen hash contract is already deployed at 0x1a1eB562D2caB99959352E40a03B52C00ba7a5b1
Copy link

Choose a reason for hiding this comment

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

could we verify this contract?

Copy link
Contributor Author

@FawadHa1der FawadHa1der Apr 6, 2023

Choose a reason for hiding this comment

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

Yes 0xBB49c34D4d92aC3207d589657fAC14186a470116 has been verified but this will change due to the changes as part of this review


Poseidon3(starkewares version) contracts EVM code has ben generated from the following repo and deployed on Goerli at 0x84d43a8cbEbF4F43863f399c34c06fC109c957a4.
Copy link

Choose a reason for hiding this comment

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

could we add links to goerli etherscan to these contracts' addresses?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link

Choose a reason for hiding this comment

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

same here: could we verify this contract?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pedersen contract is not so simple to verify, I have created an issue here -> #22


https://github.com/NethermindEth/circomlibjs/

## Run contract tests
From the root folder run the following
Expand Down Expand Up @@ -60,4 +67,4 @@ Help taken from existing implementaion of optimism related solution at https://g


# Disclaimer
None of the contracts have been audited and shoud not be used in production.
None of the contracts have been audited and should not be used in production.
Binary file removed contracts/.DS_Store
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove this file as this is not required.

Binary file not shown.
64 changes: 32 additions & 32 deletions contracts/contracts/SNResolverStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

// the gateway service will implement thhis interface. The gateway will be responsible for fetching the proof from the starknet network
interface IStarknetResolverService {
function addr(
bytes32 node
) external view returns (StarknetCompositeStateProof memory proof);
}

// Starknet Proof Verifier. This contract verifies a Starknet proof for a contract and a storage address/value
contract SNResolverStub is SNStateProofVerifier, ERC165 {
string[] public gateways;
Expand All @@ -24,7 +31,7 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
bytes extraData
);

uint256 constant MASK_250 = (2**250) - 1; // to simulate sn_keccak
uint256 constant MASK_250 = (2 ** 250) - 1; // to simulate sn_keccak
uint256 constant storageVarName =
0x29539a1d23af1810c48a07fe7fc66a3b34fbc8b37e9b3cdb97bb88ceab7e4bf; // sn_keccak of 'resolver' in https://github.com/starknet-id/ens_resolver/blob/3577d3bf3e309614dbec16aca56b7cade2bac949/src/main.cairo#L7

Expand All @@ -35,12 +42,14 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {

function initialize(
address pedersenAddress,
address poseidonAddress,
address _starknetCoreContractAddress,
string[] memory _gateways,
uint256 _l2resolver
) public initializer {
SNStateProofVerifier.initialize(
pedersenAddress,
poseidonAddress,
_starknetCoreContractAddress
);
gateways = _gateways;
Expand All @@ -55,11 +64,9 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
}

// returns the address of the storage within the starknet l2 resolver contract
function calculateDomainStorageVarAddressFor(uint256 domain)
internal
view
returns (uint256)
{
function calculateDomainStorageVarAddressFor(
uint256 domain
) internal view returns (uint256) {
return hash(storageVarName, domain);
}

Expand All @@ -70,11 +77,10 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
);
}

function addr(bytes32 node, uint256 coinType)
public
view
returns (bytes memory)
{
function addr(
bytes32 node,
uint256 coinType
) public view returns (bytes memory) {
if (coinType == 60) {
// replicated logic to demonstrate CCIP reslution on the ens app, as starknet address will not resolve so we are just resolving eth address.
// 60 for eth address
Expand All @@ -93,11 +99,10 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
}
}

function _addr(bytes32 node, bytes4 selector)
private
view
returns (uint256)
{
function _addr(
bytes32 node,
bytes4 selector
) private view returns (uint256) {
uint256 starknetNode = uint256(node) & MASK_250;

bytes memory callData = abi.encodeWithSelector(
Expand Down Expand Up @@ -128,11 +133,10 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
return uint256ToBytes(_addrWithProof(response, extraData));
}

function _addrWithProof(bytes calldata response, bytes calldata extraData)
internal
view
returns (uint256)
{
function _addrWithProof(
bytes calldata response,
bytes calldata extraData
) internal view returns (uint256) {
StarknetCompositeStateProof memory proof = abi.decode(
response,
(StarknetCompositeStateProof)
Expand All @@ -147,6 +151,7 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
proof.contractData.storageVarAddress = storageVarAdress;
uint256 starknetAddress = this.verifiedStorageValue(
proof.blockNumber,
proof.classCommitment,
proof.contractData,
proof.contractProofArray,
proof.storageProofArray
Expand All @@ -155,12 +160,9 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
return starknetAddress;
}

function supportsInterface(bytes4 interfaceID)
public
pure
override
returns (bool)
{
function supportsInterface(
bytes4 interfaceID
) public pure override returns (bool) {
return
interfaceID == ADDR_INTERFACE_ID ||
interfaceID == ADDRESS_INTERFACE_ID;
Expand All @@ -180,9 +182,7 @@ contract SNResolverStub is SNStateProofVerifier, ERC165 {
}
}

function _authorizeUpgrade(address newImplementation)
internal
override
onlyOwner
{}
function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
}
113 changes: 83 additions & 30 deletions contracts/contracts/SNStateProofVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ struct StarknetProof {
// includes contract proof and state/storage proof for a partciular starknet block
struct StarknetCompositeStateProof {
int256 blockNumber;
uint256 classCommitment;
ContractData contractData;
StarknetProof[] contractProofArray;
StarknetProof[] storageProofArray;
}

interface IStarknetResolverService {
function addr(bytes32 node)
external
view
returns (StarknetCompositeStateProof memory proof);
interface PoseidonHash3 {
// this is the hades permutation function. TODO update the name when goerli eth is not so expensive
function poseidon(
uint256[3] calldata input
) external view returns (uint256[3] calldata);
Copy link

Choose a reason for hiding this comment

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

I'm a bit surprised by calldata here. This is only available for function arguments of external functions.

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 am not so sure about that, you can use it for contract to contract calls and it works fine, its a little bit cheaper I think(but could be wrong) and one should be aware of the downsides(immutability etc etc). But you are right that that is not the convention and probably not worth the confusion it will cause. I have updated it to memory. Interesting question in general though. Happy to be corrected on this understanding

}

// Starknet Core Contract Minimal Interface
Expand All @@ -76,7 +77,10 @@ contract SNStateProofVerifier is
{
uint256 private constant BIG_PRIME =
3618502788666131213697322783095070105623107215331596699973092056135872020481;
uint256 private constant FELT_FOR_STARKNET_STATE_V0 =
28355430774503553497671514844211693180464; // short_str_to_felt for "STARKNET_STATE_V0"
PedersenHash public pedersen;
PoseidonHash3 public poseidon;
StarknetCoreContract public starknetCoreContract;

/// @custom:oz-upgrades-unsafe-allow constructor
Expand All @@ -86,28 +90,25 @@ contract SNStateProofVerifier is

function initialize(
address pedersenAddress,
address poseidonAddress,
address _starknetCoreContractAddress
) public initializer {
pedersen = PedersenHash(pedersenAddress);
poseidon = PoseidonHash3(poseidonAddress);
starknetCoreContract = StarknetCoreContract(
_starknetCoreContractAddress
);
__Ownable_init();
__UUPSUpgradeable_init();
}

function _authorizeUpgrade(address newImplementation)
internal
virtual
override
onlyOwner
{}

function hashForSingleProofNode(StarknetProof memory proof)
private
view
returns (uint256)
{
function _authorizeUpgrade(
address newImplementation
) internal virtual override onlyOwner {}

function hashForSingleProofNode(
StarknetProof memory proof
) private view returns (uint256) {
uint256 hashvalue = 0;
if (proof.nodeType == NodeType.BINARY) {
hashvalue = hash(
Expand All @@ -123,6 +124,27 @@ contract SNStateProofVerifier is
return hashvalue;
}

// based on https://docs.starknet.io/documentation/architecture_and_concepts/Hashing/hash-functions/
function poseidonHashMany(
uint256[] memory elems
) public view returns (uint256) {
uint256[3] memory state = [uint256(0), uint256(0), uint256(0)];
Copy link

Choose a reason for hiding this comment

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

maybe I'm missing something, but I feel that the code here never updates state[2].

Copy link

Choose a reason for hiding this comment

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

oh, it's probably updated by the function poseidon.poseidon(state), right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes that is correct


for (uint256 i = 0; i < elems.length / 2; i++) {
state[0] = (state[0] + elems[2 * i]) % BIG_PRIME;
state[1] = (state[1] + elems[2 * i + 1]) % BIG_PRIME;
state = poseidon.poseidon(state);
}
Copy link

Choose a reason for hiding this comment

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

alternative to avoid division and multiplication:

for (uint256 i = 0; i < elems.length; i += 2) {
    state[0] = (state[0] + elems[i]) % BIG_PRIME;
    state[1] = (state[1] + elems[i + 1]) % BIG_PRIME;
    state = poseidon.poseidon(state);
}

Copy link
Contributor Author

@FawadHa1der FawadHa1der Apr 6, 2023

Choose a reason for hiding this comment

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

but with (i < elems.length -1) but great suggestion, thanks Massil


uint256 rem = elems.length % 2;
if (rem == 1) {
state[0] = (state[0] + elems[elems.length - 1]) % BIG_PRIME;
}
state[rem] = (state[rem] + (1)) % BIG_PRIME;

return poseidon.poseidon(state)[0];
}

// this functions connects the contract state root with value of leaf node in the contract proof.
// state_hash = H(H(H(class_hash, contract_root), contract_nonce), RESERVED)
function stateHash(
Expand All @@ -147,11 +169,10 @@ contract SNStateProofVerifier is
return hashes[0];
}

function convertToBytes(uint256 x, uint256 y)
private
pure
returns (bytes memory)
{
function convertToBytes(
uint256 x,
uint256 y
) private pure returns (bytes memory) {
bytes memory b = new bytes(64);
assembly {
mstore(add(b, 32), x)
Expand All @@ -168,6 +189,7 @@ contract SNStateProofVerifier is
// Only supports verifiying a proof for a single storage variable value.
function verifiedStorageValue(
int256 blockNumber,
uint256 classCommitment,
ContractData calldata contractData,
StarknetProof[] calldata contractProofArray,
StarknetProof[] calldata storageProofArray
Expand Down Expand Up @@ -208,24 +230,23 @@ contract SNStateProofVerifier is
contractData.hashVersion
);

uint256 storageVarValue = verifyProof(
uint256 storageVarValue = verifyStorageProof(
contractData.contractStateRoot,
contractData.storageVarAddress,
storageProofArray
);

// the contract proof has to be verified against the state root committed on L1 in the Starknet Core Contract
uint256 stateRootCoreHash = starknetCoreContract.stateRoot();

require(
_stateHash != 0,
"stateroot hash is not fetched properly! revert"
);

// the contract proof has to be verified against the state root committed on L1 in the Starknet Core Contract
uint256 expectedStateHash = verifyProof(
stateRootCoreHash,
starknetCoreContract.stateRoot(),
contractData.contractAddress,
contractProofArray
contractProofArray,
classCommitment
);

require(
Expand All @@ -248,15 +269,48 @@ contract SNStateProofVerifier is
return aExtracted == b;
}

// overloaded/wrapper function around verifyProof, used to verify storage proof array where the class commitment does not apply
function verifyStorageProof(
uint256 rootHash,
uint256 path,
StarknetProof[] calldata proofArray
) public view returns (uint256 value) {
// classCommitment is 0 means we are verifying storage proof array
return verifyProof(rootHash, path, proofArray, 0);
}

// A generic method to verify a proof against a root hash and a path.
function verifyProof(
uint256 rootHash,
uint256 path,
StarknetProof[] calldata proofArray
StarknetProof[] calldata proofArray,
uint256 classCommitment // 0 means no class commitment
) public view returns (uint256 value) {
uint256 expectedHash = rootHash;
int256 pathBitIndex = 250; // start from the MSB bit index
if (classCommitment > 0) {
// https://docs.starknet.io/documentation/architecture_and_concepts/State/starknet-state/
require(
proofArray.length > 0,
"proofs must have atleast one element!"
);
Copy link

Choose a reason for hiding this comment

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

if we move the same check below above the if statement, we can remove this one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


uint256 calculatedContractStateRoot = hashForSingleProofNode(
proofArray[0]
); // the hash of first element in the proof array is the contract state root
uint256[] memory poseidonInput = new uint256[](3);
poseidonInput[0] = FELT_FOR_STARKNET_STATE_V0;
poseidonInput[1] = calculatedContractStateRoot;
poseidonInput[2] = classCommitment;

uint256 calculateStateCommitment = poseidonHashMany(poseidonInput);
require(
calculateStateCommitment == expectedHash,
"calculated state commitment doesn't match with the expected state commitment!"
);
// since we have already verified the first element in the proof array correctly hashes up to the state commitment, we can assume the hash of first element in the proof array is correct.
expectedHash = calculatedContractStateRoot;
}
require(
Copy link

Choose a reason for hiding this comment

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

we can move this check before the if statement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

proofArray.length > 0,
"proof array must have atleast one element."
Expand Down Expand Up @@ -296,7 +350,6 @@ contract SNStateProofVerifier is
expectedHash = proof.edgeProof.childHash;
int256 edgePathLength = int256(proof.edgeProof.length);
pathBitIndex -= edgePathLength;
console.log("pathBitIndex", uint256(pathBitIndex));
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/contracts/StarknetCoreContractStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ contract StarknetCoreContractStub {
*/
function stateRoot() external view returns (uint256) {
return
3321080970052968843805320357911538392446265524314601848332135665594593955343;
2793869633745137896926484933765493995916819896677591814984705621839603171245;
Copy link

Choose a reason for hiding this comment

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

what the meaning of this value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a comment at the top of the file mentioning its only to be used in tests. I have moved it into mocks folder to make it more explicit. Its mock contract used only unit tests

}

/**
Returns the current block number.
Copy link

Choose a reason for hiding this comment

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

the comment is not accurate. what does correspond to the block number 789146?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated the comment

*/
function stateBlockNumber() external view returns (int256) {
return 595364;
return 789146;
}
}
Loading