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