How to Integrate Your Smart Contract with kOS?

In this page, we are going to use a simple smart contract as an example.

Smart Contract (before)

The below base sample of smart contract code is provided as an example for illustration and learning purposes only. Make sure that your smart contract aligns with security standards before deploying.

Here is the example of base Solidity smart contract, which is the contract that we want to register on the KRNL Platform.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

contract UnprotectedSampleContract {

    string message = "hello";

    function unprotectedFunction(string memory input) external {
        message = input;
    }

    function readMessage() external view returns (string memory) {
        return message;
    }
}

Extension Part for Upgrading Your Smart Contract

Below here is the block of Solidity which you need to add into your smart contract. It allows your smart contract to be able to use the KRNL Operating System, kOS.

Full Version

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

// Struct to group the parameters
struct KrnlPayload {
    bytes auth;
    bytes kernelResponses;
    bytes kernelParams;
}

struct KernelParameter {
    uint8 resolverType;
    bytes parameters;
    string err;
}

struct KernelResponse {
    uint256 kernelId;
    bytes result;
    string err;
}

// Draft Version
contract KRNL is Ownable {
    error UnauthorizedTransaction();

    address public tokenAuthorityPublicKey;
    mapping(bytes => bool) public executed;

    modifier onlyAuthorized(
        KrnlPayload memory krnlPayload,
        bytes memory params
    ) {
        if (!_isAuthorized(krnlPayload, params)) {
            revert UnauthorizedTransaction();
        }

        _;
    }

    constructor(address _tokenAuthorityPublicKey) Ownable(msg.sender) {
        tokenAuthorityPublicKey = _tokenAuthorityPublicKey;
    }

    function setTokenAuthorityPublicKey(
        address _tokenAuthorityPublicKey
    ) external onlyOwner {
        tokenAuthorityPublicKey = _tokenAuthorityPublicKey;
    }

    function _isAuthorized(
        KrnlPayload memory payload,
        bytes memory functionParams
    ) private view returns (bool) {
        
        (
            bytes memory kernelResponseSignature,
            bytes32 kernelParamObjectDigest,
            bytes memory signatureToken,
            uint256 nonce,
            bool finalOpinion
        ) = abi.decode(
                payload.auth,
                (bytes, bytes32, bytes, uint256, bool)
            );

        if (finalOpinion == false) {
            revert("Final opinion reverted");
        }

        bytes32 kernelResponsesDigest = keccak256(
            abi.encodePacked(payload.kernelResponses, msg.sender)
        );

        address recoveredAddress = ECDSA.recover(
            kernelResponsesDigest,
            kernelResponseSignature
        );

        if (recoveredAddress != tokenAuthorityPublicKey) {
            revert("Invalid signature for kernel responses");
        }

        bytes32 _kernelParamsDigest = keccak256(
            abi.encodePacked(payload.kernelParams, msg.sender)
        );

        bytes32 functionParamsDigest = keccak256(functionParams);

        if (_kernelParamsDigest != kernelParamObjectDigest) {
            revert("Invalid kernel params digest");
        }

        bytes32 dataDigest = keccak256(
            abi.encodePacked(
                functionParamsDigest,
                kernelParamObjectDigest,
                msg.sender,
                nonce,
                finalOpinion
            )
        );

        recoveredAddress = ECDSA.recover(dataDigest, signatureToken);
        if (recoveredAddress != tokenAuthorityPublicKey) {
            revert("Invalid signature for function call");
        }

        // executed[signatureToken] = true;
        return true;
    }
}

Upgraded Smart Contract (after)

Instead of having a long chuck of codes in the smart contract, this version requires the inheritance of KRNL.sol into your smart contract.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {KRNL, KrnlPayload, KernelParameter, KernelResponse} from "./KRNL.sol";

contract Sample is KRNL {
    // Token Authority public key as a constructor
    constructor(address _tokenAuthorityPublicKey) KRNL(_tokenAuthorityPublicKey) {}

    // Initial value of message when this contract is being created
    string message = "hello";

    // Results from kernel will be emitted through this event
    event Broadcast(address sender, uint256 score, string message);

    // protected function
    function protectedFunction(
        KrnlPayload memory krnlPayload,
        string memory input
    )
        external
        onlyAuthorized(krnlPayload, abi.encode(input))
    {
        
        // Decode response from kernel
        KernelResponse[] memory kernelResponses = abi.decode(krnlPayload.kernelResponses, (KernelResponse[]));
        uint256 score;
        for (uint i; i < kernelResponses.length; i ++) {
            // kernel id 337 is an example here
            // the response from kernel is in uint256 format
            if (kernelResponses[i].kernelId == 337) {
                score = abi.decode(kernelResponses[i].result, (uint256));
            }
        }

        // Write new message
        message = input;

        // Emitting an event
        emit Broadcast(msg.sender, score, input);
    }

    // Read message from contract
    function readMessage() external view returns (string memory) {
        return message;
    }
}

Deployment

At this point, you can now deploy your upgraded smart contract.

Last updated