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

Commit

Permalink
Merge pull request #21 from NethermindEth/starknetv0.11
Browse files Browse the repository at this point in the history
Starknetv0.11 Ready for review

> LGTM
  • Loading branch information
FawadHa1der authored Apr 7, 2023
2 parents 9dcea5a + f58aa9f commit 4945f5d
Show file tree
Hide file tree
Showing 43 changed files with 9,105 additions and 2,714 deletions.
15 changes: 11 additions & 4 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 [0xF1979Ec7Fc554e83cF722978Cf73AE94381fBD92](https://goerli.etherscan.io/address/0xF1979Ec7Fc554e83cF722978Cf73AE94381fBD92)

On goerli Pedersen hash contract is already deployed at [0x1a1eB562D2caB99959352E40a03B52C00ba7a5b1](https://goerli.etherscan.io/address/0x1a1eB562D2caB99959352E40a03B52C00ba7a5b1)

Poseidon3(starkewares version) contracts EVM code has ben generated from the following repo and deployed on Goerli at [0x84d43a8cbEbF4F43863f399c34c06fC109c957a4](https://goerli.etherscan.io/address/0x84d43a8cbebf4f43863f399c34c06fc109c957a4).

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

## Run contract tests
From the root folder run the following
Expand All @@ -33,7 +40,7 @@ REPORT_GAS=true npx hardhat test
```

## Build and deploy the gateway
From the root folder run the following. L2/Starknet resolver is already deployed on goerli at 0x7412b9155cdb517c5d24e1c80f4af96f31f221151aab9a9a1b67f380a349ea
From the root folder run the following. L2/Starknet resolver is already deployed on goerli at [0x7412b9155cdb517c5d24e1c80f4af96f31f221151aab9a9a1b67f380a349ea](https://goerli.voyager.online/contract/0x07412b9155cdb517c5d24e1c80f4af96f31f221151aab9a9a1b67f380a349ea3#readContract)
```shell
cd gateway
yarn install
Expand All @@ -51,7 +58,7 @@ Current implementation is already deployed for goerli at https://starknetens.ue.


## L2 Resolver
L2 resolver is a git subtree of https://github.com/starknet-id/ens_resolver. This has been deployed to 0x7412b9155cdb517c5d24e1c80f4af96f31f221151aab9a9a1b67f380a349ea on goerli
This has been upgraded to cairo1 and deployed to [0x7412b9155cdb517c5d24e1c80f4af96f31f221151aab9a9a1b67f380a349ea](https://goerli.voyager.online/contract/0x07412b9155cdb517c5d24e1c80f4af96f31f221151aab9a9a1b67f380a349ea3#readContract) on goerli

# Acknowledgements
Pedersen Hash implementation has been borrowed from https://github.com/Kelvyne/starknet-storage-proof-solidity. Many Thanks!
Expand All @@ -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
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 {}
}
114 changes: 81 additions & 33 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] memory input
) external view returns (uint256[3] memory);
}

// 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)];

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

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,19 +269,47 @@ contract SNStateProofVerifier is
return aExtracted == b;
}

// A generic method to verify a proof against a root hash and a path.
function verifyProof(
// 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) {
uint256 expectedHash = rootHash;
int256 pathBitIndex = 250; // start from the MSB bit index
// 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,
uint256 classCommitment // 0 means no class commitment
) public view returns (uint256 value) {
require(
proofArray.length > 0,
"proof array must have atleast one element."
);
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/
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;
}

bool isRight = true;
for (uint256 i = 0; i < proofArray.length; i++) {
Expand Down Expand Up @@ -296,7 +345,6 @@ contract SNStateProofVerifier is
expectedHash = proof.edgeProof.childHash;
int256 edgePathLength = int256(proof.edgeProof.length);
pathBitIndex -= edgePathLength;
console.log("pathBitIndex", uint256(pathBitIndex));
}
}
}
Expand Down
Loading

0 comments on commit 4945f5d

Please sign in to comment.