Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: v0.3.0

- name: Install Solidity Dependencies
run: forge soldeer update -d
Expand Down Expand Up @@ -59,7 +59,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: v0.3.0

- name: Install Solidity Dependencies
run: forge soldeer update -d
Expand Down
2 changes: 1 addition & 1 deletion bytecode/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tnt-core-bytecode"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
description = "Bytecode exports for TNT Core Solidity contracts"
license = "MIT"
Expand Down
3 changes: 2 additions & 1 deletion bytecode/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ fn main() {

let mut rust_code = String::from(
r#"//! TNT Core contract bytecode exports
//!
//!
//! This crate exports the bytecode of TNT Core contracts as constant byte vectors
//! that can be easily imported and used in other Rust projects.

/// Module containing all contract bytecodes
#[rustfmt::skip]
pub mod bytecode {
"#,
);
Expand Down
5 changes: 3 additions & 2 deletions bytecode/src/lib.rs

Large diffs are not rendered by default.

127 changes: 48 additions & 79 deletions examples/IncredibleSquaringBlueprint.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
pragma solidity ^0.8.20;

import "../src/BlueprintServiceManagerBase.sol";

Expand All @@ -14,7 +14,7 @@ contract IncredibleSquaringBlueprint is BlueprintServiceManagerBase {
* @dev A mapping of all service operators registered with the blueprint.
* The key is the operator's address and the value is the operator's details.
*/
mapping(address => bytes) public operators;
mapping(address => ServiceOperators.OperatorPreferences) public operators;

/**
* @dev A mapping of all service instances requested from the blueprint.
Expand All @@ -26,144 +26,113 @@ contract IncredibleSquaringBlueprint is BlueprintServiceManagerBase {
* @dev Hook for service operator registration. Called when a service operator
* attempts to register with the blueprint.
* @param operator The operator's details.
* @param _registrationInputs Inputs required for registration.
* @param registrationInputs Inputs required for registration.
*/
function onRegister(
bytes calldata operator,
bytes calldata _registrationInputs
) public payable override onlyFromRootChain {
ServiceOperators.OperatorPreferences calldata operator,
bytes calldata registrationInputs
)
public
payable
override
onlyFromMaster
{
// compute the operator's address from the operator's public key
address operatorAddress = operatorAddressFromPublicKey(operator);
address operatorAddress = ServiceOperators.asOperatorAddress(operator.ecdsaPublicKey);
// store the operator's details
operators[operatorAddress] = operator;
}

/**
* @dev Hook for service instance requests. Called when a user requests a service
* instance from the blueprint.
* @param serviceId The ID of the requested service.
* @param operators The operators involved in the service.
* @param _requestInputs Inputs required for the service request.
* @param params The parameters for the service request.
*/
function onRequest(
uint64 serviceId,
bytes[] calldata operators,
bytes calldata _requestInputs
) public payable override onlyFromRootChain {
function onRequest(ServiceOperators.RequestParams calldata params) public payable override onlyFromMaster {
// store the service instance request
for (uint i = 0; i < operators.length; i++) {
address operatorAddress = operatorAddressFromPublicKey(
operators[i]
);
serviceInstances[serviceId].push(operatorAddress);
for (uint256 i = 0; i < params.operators.length; i++) {
address operatorAddress = ServiceOperators.asOperatorAddress(params.operators[i].ecdsaPublicKey);
serviceInstances[params.requestId].push(operatorAddress);
}
}

/**
* @dev Hook for job calls on the service. Called when a job is called within
* the service context.
* @param serviceId The ID of the service where the job is called.
* @param job The job identifier.
* @param jobCallId A unique ID for the job call.
* @param inputs Inputs required for the job execution in bytes format.
*/
function onJobCall(
uint64 serviceId,
uint8 job,
uint64 jobCallId,
bytes calldata inputs
) public payable override onlyFromRootChain {
)
public
payable
override
onlyFromMaster
{
// Implement job call logic here
}

/**
* @dev Hook for handling job result. Called when operators send the result
* of a job execution.
* @param serviceId The ID of the service related to the job.
* @param job The job identifier.
* @param jobCallId The unique ID for the job call.
* @param operator The operator (operator) sending the result in bytes format.
* @param inputs Inputs used for the job execution in bytes format.
* @param outputs Outputs resulting from the job execution in bytes format.
*/
function onJobResult(
uint64 serviceId,
uint8 job,
uint64 jobCallId,
bytes calldata operator,
ServiceOperators.OperatorPreferences calldata operator,
bytes calldata inputs,
bytes calldata outputs
) public payable virtual override onlyFromRootChain {
)
public
payable
override
onlyFromMaster
{
// Do something with the job result
}

/**
* @dev Verifies the result of a job call. This function is used to validate the
* outputs of a job execution against the expected results.
* @param serviceId The ID of the service related to the job.
* @param job The job identifier.
* @param jobCallId The unique ID for the job call.
* @param operator The operator (operator) whose result is being verified.
* @param inputs Inputs used for the job execution.
* @param outputs Outputs resulting from the job execution.
* @return bool Returns true if the job call result is verified successfully,
* otherwise false.
* @dev Verifies the result of a job call.
*/
function verifyResult(
uint64 serviceId,
uint8 job,
uint64 jobCallId,
bytes calldata operator,
ServiceOperators.OperatorPreferences calldata operator,
bytes calldata inputs,
bytes calldata outputs
) public view returns (bool) {
// Someone requested to verify the result of a job call.
// We need to check if the output is the square of the input.

)
public
view
returns (bool)
{
// check if job is zero.
require(job == 0, "Job not found");
// Check if the operator is a registered operator, so we can slash
// their stake if they are cheating.
address operatorAddress = operatorAddressFromPublicKey(operator);
require(
operators[operatorAddress].length > 0,
"Operator not registered"
);

// Check if the operator is a registered operator
address operatorAddress = ServiceOperators.asOperatorAddress(operator.ecdsaPublicKey);
require(operators[operatorAddress].ecdsaPublicKey.length > 0, "Operator not registered");

// Check if operator is part of the service instance
require(
isOperatorInServiceInstance(serviceId, operatorAddress),
"Operator not part of service instance"
);
require(isOperatorInServiceInstance(serviceId, operatorAddress), "Operator not part of service instance");

// Decode the inputs and outputs
uint256 input = abi.decode(inputs, (uint256));
uint256 output = abi.decode(outputs, (uint256));
// Check if the output is the square of the input
bool isValid = output == input * input;
if (!isValid) {
// Slash the operator's stake if the result is invalid
// Using ServicesPrecompile to slash the operator's stake
// slashPercent = 10; // 10% slash
// ServicesPrecompile.slash(serviceId, operator, slashPercent);
}

return isValid;
// Check if the output is the square of the input
return output == input * input;
}

function isOperatorInServiceInstance(
uint64 serviceId,
address operatorAddress
) public view returns (bool) {
for (uint i = 0; i < serviceInstances[serviceId].length; i++) {
function isOperatorInServiceInstance(uint64 serviceId, address operatorAddress) public view returns (bool) {
for (uint256 i = 0; i < serviceInstances[serviceId].length; i++) {
if (serviceInstances[serviceId][i] == operatorAddress) {
return true;
}
}
return false;
}

function operatorAddressFromPublicKey(
bytes calldata publicKey
) public pure returns (address) {
return address(uint160(uint256(keccak256(publicKey))));
}
}
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 14 additions & 35 deletions test/AssetsLib.t.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {Assets} from "../src/AssetsLib.sol";
import { Test } from "forge-std/Test.sol";
import { Assets } from "../src/AssetsLib.sol";

contract AssetsLibTest is Test {
// Test constants
address constant TEST_ERC20_ADDRESS = address(0x1234567890123456789012345678901234567890);
bytes32 constant TEST_ASSET_ID = bytes32(uint256(123));

function setUp() public {
// No setup required as we're testing a library
}

function testErc20AssetConversion() public {
Assets.Asset memory asset = Assets.Asset({
kind: Assets.Kind.Erc20,
data: bytes32(uint256(uint160(TEST_ERC20_ADDRESS)))
});
Assets.Asset memory asset =
Assets.Asset({ kind: Assets.Kind.Erc20, data: bytes32(uint256(uint160(TEST_ERC20_ADDRESS))) });

address result = Assets.toAddress(asset);
assertEq(result, TEST_ERC20_ADDRESS, "ERC20 address conversion failed");
}

function testCustomAssetConversion() public {
Assets.Asset memory asset = Assets.Asset({
kind: Assets.Kind.Custom,
data: TEST_ASSET_ID
});
Assets.Asset memory asset = Assets.Asset({ kind: Assets.Kind.Custom, data: TEST_ASSET_ID });

address result = Assets.toAddress(asset);
assertTrue(Assets.isAssetIdCompatible(result), "Custom asset address should be compatible");
Expand All @@ -47,15 +42,10 @@ contract AssetsLibTest is Test {
}

function testAssetTypeChecks() public {
Assets.Asset memory erc20Asset = Assets.Asset({
kind: Assets.Kind.Erc20,
data: bytes32(uint256(uint160(TEST_ERC20_ADDRESS)))
});
Assets.Asset memory erc20Asset =
Assets.Asset({ kind: Assets.Kind.Erc20, data: bytes32(uint256(uint160(TEST_ERC20_ADDRESS))) });

Assets.Asset memory customAsset = Assets.Asset({
kind: Assets.Kind.Custom,
data: TEST_ASSET_ID
});
Assets.Asset memory customAsset = Assets.Asset({ kind: Assets.Kind.Custom, data: TEST_ASSET_ID });

assertTrue(Assets.isErc20(erc20Asset), "Should be identified as ERC20");
assertFalse(Assets.isErc20(customAsset), "Should not be identified as ERC20");
Expand All @@ -77,30 +67,19 @@ contract AssetsLibTest is Test {

function testNativeAssetChecks() public {
// Test native ERC20 (address(0))
Assets.Asset memory nativeErc20 = Assets.Asset({
kind: Assets.Kind.Erc20,
data: bytes32(0)
});
Assets.Asset memory nativeErc20 = Assets.Asset({ kind: Assets.Kind.Erc20, data: bytes32(0) });
assertTrue(Assets.isNative(nativeErc20), "Should identify native ERC20");

// Test native Custom asset (id = 0)
Assets.Asset memory nativeCustom = Assets.Asset({
kind: Assets.Kind.Custom,
data: bytes32(0)
});
Assets.Asset memory nativeCustom = Assets.Asset({ kind: Assets.Kind.Custom, data: bytes32(0) });
assertTrue(Assets.isNative(nativeCustom), "Should identify native Custom asset");

// Test non-native assets
Assets.Asset memory nonNativeErc20 = Assets.Asset({
kind: Assets.Kind.Erc20,
data: bytes32(uint256(uint160(TEST_ERC20_ADDRESS)))
});
Assets.Asset memory nonNativeErc20 =
Assets.Asset({ kind: Assets.Kind.Erc20, data: bytes32(uint256(uint160(TEST_ERC20_ADDRESS))) });
assertFalse(Assets.isNative(nonNativeErc20), "Should not identify as native ERC20");

Assets.Asset memory nonNativeCustom = Assets.Asset({
kind: Assets.Kind.Custom,
data: TEST_ASSET_ID
});
Assets.Asset memory nonNativeCustom = Assets.Asset({ kind: Assets.Kind.Custom, data: TEST_ASSET_ID });
assertFalse(Assets.isNative(nonNativeCustom), "Should not identify as native Custom asset");
}
}
Loading