Contract Diff Checker

Contract Name:
Starknet

Contract Source Code:

/*
  Copyright 2019-2021 StarkWare Industries Ltd.

  Licensed under the Apache License, Version 2.0 (the "License").
  You may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  https://www.starkware.co/open-source-license/

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions
  and limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0.
pragma solidity ^0.6.11;

interface Identity {
    /*
      Allows a caller to ensure that the provided address is of the expected type and version.
    */
    function identify()
        external pure
        returns (string memory);
}

/*
  Copyright 2019-2021 StarkWare Industries Ltd.

  Licensed under the Apache License, Version 2.0 (the "License").
  You may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  https://www.starkware.co/open-source-license/

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions
  and limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0.
pragma solidity ^0.6.11;

/*
  The Fact Registry design pattern is a way to separate cryptographic verification from the
  business logic of the contract flow.

  A fact registry holds a hash table of verified "facts" which are represented by a hash of claims
  that the registry hash check and found valid. This table may be queried by accessing the
  isValid() function of the registry with a given hash.

  In addition, each fact registry exposes a registry specific function for submitting new claims
  together with their proofs. The information submitted varies from one registry to the other
  depending of the type of fact requiring verification.

  For further reading on the Fact Registry design pattern see this
  `StarkWare blog post <https://medium.com/starkware/the-fact-registry-a64aafb598b6>`_.
*/
interface IFactRegistry {
    /*
      Returns true if the given fact was previously registered in the contract.
    */
    function isValid(bytes32 fact)
        external view
        returns(bool);
}

/*
  Copyright 2019-2021 StarkWare Industries Ltd.

  Licensed under the Apache License, Version 2.0 (the "License").
  You may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  https://www.starkware.co/open-source-license/

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions
  and limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0.
pragma solidity ^0.6.11;

library OnchainDataFactTreeEncoder {
    struct DataAvailabilityFact {
        uint256 onchainDataHash;
        uint256 onchainDataSize;
    }

    // The number of additional words appended to the public input when using the
    // OnchainDataFactTreeEncoder format.
    uint256 internal constant ONCHAIN_DATA_FACT_ADDITIONAL_WORDS = 2;

    /*
      Encodes a GPS fact Merkle tree where the root has two children.
      The left child contains the data we care about and the right child contains
      on-chain data for the fact.
    */
    function encodeFactWithOnchainData(
        uint256[] calldata programOutput, DataAvailabilityFact memory factData
    )
        internal pure
        returns (bytes32)
    {
        // The state transition fact is computed as a Merkle tree, as defined in
        // GpsOutputParser.
        //
        // In our case the fact tree looks as follows:
        //   The root has two children.
        //   The left child is a leaf that includes the main part - the information regarding
        //   the state transition required by this contract.
        //   The right child contains the onchain-data which shouldn't be accessed by this
        //   contract, so we are only given its hash and length
        //   (it may be a leaf or an inner node, this has no effect on this contract).

        // Compute the hash without the two additional fields.
        uint256 mainPublicInputLen = programOutput.length;
        bytes32 mainPublicInputHash = keccak256(abi.encodePacked(programOutput));

        // Compute the hash of the fact Merkle tree.
        bytes32 hashResult = keccak256(
            abi.encodePacked(
                mainPublicInputHash,
                mainPublicInputLen,
                factData.onchainDataHash,
                mainPublicInputLen + factData.onchainDataSize
            )
        );
        // Add one to the hash to indicate it represents an inner node, rather than a leaf.
        return bytes32(uint256(hashResult) + 1);
    }
}

/*
  Copyright 2019-2021 StarkWare Industries Ltd.

  Licensed under the Apache License, Version 2.0 (the "License").
  You may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  https://www.starkware.co/open-source-license/

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions
  and limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0.
pragma solidity ^0.6.11;

library MerkleUpdateOutput {
    /**
      Returns the previous Merkle root.
    */
    function getPrevRoot(uint256[] calldata merkle_update_data)
        internal
        pure
        returns (uint256)
    {
        return merkle_update_data[0];
    }

    /**
      Returns the new Merkle root.
    */
    function getNewRoot(uint256[] calldata merkle_update_data)
        internal
        pure
        returns (uint256)
    {
        return merkle_update_data[1];
    }
}

library StarknetOutput {
    uint256 internal constant MERKLE_UPDATE_OFFSET = 0;
    uint256 internal constant HEADER_SIZE = 2;

    uint256 constant MESSAGE_FROM_ADDRESS_OFFSET = 0;
    uint256 constant MESSAGE_TO_ADDRESS_OFFSET = 1;
    uint256 constant MESSAGE_PAYLOAD_SIZE_OFFSET = 2;
    uint256 constant MESSAGE_PREFIX_SIZE = 3;

    // An event that is raised when a message is sent from L2 to L1.
    event LogMessageToL1(
        uint256 indexed from_address,
        address indexed to_address,
        uint256[] payload
    );

    /**
      Does a sanity check of the output_data length.
    */
    function validate(uint256[] calldata output_data) internal pure {
        require(output_data.length > HEADER_SIZE, "STARKNET_OUTPUT_TOO_SHORT");
    }

    /**
      Returns a slice of the 'output_data' with the merkle update information.
    */
    function getMerkleUpdate(uint256[] calldata output_data)
        internal
        pure
        returns (uint256[] calldata)
    {
        return output_data[MERKLE_UPDATE_OFFSET:MERKLE_UPDATE_OFFSET + 2];
    }

    /**
      Processes a message segment from the program output.
      The format of a message segment is the length of the messages in words followed
      by the concatenation of all the messages.

      The 'messages' mapping is updated according to the messages and the direction ('isL2ToL1').
    */
    function processMessages(
        bool isL2ToL1,
        uint256[] calldata programOutputSlice,
        mapping (bytes32 => uint256) storage messages
    ) internal returns (uint256) {
        uint256 message_segment_size = programOutputSlice[0];
        require(message_segment_size < 2**30, "INVALID_MESSAGE_SEGMENT_SIZE");

        uint256 offset = 1;
        uint256 message_segment_end = offset + message_segment_size;
        while (offset < message_segment_end) {
            uint256 payloadLengthOffset = offset + MESSAGE_PAYLOAD_SIZE_OFFSET;
            require (
                payloadLengthOffset < programOutputSlice.length,
                "MESSAGE_TOO_SHORT");

            uint256 payloadLengh = programOutputSlice[payloadLengthOffset];
            require(payloadLengh < 2**30, "INVALID_PAYLOAD_LENGTH");

            uint256 endOffset = offset + MESSAGE_PREFIX_SIZE + payloadLengh;
            require (
                endOffset <= programOutputSlice.length,
                "TRUNCATED_MESSAGE_PAYLOAD");

            bytes32 messageHash = keccak256(abi.encodePacked(
                programOutputSlice[offset:endOffset]));
            if (isL2ToL1) {
                emit LogMessageToL1(
                    programOutputSlice[offset + MESSAGE_FROM_ADDRESS_OFFSET],
                    address(programOutputSlice[offset + MESSAGE_TO_ADDRESS_OFFSET]),
                    (uint256[])(programOutputSlice[
                        offset + MESSAGE_PREFIX_SIZE: endOffset]));
                messages[messageHash] += 1;
            } else {
                require(messages[messageHash] > 0, "INVALID_MESSAGE_TO_CONSUME");
                messages[messageHash] -= 1;
            }

            offset = endOffset;
        }
        require(
            offset == message_segment_end,
            "INVALID_MESSAGE_SEGMENT_SIZE");

        return offset;
    }
}

/*
  Copyright 2019-2021 StarkWare Industries Ltd.

  Licensed under the Apache License, Version 2.0 (the "License").
  You may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  https://www.starkware.co/open-source-license/

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions
  and limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0.
pragma solidity ^0.6.11;
pragma experimental ABIEncoderV2;

import "IFactRegistry.sol";
import "Identity.sol";
import "Output.sol";
import "StarknetState.sol";
import "OnchainDataFactTreeEncoder.sol";

contract Starknet is
    Identity
{
    using StarknetState for StarknetState.State;
    StarknetState.State internal state;
    IFactRegistry verifier;
    uint256 programHash;
    mapping (bytes32 => uint256) public l2ToL1Messages;
    mapping (bytes32 => uint256) public l1ToL2Messages;

    // Logs the new state following a state update.
    event LogStateUpdate(
        uint256 globalRoot,
        int256 sequenceNumber
    );

    // Logs a stateTransitionFact that was used to update the state.
    event LogStateTransitionFact(
        bytes32 stateTransitionFact
    );

    // An event that is raised when a message is sent from L1 to L2.
    event LogMessageToL2(
        address indexed from_address,
        uint256 indexed to_address,
        uint256 indexed selector,
        uint256[] payload
    );

    // This event needs to be compatible with the one defined in Output.sol.
    event LogMessageToL1(
        uint256 indexed from_address,
        address indexed to_address,
        uint256[] payload
    );

    /**
      The Starknet contract constructor.

      Arguments:
        programHash_ - The program hash of the StarkNet OS.
        verifier_ - The address of a SHARP verifier fact registry.
        initialState - The initial state of the system.
    */
    constructor(
        uint256 programHash_, IFactRegistry verifier_,
        StarknetState.State memory initialState) public
    {
        programHash = programHash_;
        verifier = verifier_;
        state = initialState;
    }

    /**
      Returns a string that identifies the contract.
    */
    function identify() external override pure returns (string memory) {
        return "StarkWare_Starknet_2021_1";
    }

    /**
      Returns the current state root.
    */
    function stateRoot() external view returns (uint256) {
        return state.globalRoot;
    }

    /**
      Returns the current sequence number.
    */
    function stateSequenceNumber() external view returns (int256) {
        return state.sequenceNumber;
    }

    /**
      Updates the state of the StarkNet, based on a proof of the 
      StarkNet OS that the state transition is valid.

      Arguments:
        sequenceNumber - The expected sequence number of the new block.
        programOutput - The main part of the StarkNet OS program output.
        data_availability_fact - An encoding of the on-chain data associated
        with the 'programOutput'.
    */
    function updateState(
        int256 sequenceNumber, uint256[] calldata programOutput,
        OnchainDataFactTreeEncoder.DataAvailabilityFact calldata data_availability_fact) public
    {
        // Validate program output.
        StarknetOutput.validate(programOutput);

        bytes32 stateTransitionFact = OnchainDataFactTreeEncoder.encodeFactWithOnchainData(
            programOutput, data_availability_fact);
        bytes32 sharpFact = keccak256(abi.encode(programHash, stateTransitionFact));
        require(verifier.isValid(sharpFact), "NO_STATE_TRANSITION_PROOF");
        emit LogStateTransitionFact(stateTransitionFact);

        // Process L2 -> L1 messages.
        uint256 outputOffset = StarknetOutput.HEADER_SIZE;
        outputOffset += StarknetOutput.processMessages(
            /*isL2ToL1*/ true, programOutput[outputOffset:], l2ToL1Messages);

        // Process L1 -> L2 messages.
        outputOffset += StarknetOutput.processMessages(
            /*isL2ToL1*/ false, programOutput[outputOffset:], l1ToL2Messages);

        require(
            outputOffset == programOutput.length,
            "STARKNET_OUTPUT_TOO_LONG");

        // Perform state update.
        state.update(sequenceNumber, programOutput);
        emit LogStateUpdate(state.globalRoot, state.sequenceNumber);
    }

    /**
      Sends a message to an L2 contract.
    */
    function sendMessageToL2(
        uint256 to_address,
        uint256 selector,
        uint256[] calldata payload
    ) external {
        emit LogMessageToL2(msg.sender, to_address, selector, payload);
        // Note that the selector is prepended to the payload.
        bytes32 msgHash = keccak256(
            abi.encodePacked(
                uint256(msg.sender),
                to_address,
                1 + payload.length,
                selector,
                payload
            )
        );
        l1ToL2Messages[msgHash] += 1;
    }

    /**
      Consumes a message that was sent from an L2 contract.
    */
    function consumeMessageFromL2(
        uint256 from_address,
        uint256[] calldata payload
    ) external {
        bytes32 msgHash = keccak256(
            abi.encodePacked(
                from_address,
                uint256(msg.sender),
                payload.length,
                payload)
        );

        require(l2ToL1Messages[msgHash] > 0, "INVALID_MESSAGE_TO_CONSUME");
        l2ToL1Messages[msgHash] -= 1;
    }
}

/*
  Copyright 2019-2021 StarkWare Industries Ltd.

  Licensed under the Apache License, Version 2.0 (the "License").
  You may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  https://www.starkware.co/open-source-license/

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions
  and limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0.
pragma solidity ^0.6.11;

import "Output.sol";

library StarknetState {
    struct State {
        uint256 globalRoot;
        int256 sequenceNumber;
    }

    /**
      Validates that the 'sequenceNumber' and the previous root are consistent with the
      current state and updates the state.
    */
    function update(
        State storage state, int256 sequenceNumber, uint256[] calldata starknetOutput) internal {
        // Check the sequenceNumber first as the error is less ambiguous then INVALID_PREVIOUS_ROOT.
        state.sequenceNumber += 1;
        require(state.sequenceNumber == sequenceNumber, "INVALID_SEQUENCE_NUMBER");

        uint256[] calldata merkle_update = StarknetOutput.getMerkleUpdate(starknetOutput);
        require(
            state.globalRoot == MerkleUpdateOutput.getPrevRoot(merkle_update),
            "INVALID_PREVIOUS_ROOT");
        state.globalRoot = MerkleUpdateOutput.getNewRoot(merkle_update);
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):