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

Release #180

Merged
merged 37 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
afe9576
indexer: fix build warning
ptisserand Mar 1, 2024
2882ce2
indexer: fix blockstamp bug for pending withdraw on L1
ptisserand Mar 14, 2024
98ccfa3
indexer: fix address padding issue.
ptisserand Mar 14, 2024
df34036
CI: use tool-versions instead of hardcoded scarb version
ptisserand Feb 6, 2024
f06cad4
CI: add starknet contract testing with snforge
ptisserand Feb 7, 2024
cef5bc7
CI: fix path to ethereum contracts
ptisserand Feb 7, 2024
3a52b8e
add whitelist support for starknet
ptisserand Feb 9, 2024
13ef479
Deployer is now an external library to reduce ethereum contract size
ptisserand Feb 9, 2024
6acf259
starknet: EthAddress Hash impl added in cairo 2.3.0
ptisserand Feb 12, 2024
3a07373
starknet: move event definitions into interfaces.cairo
ptisserand Feb 13, 2024
dabc57f
ethereum: add enable switch for bridge
ptisserand Feb 15, 2024
52a756a
starknet: add enable switch for bridge
ptisserand Feb 15, 2024
c38de3e
starknet: add tokenURI support in withdraw_auto_from_l1
ptisserand Feb 16, 2024
464c3d3
ethereum: add test case for internal function detection
ptisserand Feb 16, 2024
6d6b178
ethereum: byte array remaining word is NOT aligned on MSB
ptisserand Feb 16, 2024
156b491
blockchain: update README
ptisserand Feb 17, 2024
ef3c6dd
fix/audit: [C-01] Anyone can withdraw any token held by the L1 bridge
ptisserand Mar 19, 2024
b86fc94
fix/audit: add test case to ensure tokens from same L2 collection are…
ptisserand Mar 19, 2024
3973d2e
fix/audit: [C-02] Impossible to withdraw L1 native tokens back on L1 …
ptisserand Mar 20, 2024
6e08250
fix/audit: [L-03] Wrong selector
ptisserand Mar 20, 2024
b732ef7
starknet: update to cairo 2.6.3 & snforge 0.19.0
ptisserand Mar 26, 2024
e2b06a9
starknet: update erc7721_bridgeable to use OZ ERC721 component
ptisserand Mar 26, 2024
d009b57
ethereum: use StarknetMessagingLocal from katana 0.5.0
ptisserand Feb 29, 2024
1581479
ethereum: add support to set mapping L1<->L2
ptisserand Mar 5, 2024
c767e74
starknet: add support to set mapping L1<->L2
ptisserand Mar 6, 2024
45b694c
fix/audit: [M-01] Unchecked parameter
ptisserand Mar 27, 2024
7102092
fix/audit: [M-02] Unchecked parameter
ptisserand Mar 27, 2024
137fcc0
fix/audit: [M-03] Unchecked parameter
ptisserand Mar 27, 2024
ceb7510
starknet: add `collection_upgrade` to upgrade collection owned by bri…
ptisserand Mar 27, 2024
5e94174
CI: WIP docker push image
ptisserand Mar 14, 2024
68e5808
CI: add missing `cargo test` run
ptisserand Mar 27, 2024
3a2d582
CI: add workflow_dispatch
ptisserand Mar 27, 2024
ca7d52c
indexer: allow setting git revision using 'GIT_HASH' environment vari…
ptisserand Mar 28, 2024
11384f4
indexer: add GIT_HASH as docker build argument
ptisserand Mar 28, 2024
7b1da23
CI: docker tag must be lowercase
ptisserand Mar 28, 2024
cbf0426
indexer: avoid panic when ethereum event has only 1 topic
ptisserand Apr 3, 2024
c593a7c
indexer: add missing normalize hex for ethereum
ptisserand Apr 4, 2024
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
39 changes: 39 additions & 0 deletions .github/workflows/indexer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Bridge Indexer

on:
push:
paths: "apps/indexer/**"
workflow_dispatch:

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Run test
run: cargo test
working-directory: apps/indexer
- name: Lowercase repository
run: |
echo "LOWERCASE_REPOSITORY=${GITHUB_REPOSITORY@L}" >> ${GITHUB_ENV}

- name: Setup docker buildx
uses: docker/setup-buildx-action@v2
- name: Login to Github Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
push: true
context: apps/indexer
tags: ghcr.io/${{ env.LOWERCASE_REPOSITORY }}:latest
build-args: |
"GIT_HASH=$GITHUB_SHA"
23 changes: 19 additions & 4 deletions apps/blockchain/ethereum/src/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ contract Starklane is IStarklaneEvent, UUPSOwnableProxied, StarklaneState, Stark

_transferOwnership(owner);

setStarklaneL2Address(Cairo.snaddressWrap(starklaneL2Address));
setStarklaneL2Selector(Cairo.felt252Wrap(starklaneL2Selector));
setStarklaneL2Address(starklaneL2Address);
setStarklaneL2Selector(starklaneL2Selector);
}

/**
Expand All @@ -82,6 +82,9 @@ contract Starklane is IStarklaneEvent, UUPSOwnableProxied, StarklaneState, Stark
external
payable
{
if (!Cairo.isFelt252(snaddress.unwrap(ownerL2))) {
revert CairoWrapError();
}
if (!_enabled) {
revert BridgeNotEnabledError();
}
Expand Down Expand Up @@ -159,7 +162,9 @@ contract Starklane is IStarklaneEvent, UUPSOwnableProxied, StarklaneState, Stark
// Any error or permission fail in the message consumption will cause a revert.
// After message being consumed, it is considered legit and tokens can be withdrawn.
if (Protocol.canUseWithdrawAuto(header)) {
_consumeMessageAutoWithdraw(_starklaneL2Address, request);
// 2024-03-19: disabled autoWithdraw after audit report
// _consumeMessageAutoWithdraw(_starklaneL2Address, request);
revert NotSupportedYetError();
} else {
_consumeMessageStarknet(_starknetCoreAddress, _starklaneL2Address, request);
}
Expand All @@ -179,7 +184,7 @@ contract Starklane is IStarklaneEvent, UUPSOwnableProxied, StarklaneState, Stark
req.hash
);
} else {
// TODO ERC1155.
revert NotSupportedYetError();
}
}

Expand Down Expand Up @@ -348,4 +353,14 @@ contract Starklane is IStarklaneEvent, UUPSOwnableProxied, StarklaneState, Stark
function isEnabled() external view returns(bool) {
return _enabled;
}

function setL1L2CollectionMapping(
address collectionL1,
snaddress collectionL2,
bool force
) external onlyOwner {
_setL1L2AddressMapping(collectionL1, collectionL2, force);
emit L1L2CollectionMappingUpdated(collectionL1, snaddress.unwrap(collectionL2));
}

}
13 changes: 13 additions & 0 deletions apps/blockchain/ethereum/src/IStarklane.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,17 @@ interface IStarklane {
* @return true if bridge is enabled
*/
function isEnabled() external view returns (bool);

/**
*
* @param collectionL1 Collection address on L1
* @param collectionL2 Collection address on L2
* @param force Force flag
*/
function setL1L2CollectionMapping(
address collectionL1,
snaddress collectionL2,
bool force
) external;

}
8 changes: 8 additions & 0 deletions apps/blockchain/ethereum/src/IStarklaneEvent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,12 @@ interface IStarklaneEvent {
address indexed collection,
bool enable
);

/**
@notice L1 L2 collection mapping updated
*/
event L1L2CollectionMappingUpdated(
address indexed colllectionL1,
uint256 indexed collectionL2
);
}
8 changes: 4 additions & 4 deletions apps/blockchain/ethereum/src/State.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ contract StarklaneState is Ownable {
@param l2Address Starklane L2 address.
*/
function setStarklaneL2Address(
snaddress l2Address
uint256 l2Address
)
public
onlyOwner
{
_starklaneL2Address = l2Address;
_starklaneL2Address = Cairo.snaddressWrap(l2Address);
}

/**
Expand All @@ -55,12 +55,12 @@ contract StarklaneState is Ownable {
@param l2Selector Starklane L2 selector.
*/
function setStarklaneL2Selector(
felt252 l2Selector
uint256 l2Selector
)
public
onlyOwner
{
_starklaneL2Selector = l2Selector;
_starklaneL2Selector = Cairo.felt252Wrap(l2Selector);
}


Expand Down
19 changes: 19 additions & 0 deletions apps/blockchain/ethereum/src/token/CollectionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import "../sn/Cairo.sol";
error InvalidCollectionL1Address();
error InvalidCollectionL2Address();
error ErrorVerifyingAddressMapping();
error CollectionMappingAlreadySet();

/**
@title Collection manager to verify collection address matching and deploy them.
Expand Down Expand Up @@ -56,6 +57,7 @@ contract CollectionManager {
{
address proxy = Deployer.deployERC721Bridgeable(name, symbol);
_l1ToL2Addresses[proxy] = collectionL2;
_l2ToL1Addresses[collectionL2] = proxy;

emit CollectionDeployedFromL2(
reqHash,
Expand Down Expand Up @@ -86,6 +88,7 @@ contract CollectionManager {
{
address proxy = Deployer.deployERC1155Bridgeable(uri);
_l1ToL2Addresses[proxy] = collectionL2;
_l2ToL1Addresses[collectionL2] = proxy;

emit CollectionDeployedFromL2(
reqHash,
Expand Down Expand Up @@ -145,5 +148,21 @@ contract CollectionManager {
revert ErrorVerifyingAddressMapping();
}

function _setL1L2AddressMapping(
address collectionL1,
snaddress collectionL2,
bool force
)
internal
{
if (((snaddress.unwrap(_l1ToL2Addresses[collectionL1]) == 0) && (_l2ToL1Addresses[collectionL2] == address(0)))
|| (force == true)) {
_l1ToL2Addresses[collectionL1] = collectionL2;
_l2ToL1Addresses[collectionL2] = collectionL1;
} else {
revert CollectionMappingAlreadySet();
}
}

}

118 changes: 112 additions & 6 deletions apps/blockchain/ethereum/test/Bridge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ contract BridgeTest is Test, IStarklaneEvent {
);
}

function test_L2OwnerOverflow() public {
uint256[] memory ids = new uint256[](0);

uint256 salt = 0x0;
snaddress to = snaddress.wrap(SN_MODULUS);

vm.expectRevert(CairoWrapError.selector);
IStarklane(bridge).depositTokens{value: 30000}(
salt,
address(erc721C1),
to,
ids,
false
);
}

//
function test_depositTokenERC721() public {
IERC721MintRangeFree(erc721C1).mintRangeFree(alice, 0, 10);
Expand Down Expand Up @@ -261,7 +277,8 @@ contract BridgeTest is Test, IStarklaneEvent {
assertEq(whiteListed.length, 1);
}

// Test a withdraw auto that will trigger the deploy of a new collection on L1.
// Audit:
// [C-01] Anyone can withdraw any token held by the L1 bridge
function test_withdrawTokensERC721AutoWithdrawDeploy() public {
// Build the request and compute it's "would be" message hash.
felt252 header = Protocol.requestHeaderV1(CollectionType.ERC721, false, true);
Expand All @@ -273,24 +290,112 @@ contract BridgeTest is Test, IStarklaneEvent {
// as QUICK message.

IStarklane(bridge).addMessageHashForAutoWithdraw(uint256(msgHash));
vm.expectRevert(NotSupportedYetError.selector);
IStarklane(bridge).withdrawTokens(reqSerialized);
}

// Test a withdraw STARKNET that will trigger the deploy of a new collection on L1.'0x800000000000011000000000000000000000000000000000000000000000001'
function test_withdrawTokensERC721StarknetWithdrawDeploy() public {
// Build the request and compute it's "would be" message hash.
felt252 header = Protocol.requestHeaderV1(CollectionType.ERC721, false, false);
Request memory req = buildRequestDeploy(header, 888, bob);
uint256[] memory reqSerialized = Protocol.requestSerialize(req);
bytes32 msgHash = computeMessageHashFromL2(reqSerialized);

// The message must be simulated to come from starknet verifier contract
// on L1 and pushed to starknet core.
uint256[] memory hashes = new uint256[](1);
hashes[0] = uint256(msgHash);
IStarknetMessagingLocal(snCore).addMessageHashesFromL2(hashes);
address collection = IStarklane(bridge).withdrawTokens(reqSerialized);

// TODO: add verification of event emission.

assertEq(IERC721(collection).ownerOf(888), bob);

vm.expectRevert();
// Error message from Starknet Core expected.
vm.expectRevert(bytes("INVALID_MESSAGE_TO_CONSUME"));
IStarklane(bridge).withdrawTokens(reqSerialized);
}

// Test a withdraw STARKNET that will trigger the deploy of a new collection on L1.
function test_withdrawTokensERC721StarknetWithdrawDeploy() public {
// Audit:
// [C-02] Impossible to withdraw L1 native tokens back on L1 after bridging to L2. Also, a different collection address will be generated for every native l2 token bridged to l1
function test_withdrawTokensERC721StarknetWithdrawDeploySameCollection() public {
// Build the request and compute it's "would be" message hash.
felt252 header = Protocol.requestHeaderV1(CollectionType.ERC721, false, false);
Request memory req = buildRequestDeploy(header, 888, bob);
uint256[] memory reqSerialized = Protocol.requestSerialize(req);
bytes32 msgHash = computeMessageHashFromL2(reqSerialized);

// The message must be simulated to come from starknet verifier contract
// on L1 and pushed to starknet core.
uint256[] memory msgHashes = new uint256[](1);
msgHashes[0] = uint256(msgHash);
IStarknetMessagingLocal(snCore).addMessageHashesFromL2(msgHashes);
address collection1 = IStarklane(bridge).withdrawTokens(reqSerialized);

assertEq(IERC721(collection1).ownerOf(888), bob);
// Error message from Starknet Core expected.
vm.expectRevert(bytes("INVALID_MESSAGE_TO_CONSUME"));
IStarklane(bridge).withdrawTokens(reqSerialized);

req.tokenIds[0] = 777;
reqSerialized = Protocol.requestSerialize(req);
msgHash = computeMessageHashFromL2(reqSerialized);

msgHashes[0] = uint256(msgHash);
IStarknetMessagingLocal(snCore).addMessageHashesFromL2(msgHashes);
address collection2 = IStarklane(bridge).withdrawTokens(reqSerialized);

assertEq(IERC721(collection2).ownerOf(777), bob);
assertEq(collection1, collection2);
}

function test_withdrawTokensERC721StarknetWithdrawDeployDifferentCollection() public {
felt252 header = Protocol.requestHeaderV1(CollectionType.ERC721, false, false);
Request memory req = buildRequestDeploy(header, 888, bob);
uint256[] memory reqSerialized = Protocol.requestSerialize(req);
bytes32 msgHash = computeMessageHashFromL2(reqSerialized);

// The message must be simulated to come from starknet verifier contract
// on L1 and pushed to starknet core.
uint256[] memory msgHashes = new uint256[](1);
msgHashes[0] = uint256(msgHash);
IStarknetMessagingLocal(snCore).addMessageHashesFromL2(msgHashes);
address collection1 = IStarklane(bridge).withdrawTokens(reqSerialized);

assertEq(IERC721(collection1).ownerOf(888), bob);
// Error message from Starknet Core expected.
vm.expectRevert(bytes("INVALID_MESSAGE_TO_CONSUME"));
IStarklane(bridge).withdrawTokens(reqSerialized);

req.tokenIds[0] = 777;
req.collectionL2 = Cairo.snaddressWrap(0x456);
reqSerialized = Protocol.requestSerialize(req);
msgHash = computeMessageHashFromL2(reqSerialized);

msgHashes[0] = uint256(msgHash);
IStarknetMessagingLocal(snCore).addMessageHashesFromL2(msgHashes);
address collection2 = IStarklane(bridge).withdrawTokens(reqSerialized);

assertEq(IERC721(collection2).ownerOf(777), bob);
assertFalse(collection1 == collection2);
}

function test_depositWithdrawTokens_withMapping() public {
// add mapping L1 <-> L2: erc721C1 <-> 0x123
IStarklane(bridge).setL1L2CollectionMapping(address(erc721C1), Cairo.snaddressWrap(0x123), true);

// alice deposit token 0 and 9 of collection erc721C1 to bridge
test_depositTokenERC721();

// Build the request and compute it's "would be" message hash.
felt252 header = Protocol.requestHeaderV1(CollectionType.ERC721, false, false);
Request memory req = buildRequestDeploy(header, 9, bob);
req.collectionL1 = address(erc721C1);
uint256[] memory reqSerialized = Protocol.requestSerialize(req);
bytes32 msgHash = computeMessageHashFromL2(reqSerialized);

// The message must be simulated to come from starknet verifier contract
// on L1 and pushed to starknet core.
uint256[] memory hashes = new uint256[](1);
Expand All @@ -299,8 +404,8 @@ contract BridgeTest is Test, IStarklaneEvent {
address collection = IStarklane(bridge).withdrawTokens(reqSerialized);

// TODO: add verification of event emission.

assertEq(IERC721(collection).ownerOf(888), bob);
assertEq(collection, erc721C1);
assertEq(IERC721(erc721C1).ownerOf(9), bob);

// Error message from Starknet Core expected.
vm.expectRevert(bytes("INVALID_MESSAGE_TO_CONSUME"));
Expand Down Expand Up @@ -387,6 +492,7 @@ contract BridgeTest is Test, IStarklaneEvent {
assert(IERC721(erc721C1).ownerOf(ids[1]) == alice);
}


// Build a request that should trigger a deploy of a new collection on L1.
function buildRequestDeploy(
felt252 header,
Expand Down
4 changes: 2 additions & 2 deletions apps/blockchain/starknet/.tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scarb 2.4.4
starknet-foundry 0.15.0
scarb 2.6.4
starknet-foundry 0.19.0
14 changes: 14 additions & 0 deletions apps/blockchain/starknet/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "snforge_std"
version = "0.15.0"
source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.15.0#7747a2d27b9ebfccf7de8e757d9c6a3ed31e81fc"

[[package]]
name = "starklane"
version = "0.1.0"
dependencies = [
"snforge_std",
]
Loading
Loading