Skip to content

Commit 10960d0

Browse files
committed
feat: add mapping from mycelium identity to twin identity
- add storage map from myc->twin - add setter method to update the storage which emit TwinMyceliumPkSet event - update rs/ts/go clients with setter/getter methods
1 parent 77e684b commit 10960d0

File tree

8 files changed

+223
-2
lines changed

8 files changed

+223
-2
lines changed

clients/tfchain-client-go/twin.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,53 @@ func (s *Substrate) UpdateTwin(identity Identity, relay string, pk []byte) (uint
182182

183183
return s.GetTwinByPubKey(identity.PublicKey())
184184
}
185+
186+
// SetTwinMyceliumPK sets the mycelium PK for a twin
187+
func (s *Substrate) SetTwinMyceliumPK(identity Identity, twinID uint32, myceliumPK []byte) error {
188+
cl, meta, err := s.GetClient()
189+
if err != nil {
190+
return err
191+
}
192+
193+
c, err := types.NewCall(meta, "TfgridModule.set_twin_mycelium_pk", twinID, myceliumPK)
194+
if err != nil {
195+
return errors.Wrap(err, "failed to create call")
196+
}
197+
198+
if _, err := s.Call(cl, meta, identity, c); err != nil {
199+
return errors.Wrap(err, "failed to set twin mycelium pk")
200+
}
201+
202+
return nil
203+
}
204+
205+
// GetTwinIdByMyceliumPK gets twin id by mycelium PK
206+
func (s *Substrate) GetTwinIdByMyceliumPK(myceliumPK []byte) (*Twin, error) {
207+
cl, meta, err := s.GetClient()
208+
if err != nil {
209+
return nil, err
210+
}
211+
212+
// must use encode to convert byte slice to encoded format
213+
bytes, err := Encode(myceliumPK)
214+
if err != nil {
215+
return nil, errors.Wrap(err, "substrate: encoding error building query arguments")
216+
}
217+
218+
key, err := types.CreateStorageKey(meta, "TfgridModule", "TwinByMyceliumPk", bytes, nil)
219+
if err != nil {
220+
return nil, errors.Wrap(err, "failed to create substrate query key")
221+
}
222+
223+
var twinID types.U32
224+
ok, err := cl.RPC.State.GetStorageLatest(key, &twinID)
225+
if err != nil {
226+
return nil, errors.Wrap(err, "failed to lookup twin by mycelium pk")
227+
}
228+
229+
if !ok || twinID == 0 {
230+
return nil, errors.Wrap(ErrNotFound, "twin not found for mycelium pk")
231+
}
232+
233+
return s.GetTwin(uint32(twinID))
234+
}

clients/tfchain-client-js/lib/client.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const {
1010
} = require('./entity')
1111
const {
1212
createTwin, getTwin, getTwinIdByAccountId, updateTwin,
13-
deleteTwin, addTwinEntity, deleteTwinEntity, listTwins
13+
deleteTwin, addTwinEntity, deleteTwinEntity, listTwins,
14+
setTwinMyceliumPK, getTwinByMyceliumPK
1415
} = require('./twin')
1516
const {
1617
createFarm, getFarm, deleteFarm,
@@ -161,6 +162,14 @@ class Client {
161162
return deleteTwinEntity(this, twinID, entityID, callback)
162163
}
163164

165+
async setTwinMyceliumPK(twinId, myceliumPk, callback) {
166+
return setTwinMyceliumPK(this, twinId, myceliumPk, callback)
167+
}
168+
169+
async getTwinByMyceliumPK(myceliumPk) {
170+
return getTwinByMyceliumPK(this, myceliumPk)
171+
}
172+
164173
async createFarm(name, certificationType, publicIPs, callback) {
165174
return createFarm(this, name, certificationType, publicIPs, callback)
166175
}

clients/tfchain-client-js/lib/twin.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,28 @@ async function deleteTwin (self, id, callback) {
9292
.signAndSend(self.key, { nonce }, callback)
9393
}
9494

95+
// setTwinMyceliumPK sets the mycelium public key for a twin
96+
async function setTwinMyceliumPK (self, twinId, myceliumPk, callback) {
97+
const setMyceliumPk = self.api.tx.tfgridModule.setTwinMyceliumPk(twinId, myceliumPk)
98+
const nonce = await self.api.rpc.system.accountNextIndex(self.address)
99+
100+
return setMyceliumPk.signAndSend(self.key, { nonce }, callback)
101+
}
102+
103+
// getTwinByMyceliumPK gets a twin by mycelium public key
104+
async function getTwinByMyceliumPK (self, myceliumPk) {
105+
// First, get the twin ID from the mycelium PK mapping
106+
const twinIdResult = await self.api.query.tfgridModule.twinByMyceliumPk(myceliumPk)
107+
const twinId = twinIdResult.toJSON()
108+
109+
if (!twinId || twinId === 0) {
110+
throw Error(`Couldn't find a twin for mycelium pk: ${myceliumPk}`)
111+
}
112+
113+
// Now get the full twin object using the twin ID
114+
return getTwin(self, twinId)
115+
}
116+
95117
module.exports = {
96118
createTwin,
97119
updateTwin,
@@ -100,5 +122,7 @@ module.exports = {
100122
deleteTwin,
101123
addTwinEntity,
102124
deleteTwinEntity,
103-
listTwins
125+
listTwins,
126+
setTwinMyceliumPK,
127+
getTwinByMyceliumPK
104128
}

clients/tfchain-client-rs/src/client.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,26 @@ impl Client {
152152
current::get_twin_id_by_account(self, account).await
153153
}
154154

155+
// Sets mycelium public key for a twin and checks for success, blockhash is returned on success
156+
pub async fn set_twin_mycelium_pk(
157+
&self,
158+
kp: &KeyPair,
159+
twin_id: u32,
160+
mycelium_pk: Vec<u8>,
161+
) -> Result<Hash, Error> {
162+
current::set_twin_mycelium_pk(self, kp, twin_id, mycelium_pk).await
163+
}
164+
165+
// Gets a twin by mycelium public key and returns the full twin object
166+
pub async fn get_twin_by_mycelium_pk(
167+
&self,
168+
mycelium_pk: Vec<u8>,
169+
) -> Result<Option<Twin>, Error> {
170+
// We pass a dummy keypair since it's not used in the implementation
171+
let dummy_kp = KeyPair::Sr25519(sr25519::Pair::from_seed(&[0u8; 32]));
172+
current::get_twin_by_mycelium_pk(self, &dummy_kp, mycelium_pk).await
173+
}
174+
155175
pub async fn get_farm_by_id(&self, id: u32) -> Result<Option<Farm>, Error> {
156176
current::get_farm_by_id(self, id).await
157177
}

clients/tfchain-client-rs/src/runtimes/current.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,48 @@ pub async fn get_balance(
201201
.fetch(&current::storage().system().account(account))
202202
.await?)
203203
}
204+
205+
pub async fn set_twin_mycelium_pk(
206+
cl: &Client,
207+
kp: &KeyPair,
208+
twin_id: u32,
209+
mycelium_pk: Vec<u8>,
210+
) -> Result<H256, Error> {
211+
let set_mycelium_pk_tx = current::tx().tfgrid_module().set_twin_mycelium_pk(
212+
twin_id,
213+
BoundedVec(mycelium_pk),
214+
);
215+
216+
let signer = kp.signer();
217+
218+
let set_mycelium_pk = cl
219+
.api
220+
.tx()
221+
.sign_and_submit_then_watch_default(&set_mycelium_pk_tx, &signer)
222+
.await?
223+
.wait_for_finalized_success()
224+
.await?;
225+
226+
Ok(set_mycelium_pk.block_hash())
227+
}
228+
229+
pub async fn get_twin_by_mycelium_pk(
230+
cl: &Client,
231+
_kp: &KeyPair,
232+
mycelium_pk: Vec<u8>,
233+
) -> Result<Option<Twin>, Error> {
234+
// Query the storage directly using the mycelium pk to get twin ID
235+
let twin_id = cl
236+
.api
237+
.storage()
238+
.at_latest()
239+
.await?
240+
.fetch(&current::storage().tfgrid_module().twin_by_mycelium_pk(BoundedVec(mycelium_pk)))
241+
.await?;
242+
243+
if let Some(id) = twin_id {
244+
get_twin_by_id(cl, id).await
245+
} else {
246+
Ok(None)
247+
}
248+
}

substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,17 @@ benchmarks! {
737737
assert_last_event::<T>(Event::NodeUptimeReported(node_id, now, uptime).into());
738738
}
739739

740+
// set_twin_mycelium_pk
741+
set_twin_mycelium_pk {
742+
let caller: T::AccountId = whitelisted_caller();
743+
_create_twin::<T>(caller.clone());
744+
let twin_id = 1;
745+
let mycelium_pk = get_mycelium_pk_input(b"some_mycelium_pk");
746+
}: _(RawOrigin::Signed(caller), twin_id, mycelium_pk.clone())
747+
verify {
748+
assert_eq!(TfgridModule::<T>::twin_by_mycelium_pk(&mycelium_pk), Some(twin_id));
749+
}
750+
740751
// Calling the `impl_benchmark_test_suite` macro inside the `benchmarks`
741752
// block will generate one #[test] function per benchmark
742753
impl_benchmark_test_suite!(TfgridModule, crate::mock::new_test_ext(), crate::mock::TestRuntime)

substrate-node/pallets/pallet-tfgrid/src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ pub mod pallet {
146146
// Concrete type for entity
147147
pub type TfgridEntity<T> = types::Entity<AccountIdOf<T>, CityNameOf<T>, CountryNameOf<T>>;
148148

149+
pub type MyceliumPkInput = BoundedVec<u8, ConstU32<{ types::MAX_PK_LENGTH }>>;
150+
149151
#[pallet::storage]
150152
#[pallet::getter(fn nodes)]
151153
pub type Nodes<T> = StorageMap<_, Blake2_128Concat, u32, TfgridNode<T>, OptionQuery>;
@@ -263,6 +265,16 @@ pub mod pallet {
263265
ValueQuery,
264266
>;
265267

268+
#[pallet::storage]
269+
#[pallet::getter(fn twin_by_mycelium_pk)]
270+
pub type TwinByMyceliumPk<T: Config> = StorageMap<
271+
_,
272+
Blake2_128Concat,
273+
MyceliumPkInput, // key is mycelium pk
274+
u32, // value is twin id
275+
OptionQuery, // returns None if pk not found
276+
>;
277+
266278
#[pallet::config]
267279
pub trait Config: frame_system::Config + pallet_timestamp::Config {
268280
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
@@ -411,6 +423,7 @@ pub mod pallet {
411423
TwinEntityRemoved(u32, u32),
412424
TwinDeleted(u32),
413425
TwinAccountBounded(u32, T::AccountId),
426+
TwinMyceliumPkSet(u32, MyceliumPkInput),
414427

415428
PricingPolicyStored(types::PricingPolicy<T::AccountId>),
416429
// CertificationCodeStored(types::CertificationCodes),
@@ -1238,5 +1251,27 @@ pub mod pallet {
12381251
// Deprecated! Use index 40 for next extrinsic
12391252
// #[pallet::call_index(39)]
12401253
// #[pallet::weight(<T as Config>::WeightInfo::set_node_gpu_status())]
1254+
1255+
#[pallet::call_index(40)]
1256+
#[pallet::weight(<T as Config>::WeightInfo::set_twin_mycelium_pk())]
1257+
pub fn set_twin_mycelium_pk(
1258+
origin: OriginFor<T>,
1259+
twin_id: u32,
1260+
mycelium_pk: MyceliumPkInput,
1261+
) -> DispatchResultWithPostInfo {
1262+
let account_id = ensure_signed(origin)?;
1263+
1264+
// Ensure the caller owns this twin
1265+
let twin = Twins::<T>::get(twin_id).ok_or(Error::<T>::TwinNotExists)?;
1266+
ensure!(twin.account_id == account_id, Error::<T>::UnauthorizedToUpdateTwin);
1267+
1268+
// Store the mapping
1269+
TwinByMyceliumPk::<T>::insert(&mycelium_pk, twin_id);
1270+
1271+
// Emit event that mycelium-twin mapping is updated
1272+
Self::deposit_event(Event::TwinMyceliumPkSet(twin_id, mycelium_pk));
1273+
1274+
Ok(().into())
1275+
}
12411276
}
12421277
}

substrate-node/pallets/pallet-tfgrid/src/weights.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub trait WeightInfo {
6565
fn change_power_target() -> Weight;
6666
fn bond_twin_account() -> Weight;
6767
fn report_uptime_v2() -> Weight;
68+
fn set_twin_mycelium_pk() -> Weight;
6869
}
6970

7071
/// Weights for pallet_tfgrid using the Substrate node and recommended hardware.
@@ -548,6 +549,19 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
548549
Weight::from_parts(18_465_000, 3919)
549550
.saturating_add(T::DbWeight::get().reads(4_u64))
550551
}
552+
/// Storage: `TfgridModule::Twins` (r:1 w:0)
553+
/// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`)
554+
/// Storage: `TfgridModule::TwinByMyceliumPk` (r:0 w:1)
555+
/// Proof: `TfgridModule::TwinByMyceliumPk` (`max_values`: None, `max_size`: None, mode: `Measured`)
556+
fn set_twin_mycelium_pk() -> Weight {
557+
// Proof Size summary in bytes:
558+
// Measured: `387`
559+
// Estimated: `3852`
560+
// Minimum execution time: 11_743_000 picoseconds.
561+
Weight::from_parts(12_053_000, 3852)
562+
.saturating_add(T::DbWeight::get().reads(1_u64))
563+
.saturating_add(T::DbWeight::get().writes(1_u64))
564+
}
551565
}
552566

553567
// For backwards compatibility and tests
@@ -1030,4 +1044,17 @@ impl WeightInfo for () {
10301044
Weight::from_parts(18_465_000, 3919)
10311045
.saturating_add(RocksDbWeight::get().reads(4_u64))
10321046
}
1047+
/// Storage: `TfgridModule::Twins` (r:1 w:0)
1048+
/// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`)
1049+
/// Storage: `TfgridModule::TwinByMyceliumPk` (r:0 w:1)
1050+
/// Proof: `TfgridModule::TwinByMyceliumPk` (`max_values`: None, `max_size`: None, mode: `Measured`)
1051+
fn set_twin_mycelium_pk() -> Weight {
1052+
// Proof Size summary in bytes:
1053+
// Measured: `387`
1054+
// Estimated: `3852`
1055+
// Minimum execution time: 11_743_000 picoseconds.
1056+
Weight::from_parts(12_053_000, 3852)
1057+
.saturating_add(RocksDbWeight::get().reads(1_u64))
1058+
.saturating_add(RocksDbWeight::get().writes(1_u64))
1059+
}
10331060
}

0 commit comments

Comments
 (0)