Skip to content

Commit

Permalink
Merge pull request #1958 from oasisprotocol/kostko/feature/rofl-appid…
Browse files Browse the repository at this point in the history
…-scheme

runtime-sdk/modules/rofl: Add a deterministic ID generation scheme
  • Loading branch information
kostko authored Aug 28, 2024
2 parents 76d4413 + f18d394 commit 68e084b
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 5 deletions.
14 changes: 14 additions & 0 deletions client-sdk/go/modules/rofl/app_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
var (
// AppIDV0CRIContext is the unique context for v0 creator/round/index application identifiers.
AppIDV0CRIContext = address.NewContext("oasis-sdk/rofl: cri app id", 0)
// AppIDV0CNContext is the unique context for v0 creator/nonce application identifiers.
AppIDV0CNContext = address.NewContext("oasis-sdk/rofl: cn app id", 0)
// AppIDV0GlobalNameContext is the unique context for v0 global name application identifiers.
AppIDV0GlobalNameContext = address.NewContext("oasis-sdk/rofl: global name app id", 0)
// AppIDBech32HRP is the unique human readable part of Bech32 encoded application identifiers.
Expand Down Expand Up @@ -75,6 +77,18 @@ func NewAppIDCreatorRoundIndex(creator types.Address, round uint64, index uint32
return NewAppIDRaw(AppIDV0CRIContext, data)
}

// NewAppIDCreatorNonce creates a new application identifier from the given creator/nonce tuple.
func NewAppIDCreatorNonce(creator types.Address, nonce uint64) AppID {
data := make([]byte, address.Size+8)

rawCreator, _ := creator.MarshalBinary()
copy(data[:address.Size], rawCreator)

binary.BigEndian.PutUint64(data[address.Size:], nonce)

return NewAppIDRaw(AppIDV0CNContext, data)
}

// NewAppIDGlobalName creates a new application identifier from the given global name.
func NewAppIDGlobalName(name string) AppID {
return NewAppIDRaw(AppIDV0GlobalNameContext, []byte(name))
Expand Down
9 changes: 9 additions & 0 deletions client-sdk/go/modules/rofl/app_id_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ func TestIdentifierV0(t *testing.T) {
appID = NewAppIDCreatorRoundIndex(sdkTesting.Bob.Address, 42, 1)
require.Equal("rofl1qzmh56f52yd0tcqh757fahzc7ec49s8kaguyylvu", appID.String())

appID = NewAppIDCreatorNonce(sdkTesting.Alice.Address, 0)
require.Equal("rofl1qqxxv77j6qy3rh50ah9kxehsh26e2hf7p5r6kwsq", appID.String())

appID = NewAppIDCreatorNonce(sdkTesting.Alice.Address, 42)
require.Equal("rofl1qr90w0m8j7h34c2hhpfmg2wgqmtu0q82vyaxv6e0", appID.String())

appID = NewAppIDCreatorNonce(sdkTesting.Bob.Address, 0)
require.Equal("rofl1qqzuxsh8fkga366kxrze8vpltdjge3rc7qg6tlrg", appID.String())

appID = NewAppIDGlobalName("test global app")
require.Equal("rofl1qrev5wq76npkmcv5wxkdxxcu4dhmu704yyl30h43", appID.String())
}
10 changes: 10 additions & 0 deletions client-sdk/go/modules/rofl/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,18 @@ import (
type Create struct {
// Policy is the application authentication policy.
Policy AppAuthPolicy `json:"policy"`
// Scheme is the identifier generation scheme.
Scheme IdentifierScheme `json:"scheme"`
}

// IdentifierScheme is a ROFL application identifier generation scheme.
type IdentifierScheme uint8

const (
CreatorRoundIndex IdentifierScheme = 0
CreatorNonce IdentifierScheme = 1
)

// Update an existing ROFL application call.
type Update struct {
// ID is the application identifier.
Expand Down
43 changes: 43 additions & 0 deletions runtime-sdk/src/modules/rofl/app_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const APP_ID_SIZE: usize = APP_ID_VERSION_SIZE + APP_ID_DATA_SIZE;
const APP_ID_V0_VERSION: u8 = 0;
/// Creator/round/index identifier context.
const APP_ID_CRI_CONTEXT: &[u8] = b"oasis-sdk/rofl: cri app id";
/// Creator/nonce identifier context.
const APP_ID_CN_CONTEXT: &[u8] = b"oasis-sdk/rofl: cn app id";
/// Global name identifier context.
const APP_ID_GLOBAL_NAME_CONTEXT: &[u8] = b"oasis-sdk/rofl: global name app id";

Expand Down Expand Up @@ -66,6 +68,15 @@ impl AppId {
)
}

/// Creates a new v0 application identifier from creator/nonce tuple.
pub fn from_creator_nonce(creator: Address, nonce: u64) -> Self {
Self::new(
APP_ID_CN_CONTEXT,
APP_ID_V0_VERSION,
&[creator.as_ref(), &nonce.to_be_bytes()].concat(),
)
}

/// Tries to create a new identifier from raw bytes.
pub fn from_bytes(data: &[u8]) -> Result<Self, Error> {
if data.len() != APP_ID_SIZE {
Expand Down Expand Up @@ -213,6 +224,38 @@ mod test {
"rofl1qzmh56f52yd0tcqh757fahzc7ec49s8kaguyylvu"
);

let creator = keys::alice::address();
let app_id = AppId::from_creator_nonce(creator, 0);

assert_eq!(
app_id.to_bech32(),
"rofl1qqxxv77j6qy3rh50ah9kxehsh26e2hf7p5r6kwsq"
);

let creator = keys::alice::address();
let app_id = AppId::from_creator_nonce(creator, 1);

assert_eq!(
app_id.to_bech32(),
"rofl1qqfuf7u556prwv0wkdt398prhrpat7r3rvr97khf"
);

let creator = keys::alice::address();
let app_id = AppId::from_creator_nonce(creator, 42);

assert_eq!(
app_id.to_bech32(),
"rofl1qr90w0m8j7h34c2hhpfmg2wgqmtu0q82vyaxv6e0"
);

let creator = keys::bob::address();
let app_id = AppId::from_creator_nonce(creator, 0);

assert_eq!(
app_id.to_bech32(),
"rofl1qqzuxsh8fkga366kxrze8vpltdjge3rc7qg6tlrg"
);

let app_id = AppId::from_global_name("test global app");

assert_eq!(
Expand Down
17 changes: 12 additions & 5 deletions runtime-sdk/src/modules/rofl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,18 @@ impl<Cfg: Config> Module<Cfg> {

let (creator, tx_index) =
CurrentState::with_env(|env| (env.tx_caller_address(), env.tx_index()));
let app_id = app_id::AppId::from_creator_round_index(
creator,
ctx.runtime_header().round,
tx_index.try_into().map_err(|_| Error::InvalidArgument)?,
);
let app_id = match body.scheme {
types::IdentifierScheme::CreatorRoundIndex => app_id::AppId::from_creator_round_index(
creator,
ctx.runtime_header().round,
tx_index.try_into().map_err(|_| Error::InvalidArgument)?,
),
types::IdentifierScheme::CreatorNonce => {
let nonce = <C::Runtime as Runtime>::Accounts::get_nonce(creator)?;

app_id::AppId::from_creator_nonce(creator, nonce)
}
};

// Sanity check that the application doesn't already exist.
if state::get_app(app_id).is_some() {
Expand Down
25 changes: 25 additions & 0 deletions runtime-sdk/src/modules/rofl/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ fn test_management_ops() {

let create = types::Create {
policy: Default::default(),
scheme: Default::default(),
};

// Bob attempts to create a new ROFL application, but he doesn't have enough to stake.
Expand Down Expand Up @@ -206,3 +207,27 @@ fn test_management_ops() {
let balance = Accounts::get_balance(*ADDRESS_APP_STAKE_POOL, Denomination::NATIVE).unwrap();
assert_eq!(balance, 1_000); // One application remains.
}

#[test]
fn test_create_scheme() {
let mut mock = mock::Mock::default();
let ctx = mock.create_ctx_for_runtime::<TestRuntime>(false);

TestRuntime::migrate(&ctx);

let create = types::Create {
policy: Default::default(),
scheme: types::IdentifierScheme::CreatorNonce,
};

let mut signer_alice = mock::Signer::new(0, keys::alice::sigspec());
let dispatch_result = signer_alice.call(&ctx, "rofl.Create", create.clone());
assert!(dispatch_result.result.is_success(), "call should succeed");

// Ensure the correct application ID has been created.
let app_id: AppId = cbor::from_value(dispatch_result.result.unwrap()).unwrap();
assert_eq!(
app_id.to_bech32(),
"rofl1qqfuf7u556prwv0wkdt398prhrpat7r3rvr97khf"
);
}
11 changes: 11 additions & 0 deletions runtime-sdk/src/modules/rofl/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ use super::{app_id::AppId, policy::AppAuthPolicy};
pub struct Create {
/// Application authentication policy.
pub policy: AppAuthPolicy,
/// Identifier generation scheme.
pub scheme: IdentifierScheme,
}

/// ROFL application identifier generation scheme.
#[derive(Clone, Copy, Debug, Default, cbor::Encode, cbor::Decode)]
#[repr(u8)]
pub enum IdentifierScheme {
#[default]
CreatorRoundIndex = 0,
CreatorNonce = 1,
}

/// Update an existing ROFL application call.
Expand Down

0 comments on commit 68e084b

Please sign in to comment.