From 10960d0ac7a594deb3689b8d2a1853230b100842 Mon Sep 17 00:00:00 2001 From: Omar Abdulaziz Date: Sat, 31 May 2025 05:03:29 +0300 Subject: [PATCH 1/3] 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 --- clients/tfchain-client-go/twin.go | 50 +++++++++++++++++++ clients/tfchain-client-js/lib/client.js | 11 +++- clients/tfchain-client-js/lib/twin.js | 26 +++++++++- clients/tfchain-client-rs/src/client.rs | 20 ++++++++ .../tfchain-client-rs/src/runtimes/current.rs | 45 +++++++++++++++++ .../pallets/pallet-tfgrid/src/benchmarking.rs | 11 ++++ .../pallets/pallet-tfgrid/src/lib.rs | 35 +++++++++++++ .../pallets/pallet-tfgrid/src/weights.rs | 27 ++++++++++ 8 files changed, 223 insertions(+), 2 deletions(-) diff --git a/clients/tfchain-client-go/twin.go b/clients/tfchain-client-go/twin.go index c4600e3b3..ce5dc320c 100644 --- a/clients/tfchain-client-go/twin.go +++ b/clients/tfchain-client-go/twin.go @@ -182,3 +182,53 @@ func (s *Substrate) UpdateTwin(identity Identity, relay string, pk []byte) (uint return s.GetTwinByPubKey(identity.PublicKey()) } + +// SetTwinMyceliumPK sets the mycelium PK for a twin +func (s *Substrate) SetTwinMyceliumPK(identity Identity, twinID uint32, myceliumPK []byte) error { + cl, meta, err := s.GetClient() + if err != nil { + return err + } + + c, err := types.NewCall(meta, "TfgridModule.set_twin_mycelium_pk", twinID, myceliumPK) + if err != nil { + return errors.Wrap(err, "failed to create call") + } + + if _, err := s.Call(cl, meta, identity, c); err != nil { + return errors.Wrap(err, "failed to set twin mycelium pk") + } + + return nil +} + +// GetTwinIdByMyceliumPK gets twin id by mycelium PK +func (s *Substrate) GetTwinIdByMyceliumPK(myceliumPK []byte) (*Twin, error) { + cl, meta, err := s.GetClient() + if err != nil { + return nil, err + } + + // must use encode to convert byte slice to encoded format + bytes, err := Encode(myceliumPK) + if err != nil { + return nil, errors.Wrap(err, "substrate: encoding error building query arguments") + } + + key, err := types.CreateStorageKey(meta, "TfgridModule", "TwinByMyceliumPk", bytes, nil) + if err != nil { + return nil, errors.Wrap(err, "failed to create substrate query key") + } + + var twinID types.U32 + ok, err := cl.RPC.State.GetStorageLatest(key, &twinID) + if err != nil { + return nil, errors.Wrap(err, "failed to lookup twin by mycelium pk") + } + + if !ok || twinID == 0 { + return nil, errors.Wrap(ErrNotFound, "twin not found for mycelium pk") + } + + return s.GetTwin(uint32(twinID)) +} diff --git a/clients/tfchain-client-js/lib/client.js b/clients/tfchain-client-js/lib/client.js index c70fe6aec..a3cbd7bea 100644 --- a/clients/tfchain-client-js/lib/client.js +++ b/clients/tfchain-client-js/lib/client.js @@ -10,7 +10,8 @@ const { } = require('./entity') const { createTwin, getTwin, getTwinIdByAccountId, updateTwin, - deleteTwin, addTwinEntity, deleteTwinEntity, listTwins + deleteTwin, addTwinEntity, deleteTwinEntity, listTwins, + setTwinMyceliumPK, getTwinByMyceliumPK } = require('./twin') const { createFarm, getFarm, deleteFarm, @@ -161,6 +162,14 @@ class Client { return deleteTwinEntity(this, twinID, entityID, callback) } + async setTwinMyceliumPK(twinId, myceliumPk, callback) { + return setTwinMyceliumPK(this, twinId, myceliumPk, callback) + } + + async getTwinByMyceliumPK(myceliumPk) { + return getTwinByMyceliumPK(this, myceliumPk) + } + async createFarm(name, certificationType, publicIPs, callback) { return createFarm(this, name, certificationType, publicIPs, callback) } diff --git a/clients/tfchain-client-js/lib/twin.js b/clients/tfchain-client-js/lib/twin.js index 9bd2299fe..4d824db17 100644 --- a/clients/tfchain-client-js/lib/twin.js +++ b/clients/tfchain-client-js/lib/twin.js @@ -92,6 +92,28 @@ async function deleteTwin (self, id, callback) { .signAndSend(self.key, { nonce }, callback) } +// setTwinMyceliumPK sets the mycelium public key for a twin +async function setTwinMyceliumPK (self, twinId, myceliumPk, callback) { + const setMyceliumPk = self.api.tx.tfgridModule.setTwinMyceliumPk(twinId, myceliumPk) + const nonce = await self.api.rpc.system.accountNextIndex(self.address) + + return setMyceliumPk.signAndSend(self.key, { nonce }, callback) +} + +// getTwinByMyceliumPK gets a twin by mycelium public key +async function getTwinByMyceliumPK (self, myceliumPk) { + // First, get the twin ID from the mycelium PK mapping + const twinIdResult = await self.api.query.tfgridModule.twinByMyceliumPk(myceliumPk) + const twinId = twinIdResult.toJSON() + + if (!twinId || twinId === 0) { + throw Error(`Couldn't find a twin for mycelium pk: ${myceliumPk}`) + } + + // Now get the full twin object using the twin ID + return getTwin(self, twinId) +} + module.exports = { createTwin, updateTwin, @@ -100,5 +122,7 @@ module.exports = { deleteTwin, addTwinEntity, deleteTwinEntity, - listTwins + listTwins, + setTwinMyceliumPK, + getTwinByMyceliumPK } diff --git a/clients/tfchain-client-rs/src/client.rs b/clients/tfchain-client-rs/src/client.rs index 61a9d0e5c..19758e407 100644 --- a/clients/tfchain-client-rs/src/client.rs +++ b/clients/tfchain-client-rs/src/client.rs @@ -152,6 +152,26 @@ impl Client { current::get_twin_id_by_account(self, account).await } + // Sets mycelium public key for a twin and checks for success, blockhash is returned on success + pub async fn set_twin_mycelium_pk( + &self, + kp: &KeyPair, + twin_id: u32, + mycelium_pk: Vec, + ) -> Result { + current::set_twin_mycelium_pk(self, kp, twin_id, mycelium_pk).await + } + + // Gets a twin by mycelium public key and returns the full twin object + pub async fn get_twin_by_mycelium_pk( + &self, + mycelium_pk: Vec, + ) -> Result, Error> { + // We pass a dummy keypair since it's not used in the implementation + let dummy_kp = KeyPair::Sr25519(sr25519::Pair::from_seed(&[0u8; 32])); + current::get_twin_by_mycelium_pk(self, &dummy_kp, mycelium_pk).await + } + pub async fn get_farm_by_id(&self, id: u32) -> Result, Error> { current::get_farm_by_id(self, id).await } diff --git a/clients/tfchain-client-rs/src/runtimes/current.rs b/clients/tfchain-client-rs/src/runtimes/current.rs index e5dbb3604..a11c6fe3b 100644 --- a/clients/tfchain-client-rs/src/runtimes/current.rs +++ b/clients/tfchain-client-rs/src/runtimes/current.rs @@ -201,3 +201,48 @@ pub async fn get_balance( .fetch(¤t::storage().system().account(account)) .await?) } + +pub async fn set_twin_mycelium_pk( + cl: &Client, + kp: &KeyPair, + twin_id: u32, + mycelium_pk: Vec, +) -> Result { + let set_mycelium_pk_tx = current::tx().tfgrid_module().set_twin_mycelium_pk( + twin_id, + BoundedVec(mycelium_pk), + ); + + let signer = kp.signer(); + + let set_mycelium_pk = cl + .api + .tx() + .sign_and_submit_then_watch_default(&set_mycelium_pk_tx, &signer) + .await? + .wait_for_finalized_success() + .await?; + + Ok(set_mycelium_pk.block_hash()) +} + +pub async fn get_twin_by_mycelium_pk( + cl: &Client, + _kp: &KeyPair, + mycelium_pk: Vec, +) -> Result, Error> { + // Query the storage directly using the mycelium pk to get twin ID + let twin_id = cl + .api + .storage() + .at_latest() + .await? + .fetch(¤t::storage().tfgrid_module().twin_by_mycelium_pk(BoundedVec(mycelium_pk))) + .await?; + + if let Some(id) = twin_id { + get_twin_by_id(cl, id).await + } else { + Ok(None) + } +} diff --git a/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs b/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs index 958880ad6..f2f573143 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs @@ -737,6 +737,17 @@ benchmarks! { assert_last_event::(Event::NodeUptimeReported(node_id, now, uptime).into()); } + // set_twin_mycelium_pk + set_twin_mycelium_pk { + let caller: T::AccountId = whitelisted_caller(); + _create_twin::(caller.clone()); + let twin_id = 1; + let mycelium_pk = get_mycelium_pk_input(b"some_mycelium_pk"); + }: _(RawOrigin::Signed(caller), twin_id, mycelium_pk.clone()) + verify { + assert_eq!(TfgridModule::::twin_by_mycelium_pk(&mycelium_pk), Some(twin_id)); + } + // Calling the `impl_benchmark_test_suite` macro inside the `benchmarks` // block will generate one #[test] function per benchmark impl_benchmark_test_suite!(TfgridModule, crate::mock::new_test_ext(), crate::mock::TestRuntime) diff --git a/substrate-node/pallets/pallet-tfgrid/src/lib.rs b/substrate-node/pallets/pallet-tfgrid/src/lib.rs index 55c032ef9..41627e111 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/lib.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/lib.rs @@ -146,6 +146,8 @@ pub mod pallet { // Concrete type for entity pub type TfgridEntity = types::Entity, CityNameOf, CountryNameOf>; + pub type MyceliumPkInput = BoundedVec>; + #[pallet::storage] #[pallet::getter(fn nodes)] pub type Nodes = StorageMap<_, Blake2_128Concat, u32, TfgridNode, OptionQuery>; @@ -263,6 +265,16 @@ pub mod pallet { ValueQuery, >; + #[pallet::storage] + #[pallet::getter(fn twin_by_mycelium_pk)] + pub type TwinByMyceliumPk = StorageMap< + _, + Blake2_128Concat, + MyceliumPkInput, // key is mycelium pk + u32, // value is twin id + OptionQuery, // returns None if pk not found + >; + #[pallet::config] pub trait Config: frame_system::Config + pallet_timestamp::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -411,6 +423,7 @@ pub mod pallet { TwinEntityRemoved(u32, u32), TwinDeleted(u32), TwinAccountBounded(u32, T::AccountId), + TwinMyceliumPkSet(u32, MyceliumPkInput), PricingPolicyStored(types::PricingPolicy), // CertificationCodeStored(types::CertificationCodes), @@ -1238,5 +1251,27 @@ pub mod pallet { // Deprecated! Use index 40 for next extrinsic // #[pallet::call_index(39)] // #[pallet::weight(::WeightInfo::set_node_gpu_status())] + + #[pallet::call_index(40)] + #[pallet::weight(::WeightInfo::set_twin_mycelium_pk())] + pub fn set_twin_mycelium_pk( + origin: OriginFor, + twin_id: u32, + mycelium_pk: MyceliumPkInput, + ) -> DispatchResultWithPostInfo { + let account_id = ensure_signed(origin)?; + + // Ensure the caller owns this twin + let twin = Twins::::get(twin_id).ok_or(Error::::TwinNotExists)?; + ensure!(twin.account_id == account_id, Error::::UnauthorizedToUpdateTwin); + + // Store the mapping + TwinByMyceliumPk::::insert(&mycelium_pk, twin_id); + + // Emit event that mycelium-twin mapping is updated + Self::deposit_event(Event::TwinMyceliumPkSet(twin_id, mycelium_pk)); + + Ok(().into()) + } } } diff --git a/substrate-node/pallets/pallet-tfgrid/src/weights.rs b/substrate-node/pallets/pallet-tfgrid/src/weights.rs index 446c831a5..7ee49ecf8 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/weights.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/weights.rs @@ -65,6 +65,7 @@ pub trait WeightInfo { fn change_power_target() -> Weight; fn bond_twin_account() -> Weight; fn report_uptime_v2() -> Weight; + fn set_twin_mycelium_pk() -> Weight; } /// Weights for pallet_tfgrid using the Substrate node and recommended hardware. @@ -548,6 +549,19 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(18_465_000, 3919) .saturating_add(T::DbWeight::get().reads(4_u64)) } + /// Storage: `TfgridModule::Twins` (r:1 w:0) + /// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::TwinByMyceliumPk` (r:0 w:1) + /// Proof: `TfgridModule::TwinByMyceliumPk` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_twin_mycelium_pk() -> Weight { + // Proof Size summary in bytes: + // Measured: `387` + // Estimated: `3852` + // Minimum execution time: 11_743_000 picoseconds. + Weight::from_parts(12_053_000, 3852) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -1030,4 +1044,17 @@ impl WeightInfo for () { Weight::from_parts(18_465_000, 3919) .saturating_add(RocksDbWeight::get().reads(4_u64)) } + /// Storage: `TfgridModule::Twins` (r:1 w:0) + /// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::TwinByMyceliumPk` (r:0 w:1) + /// Proof: `TfgridModule::TwinByMyceliumPk` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_twin_mycelium_pk() -> Weight { + // Proof Size summary in bytes: + // Measured: `387` + // Estimated: `3852` + // Minimum execution time: 11_743_000 picoseconds. + Weight::from_parts(12_053_000, 3852) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } From e2c5f90f9c4f9377258ad6390d70d8a30ccd5510 Mon Sep 17 00:00:00 2001 From: Omar Abdulaziz Date: Mon, 2 Jun 2025 18:32:14 +0300 Subject: [PATCH 2/3] update map name/methods and clients --- clients/tfchain-client-go/events.go | 20 ++++++--- clients/tfchain-client-go/twin.go | 42 +++++++++++++------ clients/tfchain-client-js/lib/client.js | 26 ++++++++---- clients/tfchain-client-js/lib/twin.js | 36 ++++++++++------ clients/tfchain-client-rs/src/client.rs | 32 +++++++++----- .../tfchain-client-rs/src/runtimes/current.rs | 27 +++++------- .../pallets/pallet-tfgrid/src/benchmarking.rs | 14 ++++--- .../pallets/pallet-tfgrid/src/lib.rs | 25 ++++------- .../pallets/pallet-tfgrid/src/twin.rs | 18 ++++++++ .../pallets/pallet-tfgrid/src/weights.rs | 14 +++---- 10 files changed, 160 insertions(+), 94 deletions(-) diff --git a/clients/tfchain-client-go/events.go b/clients/tfchain-client-go/events.go index 2538fa374..3027c6478 100644 --- a/clients/tfchain-client-go/events.go +++ b/clients/tfchain-client-go/events.go @@ -100,6 +100,13 @@ type TwinEntityRemoved struct { Topics []types.Hash } +type MyceliumTwinUpdated struct { + Phase types.Phase + MyceliumPk []byte `json:"mycelium_pk"` + TwinId types.U32 `json:"twin_id"` + Topics []types.Hash +} + type TwinAccountBounded struct { Phase types.Phase Twin types.U32 `json:"twin_id"` @@ -414,12 +421,13 @@ type EventRecords struct { TfgridModule_EntityDeleted []EntityDeleted //nolint:stylecheck,golint // twin events - TfgridModule_TwinStored []TwinStored //nolint:stylecheck,golint - TfgridModule_TwinUpdated []TwinStored //nolint:stylecheck,golint - TfgridModule_TwinDeleted []TwinDeleted //nolint:stylecheck,golint - TfgridModule_TwinEntityStored []TwinEntityStored //nolint:stylecheck,golint - TfgridModule_TwinEntityRemoved []TwinEntityRemoved //nolint:stylecheck,golint - TfgridModule_TwinAccountBounded []TwinAccountBounded //nolint:stylecheck,golint + TfgridModule_TwinStored []TwinStored //nolint:stylecheck,golint + TfgridModule_TwinUpdated []TwinStored //nolint:stylecheck,golint + TfgridModule_TwinDeleted []TwinDeleted //nolint:stylecheck,golint + TfgridModule_TwinEntityStored []TwinEntityStored //nolint:stylecheck,golint + TfgridModule_TwinEntityRemoved []TwinEntityRemoved //nolint:stylecheck,golint + TfgridModule_TwinAccountBounded []TwinAccountBounded //nolint:stylecheck,golint + TfgridModule_MyceliumTwinUpdated []MyceliumTwinUpdated //nolint:stylecheck,golint // policy events TfgridModule_PricingPolicyStored []PricingPolicyStored //nolint:stylecheck,golint diff --git a/clients/tfchain-client-go/twin.go b/clients/tfchain-client-go/twin.go index ce5dc320c..0d145e3a5 100644 --- a/clients/tfchain-client-go/twin.go +++ b/clients/tfchain-client-go/twin.go @@ -183,52 +183,68 @@ func (s *Substrate) UpdateTwin(identity Identity, relay string, pk []byte) (uint return s.GetTwinByPubKey(identity.PublicKey()) } -// SetTwinMyceliumPK sets the mycelium PK for a twin -func (s *Substrate) SetTwinMyceliumPK(identity Identity, twinID uint32, myceliumPK []byte) error { +// SetMyceliumTwin sets the mycelium public key mapping for a twin +// +// Parameters: +// - identity: The identity for signing the transaction +// - myceliumPK: The mycelium public key (hex string), ip will work as well +// - twinID: The twin ID to map to the mycelium public key +// +// Returns: +// - error: nil on success, error on failure +func (s *Substrate) SetMyceliumTwin(identity Identity, myceliumPK string, twinID uint32) error { cl, meta, err := s.GetClient() if err != nil { return err } - c, err := types.NewCall(meta, "TfgridModule.set_twin_mycelium_pk", twinID, myceliumPK) + bytes := []byte(myceliumPK) + c, err := types.NewCall(meta, "TfgridModule.set_mycelium_twin", bytes, twinID) if err != nil { return errors.Wrap(err, "failed to create call") } if _, err := s.Call(cl, meta, identity, c); err != nil { - return errors.Wrap(err, "failed to set twin mycelium pk") + return errors.Wrap(err, "failed to set mycelium twin") } return nil } -// GetTwinIdByMyceliumPK gets twin id by mycelium PK -func (s *Substrate) GetTwinIdByMyceliumPK(myceliumPK []byte) (*Twin, error) { +// GetMyceliumTwin gets the twin ID associated with a mycelium public key +// +// Parameters: +// - myceliumPK: The mycelium public key (hex string) +// +// Returns: +// - uint32: The twin ID associated with the mycelium public key +// - error: nil on success, error on failure (including ErrNotFound if no twin is found) +func (s *Substrate) GetMyceliumTwin(myceliumPK string) (uint32, error) { cl, meta, err := s.GetClient() if err != nil { - return nil, err + return 0, err } // must use encode to convert byte slice to encoded format bytes, err := Encode(myceliumPK) if err != nil { - return nil, errors.Wrap(err, "substrate: encoding error building query arguments") + return 0, errors.Wrap(err, "substrate: encoding error building query arguments") } - key, err := types.CreateStorageKey(meta, "TfgridModule", "TwinByMyceliumPk", bytes, nil) + key, err := types.CreateStorageKey(meta, "TfgridModule", "MyceliumTwin", bytes, nil) if err != nil { - return nil, errors.Wrap(err, "failed to create substrate query key") + return 0, errors.Wrap(err, "failed to create substrate query key") } var twinID types.U32 ok, err := cl.RPC.State.GetStorageLatest(key, &twinID) if err != nil { - return nil, errors.Wrap(err, "failed to lookup twin by mycelium pk") + return 0, errors.Wrap(err, "failed to lookup twin by mycelium pk") } if !ok || twinID == 0 { - return nil, errors.Wrap(ErrNotFound, "twin not found for mycelium pk") + return 0, errors.Wrap(ErrNotFound, "twin not found for mycelium pk") } - return s.GetTwin(uint32(twinID)) + return uint32(twinID), nil } diff --git a/clients/tfchain-client-js/lib/client.js b/clients/tfchain-client-js/lib/client.js index a3cbd7bea..d52745c2d 100644 --- a/clients/tfchain-client-js/lib/client.js +++ b/clients/tfchain-client-js/lib/client.js @@ -11,7 +11,7 @@ const { const { createTwin, getTwin, getTwinIdByAccountId, updateTwin, deleteTwin, addTwinEntity, deleteTwinEntity, listTwins, - setTwinMyceliumPK, getTwinByMyceliumPK + setMyceliumTwin, getMyceliumTwin } = require('./twin') const { createFarm, getFarm, deleteFarm, @@ -162,12 +162,24 @@ class Client { return deleteTwinEntity(this, twinID, entityID, callback) } - async setTwinMyceliumPK(twinId, myceliumPk, callback) { - return setTwinMyceliumPK(this, twinId, myceliumPk, callback) - } - - async getTwinByMyceliumPK(myceliumPk) { - return getTwinByMyceliumPK(this, myceliumPk) + /** + * Sets the mycelium public key mapping for a twin + * @param {string} myceliumPk - The mycelium public key (hex string) + * @param {number} twinId - The twin ID to map to the mycelium public key + * @param {Function} callback - Optional callback function + * @returns {Promise} Transaction result + */ + async setMyceliumTwin(myceliumPk, twinId, callback) { + return setMyceliumTwin(this, myceliumPk, twinId, callback) + } + + /** + * Gets the twin ID associated with a mycelium public key + * @param {string} myceliumPk - The mycelium public key (hex string) + * @returns {Promise} The twin ID associated with the mycelium public key + */ + async getMyceliumTwin(myceliumPk) { + return getMyceliumTwin(this, myceliumPk) } async createFarm(name, certificationType, publicIPs, callback) { diff --git a/clients/tfchain-client-js/lib/twin.js b/clients/tfchain-client-js/lib/twin.js index 4d824db17..a4c1d2bd2 100644 --- a/clients/tfchain-client-js/lib/twin.js +++ b/clients/tfchain-client-js/lib/twin.js @@ -92,26 +92,38 @@ async function deleteTwin (self, id, callback) { .signAndSend(self.key, { nonce }, callback) } -// setTwinMyceliumPK sets the mycelium public key for a twin -async function setTwinMyceliumPK (self, twinId, myceliumPk, callback) { - const setMyceliumPk = self.api.tx.tfgridModule.setTwinMyceliumPk(twinId, myceliumPk) +/** + * Sets the mycelium public key mapping for a twin + * @param {Object} self - The client instance + * @param {string} myceliumPk - The mycelium public key (hex string) + * @param {number} twinId - The twin ID to map to the mycelium public key + * @param {Function} callback - Optional callback function + * @returns {Promise} Transaction result + */ +async function setMyceliumTwin (self, myceliumPk, twinId, callback) { + const setMyceliumTx = self.api.tx.tfgridModule.setMyceliumTwin(myceliumPk, twinId) const nonce = await self.api.rpc.system.accountNextIndex(self.address) - return setMyceliumPk.signAndSend(self.key, { nonce }, callback) + return setMyceliumTx.signAndSend(self.key, { nonce }, callback) } -// getTwinByMyceliumPK gets a twin by mycelium public key -async function getTwinByMyceliumPK (self, myceliumPk) { - // First, get the twin ID from the mycelium PK mapping - const twinIdResult = await self.api.query.tfgridModule.twinByMyceliumPk(myceliumPk) +/** + * Gets the twin ID associated with a mycelium public key + * @param {Object} self - The client instance + * @param {string} myceliumPk - The mycelium public key (hex string) + * @returns {Promise} The twin ID associated with the mycelium public key + * @throws {Error} If no twin is found for the given mycelium public key + */ +async function getMyceliumTwin (self, myceliumPk) { + // Get the twin ID from the mycelium PK mapping using the storage getter + const twinIdResult = await self.api.query.tfgridModule.myceliumTwin(myceliumPk) const twinId = twinIdResult.toJSON() if (!twinId || twinId === 0) { throw Error(`Couldn't find a twin for mycelium pk: ${myceliumPk}`) } - // Now get the full twin object using the twin ID - return getTwin(self, twinId) + return twinId } module.exports = { @@ -123,6 +135,6 @@ module.exports = { addTwinEntity, deleteTwinEntity, listTwins, - setTwinMyceliumPK, - getTwinByMyceliumPK + setMyceliumTwin, + getMyceliumTwin } diff --git a/clients/tfchain-client-rs/src/client.rs b/clients/tfchain-client-rs/src/client.rs index 19758e407..e76cca4a3 100644 --- a/clients/tfchain-client-rs/src/client.rs +++ b/clients/tfchain-client-rs/src/client.rs @@ -152,24 +152,36 @@ impl Client { current::get_twin_id_by_account(self, account).await } - // Sets mycelium public key for a twin and checks for success, blockhash is returned on success - pub async fn set_twin_mycelium_pk( + /// Sets the mycelium public key mapping for a twin + /// + /// # Arguments + /// * `kp` - The keypair for signing the transaction + /// * `mycelium_pk` - The mycelium public key (as bytes) + /// * `twin_id` - The twin ID to map to the mycelium public key + /// + /// # Returns + /// * `Result` - The block hash on success + pub async fn set_mycelium_twin( &self, kp: &KeyPair, - twin_id: u32, mycelium_pk: Vec, + twin_id: u32, ) -> Result { - current::set_twin_mycelium_pk(self, kp, twin_id, mycelium_pk).await + current::set_mycelium_twin(self, kp, mycelium_pk, twin_id).await } - // Gets a twin by mycelium public key and returns the full twin object - pub async fn get_twin_by_mycelium_pk( + /// Gets the twin ID associated with a mycelium public key + /// + /// # Arguments + /// * `mycelium_pk` - The mycelium public key (as bytes) + /// + /// # Returns + /// * `Result, Error>` - The twin ID if found, None otherwise + pub async fn get_mycelium_twin( &self, mycelium_pk: Vec, - ) -> Result, Error> { - // We pass a dummy keypair since it's not used in the implementation - let dummy_kp = KeyPair::Sr25519(sr25519::Pair::from_seed(&[0u8; 32])); - current::get_twin_by_mycelium_pk(self, &dummy_kp, mycelium_pk).await + ) -> Result, Error> { + current::get_mycelium_twin(self, mycelium_pk).await } pub async fn get_farm_by_id(&self, id: u32) -> Result, Error> { diff --git a/clients/tfchain-client-rs/src/runtimes/current.rs b/clients/tfchain-client-rs/src/runtimes/current.rs index a11c6fe3b..63e883b25 100644 --- a/clients/tfchain-client-rs/src/runtimes/current.rs +++ b/clients/tfchain-client-rs/src/runtimes/current.rs @@ -202,47 +202,42 @@ pub async fn get_balance( .await?) } -pub async fn set_twin_mycelium_pk( +pub async fn set_mycelium_twin( cl: &Client, kp: &KeyPair, - twin_id: u32, mycelium_pk: Vec, + twin_id: u32, ) -> Result { - let set_mycelium_pk_tx = current::tx().tfgrid_module().set_twin_mycelium_pk( - twin_id, + let set_mycelium_tx = current::tx().tfgrid_module().set_mycelium_twin( BoundedVec(mycelium_pk), + twin_id, ); let signer = kp.signer(); - let set_mycelium_pk = cl + let set_mycelium = cl .api .tx() - .sign_and_submit_then_watch_default(&set_mycelium_pk_tx, &signer) + .sign_and_submit_then_watch_default(&set_mycelium_tx, &signer) .await? .wait_for_finalized_success() .await?; - Ok(set_mycelium_pk.block_hash()) + Ok(set_mycelium.block_hash()) } -pub async fn get_twin_by_mycelium_pk( +pub async fn get_mycelium_twin( cl: &Client, - _kp: &KeyPair, mycelium_pk: Vec, -) -> Result, Error> { +) -> Result, Error> { // Query the storage directly using the mycelium pk to get twin ID let twin_id = cl .api .storage() .at_latest() .await? - .fetch(¤t::storage().tfgrid_module().twin_by_mycelium_pk(BoundedVec(mycelium_pk))) + .fetch(¤t::storage().tfgrid_module().mycelium_twin(BoundedVec(mycelium_pk))) .await?; - if let Some(id) = twin_id { - get_twin_by_id(cl, id).await - } else { - Ok(None) - } + Ok(twin_id) } diff --git a/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs b/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs index f2f573143..1dd984ba1 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/benchmarking.rs @@ -737,15 +737,15 @@ benchmarks! { assert_last_event::(Event::NodeUptimeReported(node_id, now, uptime).into()); } - // set_twin_mycelium_pk - set_twin_mycelium_pk { + // set_mycelium_twin + set_mycelium_twin { let caller: T::AccountId = whitelisted_caller(); _create_twin::(caller.clone()); - let twin_id = 1; let mycelium_pk = get_mycelium_pk_input(b"some_mycelium_pk"); - }: _(RawOrigin::Signed(caller), twin_id, mycelium_pk.clone()) + let twin_id = 1; + }: _(RawOrigin::Signed(caller), mycelium_pk.clone(), twin_id) verify { - assert_eq!(TfgridModule::::twin_by_mycelium_pk(&mycelium_pk), Some(twin_id)); + assert_eq!(TfgridModule::::get_mycelium_twin(&mycelium_pk), Some(twin_id)); } // Calling the `impl_benchmark_test_suite` macro inside the `benchmarks` @@ -1033,3 +1033,7 @@ pub(crate) fn get_pub_config_gw6_input(gw6_input: &[u8]) -> Gw6Input { pub(crate) fn get_pub_config_domain_input(domain_input: &[u8]) -> DomainInput { BoundedVec::try_from(domain_input.to_vec()).expect("Invalid domain input.") } + +pub(crate) fn get_mycelium_pk_input(mycelium_pk_input: &[u8]) -> MyceliumPkInput { + BoundedVec::try_from(mycelium_pk_input.to_vec()).expect("Invalid mycelium pk input.") +} diff --git a/substrate-node/pallets/pallet-tfgrid/src/lib.rs b/substrate-node/pallets/pallet-tfgrid/src/lib.rs index 41627e111..f35da7dba 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/lib.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/lib.rs @@ -266,8 +266,8 @@ pub mod pallet { >; #[pallet::storage] - #[pallet::getter(fn twin_by_mycelium_pk)] - pub type TwinByMyceliumPk = StorageMap< + #[pallet::getter(fn get_mycelium_twin)] + pub type MyceliumTwin = StorageMap< _, Blake2_128Concat, MyceliumPkInput, // key is mycelium pk @@ -423,7 +423,7 @@ pub mod pallet { TwinEntityRemoved(u32, u32), TwinDeleted(u32), TwinAccountBounded(u32, T::AccountId), - TwinMyceliumPkSet(u32, MyceliumPkInput), + MyceliumTwinUpdated(MyceliumPkInput, u32), PricingPolicyStored(types::PricingPolicy), // CertificationCodeStored(types::CertificationCodes), @@ -1253,25 +1253,14 @@ pub mod pallet { // #[pallet::weight(::WeightInfo::set_node_gpu_status())] #[pallet::call_index(40)] - #[pallet::weight(::WeightInfo::set_twin_mycelium_pk())] - pub fn set_twin_mycelium_pk( + #[pallet::weight(::WeightInfo::set_mycelium_twin())] + pub fn set_mycelium_twin( origin: OriginFor, - twin_id: u32, mycelium_pk: MyceliumPkInput, + twin_id: u32, ) -> DispatchResultWithPostInfo { let account_id = ensure_signed(origin)?; - - // Ensure the caller owns this twin - let twin = Twins::::get(twin_id).ok_or(Error::::TwinNotExists)?; - ensure!(twin.account_id == account_id, Error::::UnauthorizedToUpdateTwin); - - // Store the mapping - TwinByMyceliumPk::::insert(&mycelium_pk, twin_id); - - // Emit event that mycelium-twin mapping is updated - Self::deposit_event(Event::TwinMyceliumPkSet(twin_id, mycelium_pk)); - - Ok(().into()) + Self::_set_mycelium_twin(account_id, mycelium_pk, twin_id) } } } diff --git a/substrate-node/pallets/pallet-tfgrid/src/twin.rs b/substrate-node/pallets/pallet-tfgrid/src/twin.rs index 7234cdecf..34d8b9083 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/twin.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/twin.rs @@ -408,4 +408,22 @@ impl Pallet { true } + + pub fn _set_mycelium_twin( + account_id: T::AccountId, + mycelium_pk: MyceliumPkInput, + twin_id: u32, + ) -> DispatchResultWithPostInfo { + // Ensure the caller owns this twin + let twin = Twins::::get(twin_id).ok_or(Error::::TwinNotExists)?; + ensure!(twin.account_id == account_id, Error::::UnauthorizedToUpdateTwin); + + // Store the mapping + MyceliumTwin::::insert(&mycelium_pk, twin_id); + + // Emit event that mycelium-twin mapping is updated + Self::deposit_event(Event::MyceliumTwinUpdated(mycelium_pk, twin_id)); + + Ok(().into()) + } } diff --git a/substrate-node/pallets/pallet-tfgrid/src/weights.rs b/substrate-node/pallets/pallet-tfgrid/src/weights.rs index 7ee49ecf8..6a6145520 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/weights.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/weights.rs @@ -65,7 +65,7 @@ pub trait WeightInfo { fn change_power_target() -> Weight; fn bond_twin_account() -> Weight; fn report_uptime_v2() -> Weight; - fn set_twin_mycelium_pk() -> Weight; + fn set_mycelium_twin() -> Weight; } /// Weights for pallet_tfgrid using the Substrate node and recommended hardware. @@ -551,9 +551,9 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `TfgridModule::Twins` (r:1 w:0) /// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `TfgridModule::TwinByMyceliumPk` (r:0 w:1) - /// Proof: `TfgridModule::TwinByMyceliumPk` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn set_twin_mycelium_pk() -> Weight { + /// Storage: `TfgridModule::MyceliumTwin` (r:0 w:1) + /// Proof: `TfgridModule::MyceliumTwin` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_mycelium_twin() -> Weight { // Proof Size summary in bytes: // Measured: `387` // Estimated: `3852` @@ -1046,9 +1046,9 @@ impl WeightInfo for () { } /// Storage: `TfgridModule::Twins` (r:1 w:0) /// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `TfgridModule::TwinByMyceliumPk` (r:0 w:1) - /// Proof: `TfgridModule::TwinByMyceliumPk` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn set_twin_mycelium_pk() -> Weight { + /// Storage: `TfgridModule::MyceliumTwin` (r:0 w:1) + /// Proof: `TfgridModule::MyceliumTwin` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_mycelium_twin() -> Weight { // Proof Size summary in bytes: // Measured: `387` // Estimated: `3852` From e325c9f820adc276fc81f1b39c9e0d47aa546ed4 Mon Sep 17 00:00:00 2001 From: Omar Abdulaziz Date: Mon, 2 Jun 2025 18:33:31 +0300 Subject: [PATCH 3/3] add helper account acitvation script --- scripts/activate/go.mod | 32 ++++++++++ scripts/activate/go.sum | 109 ++++++++++++++++++++++++++++++++++ scripts/activate/man.go | 126 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 scripts/activate/go.mod create mode 100644 scripts/activate/go.sum create mode 100644 scripts/activate/man.go diff --git a/scripts/activate/go.mod b/scripts/activate/go.mod new file mode 100644 index 000000000..cb6dd2726 --- /dev/null +++ b/scripts/activate/go.mod @@ -0,0 +1,32 @@ +module github.com/threefoldtech/tfchain/activate + +go 1.21 + +require github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-00010101000000-000000000000 + +require ( + github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect + github.com/cenkalti/backoff v2.2.1+incompatible // indirect + github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/deckarep/golang-set v1.8.0 // indirect + github.com/decred/base58 v1.0.4 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/ethereum/go-ethereum v1.10.20 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/gtank/merlin v0.1.1 // indirect + github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 // indirect + github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect + github.com/pierrec/xxHash v0.1.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rs/cors v1.8.2 // indirect + github.com/rs/zerolog v1.26.0 // indirect + github.com/vedhavyas/go-subkey v1.0.3 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/sys v0.6.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect +) + +replace github.com/threefoldtech/tfchain/clients/tfchain-client-go => ../../clients/tfchain-client-go diff --git a/scripts/activate/go.sum b/scripts/activate/go.sum new file mode 100644 index 000000000..b6554e8cd --- /dev/null +++ b/scripts/activate/go.sum @@ -0,0 +1,109 @@ +github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= +github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12 h1:DCYWIBOalB0mKKfUg2HhtGgIkBbMA1fnlnkZp7fHB18= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12/go.mod h1:5g1oM4Zu3BOaLpsKQ+O8PAv2kNuq+kPcA1VzFbsSqxE= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/decred/base58 v1.0.4 h1:QJC6B0E0rXOPA8U/kw2rP+qiRJsUaE2Er+pYb3siUeA= +github.com/decred/base58 v1.0.4/go.mod h1:jJswKPEdvpFpvf7dsDvFZyLT22xZ9lWqEByX38oGd9E= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/go-ethereum v1.10.20 h1:75IW830ClSS40yrQC1ZCMZCt5I+zU16oqId2SiQwdQ4= +github.com/ethereum/go-ethereum v1.10.20/go.mod h1:LWUN82TCHGpxB3En5HVmLLzPD7YSrEUFmFfN1nKkVN0= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 h1:4zOlv2my+vf98jT1nQt4bT/yKWUImevYPJ2H344CloE= +github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6/go.mod h1:r/8JmuR0qjuCiEhAolkfvdZgmPiHTnJaG0UXCSeR1Zo= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU= +github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= +github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= +github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/vedhavyas/go-subkey v1.0.3 h1:iKR33BB/akKmcR2PMlXPBeeODjWLM90EL98OrOGs8CA= +github.com/vedhavyas/go-subkey v1.0.3/go.mod h1:CloUaFQSSTdWnINfBRFjVMkWXZANW+nd8+TI5jYcl6Y= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scripts/activate/man.go b/scripts/activate/man.go new file mode 100644 index 000000000..697d89682 --- /dev/null +++ b/scripts/activate/man.go @@ -0,0 +1,126 @@ +package main + +import ( + "fmt" + "log" + "math/big" + "strings" + + substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" +) + +const ( + // Your mnemonic phrase - REPLACE THIS WITH YOUR ACTUAL MNEMONIC + MNEMONIC = "hen broken hedgehog page tribe motion rate mix mammal arctic alien clump" + + // Alice's well-known address for devnet - used for sending tokens + ALICE_ADDRESS = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + + // Local tfchain URL + CHAIN_URL = "ws://127.0.0.1:9944" + + // Amount to transfer (9999 TFT in units of uTFT) + TRANSFER_AMOUNT = 9999 * 10_000_000 // 9999 TFT in uTFT (micro TFT) + + // Terms and conditions dummy data + TERMS_LINK = "https://manual.grid.tf/knowledge_base/legal/terms_conditions_all3.html" + TERMS_HASH = "e4174baeb17012f3b610b8b0d2354d87" +) + +func main() { + fmt.Println("Starting TFChain account activation script...") + + // Step 1: Connect to TFChain + fmt.Println("Connecting to TFChain...") + manager := substrate.NewManager(CHAIN_URL) + substrateConn, err := manager.Substrate() + if err != nil { + log.Fatalf("Failed to connect to substrate: %v", err) + } + defer substrateConn.Close() + fmt.Println("✓ Connected to TFChain") + + // Step 2: Create identity from mnemonic + fmt.Println("Creating identity from mnemonic...") + identity, err := substrate.NewIdentityFromSr25519Phrase(MNEMONIC) + if err != nil { + log.Fatalf("Failed to create identity from mnemonic: %v", err) + } + fmt.Printf("✓ Identity created with address: %s\n", identity.Address()) + + // Step 3: Create Alice's identity for sending tokens + fmt.Println("Setting up Alice's identity for token transfer...") + aliceIdentity, err := substrate.NewIdentityFromSr25519Phrase("//Alice") + if err != nil { + log.Fatalf("Failed to create Alice identity: %v", err) + } + fmt.Printf("✓ Alice identity created with address: %s\n", aliceIdentity.Address()) + + // Step 4: Transfer tokens from Alice to user account + fmt.Println("Transferring 9999 TFT from Alice to your account...") + myAccount, err := substrate.FromAddress(identity.Address()) + if err != nil { + log.Fatalf("Failed to parse my account address: %v", err) + } + + err = substrateConn.Transfer(aliceIdentity, TRANSFER_AMOUNT, myAccount) + if err != nil { + log.Fatalf("Failed to transfer tokens: %v", err) + } + fmt.Printf("✓ Successfully transferred %d uTFT (%.3f TFT) to your account\n", TRANSFER_AMOUNT, float64(TRANSFER_AMOUNT)/10_000_000) + + // Step 5: Check account balance + fmt.Println("Checking account balance...") + accountInfo, err := substrateConn.GetAccount(identity) + if err != nil { + log.Fatalf("Failed to get account info: %v", err) + } + + balance := accountInfo.Data.Free + balanceFloat := new(big.Float).SetInt(balance.Int) + balanceFloat.Quo(balanceFloat, big.NewFloat(10_000_000)) // Convert from uTFT to TFT + fmt.Printf("✓ Current account balance: %s TFT\n", balanceFloat.String()) + + // Step 6: Accept Terms and Conditions + fmt.Println("Accepting Terms and Conditions...") + + err = substrateConn.AcceptTermsAndConditions(identity, TERMS_LINK, TERMS_HASH) + if err != nil { + log.Fatalf("Failed to accept terms and conditions: %v", err) + } + fmt.Println("✓ Terms and conditions accepted") + + // Step 7: Create Twin (this activates the account) + fmt.Println("Creating twin to activate account...") + + // Create twin with optional relay and public key + twinID, err := substrateConn.CreateTwin(identity, "", []byte{}) + if err != nil { + log.Fatalf("Failed to create twin: %v", err) + } + + fmt.Printf("✓ Account successfully activated!\n") + fmt.Printf("✓ Twin created with ID: %d\n", twinID) + + // Step 8: Verify twin creation + fmt.Println("Verifying twin creation...") + twin, err := substrateConn.GetTwin(twinID) + if err != nil { + log.Fatalf("Failed to get twin: %v", err) + } + + fmt.Printf("✓ Twin verification successful:\n") + fmt.Printf(" - Twin ID: %d\n", twin.ID) + fmt.Printf(" - Account: %s\n", twin.Account.String()) + fmt.Printf(" - Relay: %+v\n", twin.Relay) + + // Final summary + fmt.Println("\n" + strings.Repeat("=", 50)) + fmt.Println("ACTIVATION COMPLETE!") + fmt.Println(strings.Repeat("=", 50)) + fmt.Printf("Your account address: %s\n", identity.Address()) + fmt.Printf("Account balance: %s TFT\n", balanceFloat.String()) + fmt.Printf("Twin ID: %d\n", twinID) + fmt.Println("Your account is now fully activated on TFChain!") + fmt.Println(strings.Repeat("=", 50)) +}