Skip to content

Commit

Permalink
runtime/consensus: Add support for querying round roots
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrus committed Nov 23, 2023
1 parent 513c2c4 commit 4dd442c
Show file tree
Hide file tree
Showing 14 changed files with 218 additions and 15 deletions.
2 changes: 1 addition & 1 deletion runtime-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ license = "Apache-2.0"
[dependencies]
cbor = { version = "0.5.1", package = "oasis-cbor" }
oasis-core-runtime = { git = "https://github.com/oasisprotocol/oasis-core", tag = "v23.0.5" }
oasis-core-keymanager = { git = "https://github.com/oasisprotocol/oasis-core", tag = "v23.0.5" }
oasis-runtime-sdk-macros = { path = "../runtime-sdk-macros", optional = true }
oasis-core-keymanager = { git = "https://github.com/oasisprotocol/oasis-core", tag = "v23.0.5" }

# Third party.
byteorder = "1.4.3"
Expand Down
62 changes: 59 additions & 3 deletions runtime-sdk/src/modules/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
//! Low level consensus module for communicating with the consensus layer.
use std::{convert::TryInto, num::NonZeroUsize, str::FromStr, sync::Mutex};

use oasis_runtime_sdk_macros::handler;
use once_cell::sync::Lazy;
use thiserror::Error;

use oasis_core_runtime::{
common::versioned::Versioned,
common::{namespace::Namespace, versioned::Versioned},
consensus::{
beacon::EpochTime,
roothash::{Message, StakingMessage},
roothash::{Message, RoundRoots, StakingMessage},
staking,
staking::{Account as ConsensusAccount, Delegation as ConsensusDelegation},
state::{
beacon::ImmutableState as BeaconImmutableState,
roothash::ImmutableState as RoothashImmutableState,
staking::ImmutableState as StakingImmutableState, StateError,
},
HEIGHT_LATEST,
Expand All @@ -23,26 +25,40 @@ use oasis_core_runtime::{

use crate::{
context::{Context, TxContext},
core::common::crypto::hash::Hash,
history, migration, module,
module::{Module as _, Parameters as _},
modules, sdk_derive,
modules,
modules::core::API as _,
sdk_derive,
types::{
address::{Address, SignatureAddressSpec},
message::MessageEventHookInvocation,
token,
transaction::AddressSpec,
},
Runtime,
};

#[cfg(test)]
mod test;
pub mod types;

/// Unique module name.
const MODULE_NAME: &str = "consensus";

/// Gas costs.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct GasCosts {
/// Cost of the internal round_root call.
pub round_root: u64,
}

/// Parameters for the consensus module.
#[derive(Clone, Debug, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct Parameters {
pub gas_costs: GasCosts,

pub consensus_denomination: token::Denomination,
pub consensus_scaling_factor: u64,

Expand All @@ -56,6 +72,7 @@ pub struct Parameters {
impl Default for Parameters {
fn default() -> Self {
Self {
gas_costs: Default::default(),
consensus_denomination: token::Denomination::from_str("TEST").unwrap(),
consensus_scaling_factor: 1,
min_delegate_amount: 0,
Expand Down Expand Up @@ -194,6 +211,13 @@ pub trait API {
/// Determine consensus height corresponding to the given epoch transition. This query may be
/// expensive in case the epoch is far back.
fn height_for_epoch<C: Context>(ctx: &C, epoch: EpochTime) -> Result<u64, Error>;

/// Round roots return the round roots for the given runtime ID and round.
fn round_roots<C: Context>(
ctx: &C,
runtime_id: Namespace,
round: u64,
) -> Result<Option<RoundRoots>, Error>;
}

pub struct Module;
Expand Down Expand Up @@ -231,6 +255,27 @@ impl Module {
// Set genesis parameters.
Self::set_params(genesis.parameters);
}

#[handler(call = "consensus.RoundRoot", internal)]
fn internal_round_root<C: TxContext>(
ctx: &mut C,
body: types::RoundRootBody,
) -> Result<Option<Hash>, Error> {
let params = Self::params();
<C::Runtime as Runtime>::Core::use_tx_gas(ctx, params.gas_costs.round_root)?;

if !body.kind.is_valid() {
return Err(Error::InvalidArgument);
}

Ok(
Self::round_roots(ctx, body.runtime_id, body.round)?.map(|rr| match body.kind {
types::RootKind::IO => rr.io_root,
types::RootKind::State => rr.state_root,
_ => panic!("invalid root kind"), // Covered by the is_valid check above.
}),
)
}
}

impl API for Module {
Expand Down Expand Up @@ -418,6 +463,17 @@ impl API for Module {
height -= 1;
}
}

fn round_roots<C: Context>(
ctx: &C,
runtime_id: Namespace,
round: u64,
) -> Result<Option<RoundRoots>, Error> {
let roothash = RoothashImmutableState::new(ctx.consensus_state());
roothash
.round_roots(runtime_id, round)
.map_err(Error::InternalStateError)
}
}

impl module::TransactionHandler for Module {}
Expand Down
3 changes: 3 additions & 0 deletions runtime-sdk/src/modules/consensus/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ fn test_query_parameters() {
let mut ctx = mock.create_ctx();

let params = Parameters {
gas_costs: Default::default(),
consensus_denomination: Denomination::NATIVE,
consensus_scaling_factor: 1_000,
min_delegate_amount: 10,
Expand All @@ -480,6 +481,7 @@ fn test_query_parameters() {
fn test_init_bad_scaling_factor_1() {
Consensus::init(Genesis {
parameters: Parameters {
gas_costs: Default::default(),
consensus_denomination: Denomination::NATIVE,
// Zero scaling factor is invalid.
consensus_scaling_factor: 0,
Expand All @@ -493,6 +495,7 @@ fn test_init_bad_scaling_factor_1() {
fn test_init_bad_scaling_factor_2() {
Consensus::init(Genesis {
parameters: Parameters {
gas_costs: Default::default(),
consensus_denomination: Denomination::NATIVE,
// Scaling factor that is not a power of 10 is invalid.
consensus_scaling_factor: 1230,
Expand Down
28 changes: 28 additions & 0 deletions runtime-sdk/src/modules/consensus/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use oasis_core_runtime::common::namespace::Namespace;

/// Kind of root.
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[repr(u8)]
pub enum RootKind {
#[default]
Invalid = 0,
State = 1,
IO = 2,
}

impl RootKind {
/// Whether the root kind is valid.
pub fn is_valid(&self) -> bool {
!matches!(self, Self::Invalid)
}
}

/// Internal round root call body.
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct RoundRootBody {
pub runtime_id: Namespace,
pub round: u64,
pub kind: RootKind,
}
2 changes: 2 additions & 0 deletions tests/e2e/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
2 changes: 1 addition & 1 deletion tests/e2e/contracts/subcall/evm_subcall.abi
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"inputs":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"bytes","name":"module","type":"bytes"}],"name":"SubcallFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_delegatecall","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_spin","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"}]
[{"inputs":[{"internalType":"uint64","name":"code","type":"uint64"},{"internalType":"bytes","name":"module","type":"bytes"}],"name":"SubcallFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"test_consensus_round_root","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_delegatecall","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"method","type":"bytes"},{"internalType":"bytes","name":"body","type":"bytes"}],"name":"test_spin","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"}]
2 changes: 1 addition & 1 deletion tests/e2e/contracts/subcall/evm_subcall.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
608060405234801561000f575f80fd5b5061062e8061001d5f395ff3fe608060405260043610610033575f3560e01c80630c5561a61461003757806323bfb16a1461006057806350d82f381461007f575b5f80fd5b61004a610045366004610377565b61009e565b604051610057919061042b565b60405180910390f35b34801561006b575f80fd5b5061004a61007a366004610377565b610162565b34801561008a575f80fd5b5061004a610099366004610377565b610212565b60605f80610103600160981b016001600160a01b0316878787876040516020016100cb9493929190610465565b60408051601f19818403018152908290526100e59161048b565b5f604051808303815f865af19150503d805f811461011e576040519150601f19603f3d011682016040523d82523d5f602084013e610123565b606091505b50915091508161014e5760405162461bcd60e51b8152600401610145906104a6565b60405180910390fd5b610157816102e1565b979650505050505050565b60605f80610103600160981b016001600160a01b03168787878760405160200161018f9493929190610465565b60408051601f19818403018152908290526101a99161048b565b5f60405180830381855af49150503d805f81146101e1576040519150601f19603f3d011682016040523d82523d5f602084013e6101e6565b606091505b5091509150816102085760405162461bcd60e51b8152600401610145906104a6565b9695505050505050565b60605f80610103600160981b016001600160a01b03168787878760405160200161023f9493929190610465565b60408051601f19818403018152908290526102599161048b565b5f604051808303815f865af19150503d805f8114610292576040519150601f19603f3d011682016040523d82523d5f602084013e610297565b606091505b5091509150816102b95760405162461bcd60e51b8152600401610145906104a6565b5f5b60648112156102d657806102ce816104ce565b9150506102bb565b509695505050505050565b60605f80838060200190518101906102f9919061050c565b915091508167ffffffffffffffff165f1461032b57818160405163575a7c4d60e01b81526004016101459291906105ce565b9392505050565b5f8083601f840112610342575f80fd5b50813567ffffffffffffffff811115610359575f80fd5b602083019150836020828501011115610370575f80fd5b9250929050565b5f805f806040858703121561038a575f80fd5b843567ffffffffffffffff808211156103a1575f80fd5b6103ad88838901610332565b909650945060208701359150808211156103c5575f80fd5b506103d287828801610332565b95989497509550505050565b5f5b838110156103f85781810151838201526020016103e0565b50505f910152565b5f81518084526104178160208601602086016103de565b601f01601f19169290920160200192915050565b602081525f61032b6020830184610400565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f61047860408301868861043d565b828103602084015261015781858761043d565b5f825161049c8184602087016103de565b9190910192915050565b6020808252600e908201526d1cdd5898d85b1b0819985a5b195960921b604082015260600190565b5f6001600160ff1b0182016104f157634e487b7160e01b5f52601160045260245ffd5b5060010190565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561051d575f80fd5b825167ffffffffffffffff8082168214610535575f80fd5b602085015191935080821115610549575f80fd5b818501915085601f83011261055c575f80fd5b81518181111561056e5761056e6104f8565b604051601f8201601f19908116603f01168101908382118183101715610596576105966104f8565b816040528281528860208487010111156105ae575f80fd5b6105bf8360208301602088016103de565b80955050505050509250929050565b67ffffffffffffffff83168152604060208201525f6105f06040830184610400565b94935050505056fea26469706673582212202369658d710f3f71613ae8645e6c9ac9e88e7736389b455e83675353c8aed56664736f6c63430008150033
608060405234801561000f575f80fd5b506108308061001d5f395ff3fe60806040526004361061003e575f3560e01c80630c5561a61461004257806323bfb16a1461006b57806350d82f381461008a578063c7391861146100a9575b5f80fd5b61005561005036600461057e565b6100bd565b6040516100629190610632565b60405180910390f35b348015610076575f80fd5b5061005561008536600461057e565b610181565b348015610095575f80fd5b506100556100a436600461057e565b610231565b3480156100b4575f80fd5b506100556102f6565b60605f80610103600160981b016001600160a01b0316878787876040516020016100ea949392919061066c565b60408051601f198184030181529082905261010491610692565b5f604051808303815f865af19150503d805f811461013d576040519150601f19603f3d011682016040523d82523d5f602084013e610142565b606091505b50915091508161016d5760405162461bcd60e51b8152600401610164906106ad565b60405180910390fd5b610176816104e8565b979650505050505050565b60605f80610103600160981b016001600160a01b0316878787876040516020016101ae949392919061066c565b60408051601f19818403018152908290526101c891610692565b5f60405180830381855af49150503d805f8114610200576040519150601f19603f3d011682016040523d82523d5f602084013e610205565b606091505b5091509150816102275760405162461bcd60e51b8152600401610164906106ad565b9695505050505050565b60605f80610103600160981b016001600160a01b03168787878760405160200161025e949392919061066c565b60408051601f198184030181529082905261027891610692565b5f604051808303815f865af19150503d805f81146102b1576040519150601f19603f3d011682016040523d82523d5f602084013e6102b6565b606091505b5091509150816102d85760405162461bcd60e51b8152600401610164906106ad565b5f5b60648112156102eb576001016102da565b509695505050505050565b604080518082018252601381527218dbdb9cd95b9cdd5ccb949bdd5b99149bdbdd606a1b602080830191909152915160a360f81b92810192909252601960fa1b6021830152631ada5b9960e21b6022830152600160f81b6026830152606560f81b6027830152641c9bdd5b9960da1b6028830152600160f91b602d830152603560f91b602e830152691c9d5b9d1a5b5957da5960b21b602f830152600b60fb1b6039830152600160fd1b603a830152600160ff1b603b8301526060915f918291610103600160981b019190605b0160408051601f19818403018152908290526103e292916020016106d5565b60408051601f19818403018152908290526103fc91610692565b5f604051808303815f865af19150503d805f8114610435576040519150601f19603f3d011682016040523d82523d5f602084013e61043a565b606091505b5091509150816104985760405162461bcd60e51b815260206004820152602360248201527f636f6e73656e73757320726f756e6420726f6f742073756263616c6c206661696044820152621b195960ea1b6064820152608401610164565b5f80828060200190518101906104ae9190610716565b915091508167ffffffffffffffff165f146104e057818160405163575a7c4d60e01b81526004016101649291906107d8565b949350505050565b60605f80838060200190518101906105009190610716565b915091508167ffffffffffffffff165f1461053257818160405163575a7c4d60e01b81526004016101649291906107d8565b9392505050565b5f8083601f840112610549575f80fd5b50813567ffffffffffffffff811115610560575f80fd5b602083019150836020828501011115610577575f80fd5b9250929050565b5f805f8060408587031215610591575f80fd5b843567ffffffffffffffff808211156105a8575f80fd5b6105b488838901610539565b909650945060208701359150808211156105cc575f80fd5b506105d987828801610539565b95989497509550505050565b5f5b838110156105ff5781810151838201526020016105e7565b50505f910152565b5f815180845261061e8160208601602086016105e5565b601f01601f19169290920160200192915050565b602081525f6105326020830184610607565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f61067f604083018688610644565b8281036020840152610176818587610644565b5f82516106a38184602087016105e5565b9190910192915050565b6020808252600e908201526d1cdd5898d85b1b0819985a5b195960921b604082015260600190565b604081525f6106e76040830185610607565b82810360208401526106f98185610607565b95945050505050565b634e487b7160e01b5f52604160045260245ffd5b5f8060408385031215610727575f80fd5b825167ffffffffffffffff808216821461073f575f80fd5b602085015191935080821115610753575f80fd5b818501915085601f830112610766575f80fd5b81518181111561077857610778610702565b604051601f8201601f19908116603f011681019083821181831017156107a0576107a0610702565b816040528281528860208487010111156107b8575f80fd5b6107c98360208301602088016105e5565b80955050505050509250929050565b67ffffffffffffffff83168152604060208201525f6104e0604083018461060756fea264697066735822122029a34c1a7cda70295558c88edd184a88d6c65683f77f25fde5fae8ad4f1f735864736f6c63430008170033
76 changes: 67 additions & 9 deletions tests/e2e/contracts/subcall/evm_subcall.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,91 @@
pragma solidity ^0.8.0;

contract Test {
address private constant SUBCALL = 0x0100000000000000000000000000000000000103;
bytes32 private constant TEST_RUNTIME_ID =
0x8000000000000000000000000000000000000000000000000000000000000000;
string private constant CONSENSUS_ROUND_ROOT = "consensus.RoundRoot";

uint8 private constant ROOT_ROUND_KIND_STATE = 1;

address private constant SUBCALL =
0x0100000000000000000000000000000000000103;

error SubcallFailed(uint64 code, bytes module);

function test(bytes calldata method, bytes calldata body) public payable returns (bytes memory) {
(bool success, bytes memory data) = SUBCALL.call(abi.encode(method, body));
function test(
bytes calldata method,
bytes calldata body
) public payable returns (bytes memory) {
(bool success, bytes memory data) = SUBCALL.call(
abi.encode(method, body)
);
require(success, "subcall failed");
return decodeResponse(data);
}

function test_delegatecall(bytes calldata method, bytes calldata body) public returns (bytes memory) {
(bool success, bytes memory data) = SUBCALL.delegatecall(abi.encode(method, body));
function test_delegatecall(
bytes calldata method,
bytes calldata body
) public returns (bytes memory) {
(bool success, bytes memory data) = SUBCALL.delegatecall(
abi.encode(method, body)
);
require(success, "subcall failed");
return data;
}

function test_spin(bytes calldata method, bytes calldata body) public returns (bytes memory) {
(bool success, bytes memory data) = SUBCALL.call(abi.encode(method, body));
function test_spin(
bytes calldata method,
bytes calldata body
) public returns (bytes memory) {
(bool success, bytes memory data) = SUBCALL.call(
abi.encode(method, body)
);
require(success, "subcall failed");
for (int i = 0; i < 100; i++) {
// Spin.
}
return data;
}

function decodeResponse(bytes memory raw) internal pure returns (bytes memory) {
(uint64 status_code, bytes memory data) = abi.decode(raw, (uint64, bytes));
function test_consensus_round_root() public returns (bytes memory) {
(bool success, bytes memory data) = SUBCALL.call(
abi.encode(
CONSENSUS_ROUND_ROOT,
abi.encodePacked(
hex"a3",
hex"64",
"kind",
ROOT_ROUND_KIND_STATE, // Only works for values <= 23.
hex"65",
"round",
uint8(2), // Only works for values <= 23.
hex"6a",
"runtime_id",
hex"58",
hex"20",
TEST_RUNTIME_ID
)
)
);
require(success, "consensus round root subcall failed");
(uint64 status, bytes memory result) = abi.decode(
data,
(uint64, bytes)
);
if (status != 0) {
revert SubcallFailed(status, result);
}
return result;
}

function decodeResponse(
bytes memory raw
) internal pure returns (bytes memory) {
(uint64 status_code, bytes memory data) = abi.decode(
raw,
(uint64, bytes)
);

if (status_code != 0) {
revert SubcallFailed(status_code, data);
Expand Down
Loading

0 comments on commit 4dd442c

Please sign in to comment.