diff --git a/clients/tfchain-client-go/contract.go b/clients/tfchain-client-go/contract.go index 1eec5815e..4a8992c26 100644 --- a/clients/tfchain-client-go/contract.go +++ b/clients/tfchain-client-go/contract.go @@ -444,6 +444,42 @@ func (s *Substrate) CancelContract(identity Identity, contract uint64) error { return nil } +// MigrateNodeContract moves a node contract to another node in the same farm, +// keeping the contract alive instead of cancelling it. It moves the booking, not +// the workload: the source node deprovisions its copy once the contract leaves its +// list, and the owner must redeploy on the destination. The extrinsic takes a +// configurable origin (root or council), so this must be submitted through a +// collective motion rather than signed by an ordinary twin. +// +// Pass an empty hash to keep the contract's current deployment hash. +func (s *Substrate) MigrateNodeContract(identity Identity, contract uint64, nodeID uint32, hash string) error { + cl, meta, err := s.GetClient() + if err != nil { + return err + } + + var deploymentHash types.Option[HexHash] + if hash == "" { + deploymentHash = types.NewEmptyOption[HexHash]() + } else { + deploymentHash = types.NewOption[HexHash](NewHexHash(hash)) + } + + c, err := types.NewCall(meta, "SmartContractModule.migrate_node_contract", + contract, nodeID, deploymentHash, + ) + if err != nil { + return errors.Wrap(err, "failed to create call") + } + + _, err = s.Call(cl, meta, identity, c) + if err != nil { + return errors.Wrap(err, "failed to migrate node contract") + } + + return nil +} + // BatchCancelContract cancels a batch of contracts func (s *Substrate) BatchCancelContract(identity Identity, contracts []uint64) error { cl, meta, err := s.GetClient() diff --git a/docs/architecture/0027-migrate-node-contract.md b/docs/architecture/0027-migrate-node-contract.md new file mode 100644 index 000000000..42a33d069 --- /dev/null +++ b/docs/architecture/0027-migrate-node-contract.md @@ -0,0 +1,195 @@ +# 27. Migrate a Node Contract to Another Node in the Same Farm + +Date: 2026-08-18 + +## Status + +Accepted + +## Context + +A farmer cannot power a node down while it holds a single active contract: +`change_power_target(Down)` requires `node_has_no_active_contracts`. The only way +to empty a node today is to cancel its contracts, which is terminal — the contract +is gone, and with it any path for the tenant to return under the same identity. + +That blocks hardware consolidation. On mainnet at spec 157, emptying ten machines +in Freefarm means relocating 74 contracts across 29 twins, none of them the +operator. Asking those twins to recreate contracts by hand is not available to a +hosting provider. + +Two alternatives were rejected. **Cancel and recreate** is the problem, not the +fix. **Off-chain coordination** cannot work: the contract ID is the identity ZOS, +the billing loop and the indexer all key on, and nothing off-chain can move it. + +### What migration moves, and what it does not + +**It moves the booking, not the bytes.** The chain stores a contract ID, a +deployment hash and small metadata; the workload lives on the node. Once the +contract leaves the source node's `ActiveNodeContracts`, that node's ZOS +reconciles, finds a local deployment the chain no longer lists for it, and +deprovisions it — deleting the disks. Nothing is copied between nodes, and no +on-chain mechanism could copy it. + +So **preserving data is the caller's responsibility, before calling.** The team's +intended sequence is: pause the deployment on the source, copy it to S3, call +`migrate_node_contract` — at which point tearing down the source is safe and +expected — then create the deployment on the destination. The chain neither +enforces nor observes any of that. + +What migration preserves that cancellation does not: the contract ID, billing +continuity, the reward payee, reserved public IPs, and a live contract to deploy +against. That is the whole case for the feature — it turns an irreversible +operator action into a recoverable one. + +## Decisions + +### 1. A new extrinsic, restricted to council + +| Extrinsic | Origin | Call index | +| --- | --- | --- | +| `migrate_node_contract(contract_id, node_id, deployment_hash)` | `RestrictedOrigin` (root or 3/5 council) | 22 | + +The consolidation is cross-tenant by construction, so restricting v1 removes the +whole authorization matrix — owner versus farmer, and who may rewrite a deployment +hash — rather than answering it. + +`deployment_hash` is `Option`; `None` keeps the current hash. It exists +because the hash covers the full deployment sent to the node and legitimately +changes for network workloads, though not for VMs. On-chain `deployment_data` is +small metadata unaffected by a relocation, so it is not a parameter. + +**Forward constraint: owner and farmer paths must arrive as a new `call_index(23)`, +never by widening 22's origin check.** Adding a dispatchable is exempt from a +`transaction_version` bump; altering an existing one's semantics is not. + +### 2. Same farm, and deliberately no same-country rule + +Farm membership is the only relationship the chain models, and it is what keeps +public-IP reservations coherent, since `reserve_ip`/`free_ip` resolve the farm +*through* the node. A same-country rule would encode a relationship the chain +cannot verify. + +### 3. `Created` contracts only + +Every target contract is `Created`, and the chain holds exactly one `GracePeriod` +node contract. Relocating a contract that will auto-delete in 14 days only moves +where it dies. + +### 4. Opted-out and rented nodes excluded, on both sides + +Both would silently reprice the tenant: opting out of v3 billing waives billing +entirely, and a rent contract zeroes CPU/RAM/disk cost. Excluding them costs +nothing in the farms this was built for and removes two repricing paths. + +### 5. Bill before moving + +Billing settles at the **source** node's cost basis — it reads the source's +certification and resolves the payee from the source farm — so the source period +is closed before the contract changes hands. The contract is then re-read, because +billing can mutate or remove it, and the two branches that follow answer +deliberately differently: a contract pushed into grace returns `Err`, a contract +already deleted returns `Ok`. The reasoning for that asymmetry, and for the +guarded `ContractIDByNodeIDAndHash` removal and the unconditional clock stamp, +lives in comments at each site in `_migrate_node_contract`, where the next person +to touch them will actually read it. + +### 6. Reuse `ContractUpdated`; add no event + +`ContractUpdated` already carries the whole contract, hence the new `node_id` and +hash, and the indexer already writes both. So tfchain_graphql and grid proxy need +no changes, and the Go client needs only a call wrapper. Source-node cleanup needs +no chain change either: `ContractEventHandler.sync()` runs hourly, compares local +deployments against `ActiveNodeContracts[node]`, and deprovisions what the chain no +longer lists. + +A dedicated `NodeContractMigrated` would let ZOS filter before pushing to its local +stream, but `ContractUpdated` fires well under once per hour chain-wide, so the +saving is roughly one discarded stream entry every few hours. It stays purely +additive if audit ever wants it. + +### 7. Do not build prompt teardown + +A ZOS node could act on `ContractUpdated` directly rather than waiting for +`sync()`, since every node already decodes the complete `EventRecords` per block +locally. It is not worth building: it accelerates nothing the operator needs, since +`change_power_target(Down)` is gated on `ActiveNodeContracts`, which empties the +instant the extrinsic lands — and the benefit is the negligible stream saving +above. + +If it is ever built, whoever builds it must confirm the operating procedure still +copies data off the source *before* calling, because same-block teardown removes +any margin for a procedure that does not. + +### 8. Report handlers skip a stale entry instead of aborting + +A consistency sweep more than a behaviour change. Both handlers already tolerated a +report naming a contract that does not exist, skipping it silently and for free. A +report naming a contract that exists but sits on **another** node is the same class +of mistake, yet was handled the opposite way — rejecting the whole extrinsic and +charging for it. That asymmetry was the anomaly. + +It was also unreachable until now: cancellation removes the contract, so the +missing-contract guards caught it. `migrate_node_contract` is the first operation +that leaves a *live* contract pointing elsewhere, and left alone, one migrated +contract would have frozen resource and NRU reporting for every other contract on +the source node. + +Charging for a skipped entry was considered and rejected — it would re-introduce +the asymmetry in the other direction. If the anti-spam posture is revisited, both +cases should move together. + +## What the extrinsic refuses + +1. A contract that does not exist, or is not a node contract. +2. A contract not in `Created` state. +3. A destination equal to the source. +4. A destination in a different farm. +5. Either node opted out of v3 billing, or holding a rent contract. +6. A destination that is in standby or dedicated. The **source** node's power state + is deliberately unchecked — migrating off a machine you are about to shut down + is the point. +7. A destination whose `(node_id, deployment_hash)` key is taken. Stricter than + `create_node_contract`, which permits overwriting a `Deleted` entry: there is no + restore semantic here, and that contract's eventual `remove_contract` would + delete the key we just claimed. + +New errors are appended at the end of the enum, since `Error` variants are +SCALE-encoded by declaration order: `NodeNotInSameFarm`, `ContractAlreadyOnNode`, +`NodeIsOptedOutOfV3Billing`, `ContractNotInCreatedState`. +`NodeNotAuthorizedToComputeReport` is kept and marked reserved — decision 8 removed +its last construction site, and deleting it would renumber every variant below. + +## Consequences we accept + +- **No storage migration.** `Contract` unchanged, `CONTRACT_VERSION` 4, + `StorageVersion` V12, `transaction_version` 2. No indexer, grid proxy or ZOS + change required. +- **An emptied `ActiveNodeContracts` is not permission to power the machine off.** + It empties the instant the extrinsic lands and says nothing about where the + workload is. Nothing in the pallet can detect the difference. +- **A council-approved move can reprice a tenant.** Certification is per-node and + legitimately mixed within a farm, and Certified nodes bill 25% above Diy. Worse + across the reporting gap: subsequent cycles read the *destination's* + certification against the *source's* still-stored footprint — new price × old + quantity, either direction, until the destination reports. Certification-matched + destinations make the multiplier 1.0 and remove it. If the signed path is added, + it needs a directional guard. +- **A council motion has a weight ceiling.** `MaxProposalWeight` is 50% of max + block weight. Against the measured weight, that bounds a motion at about 2,000 + contracts when the nodes involved are near-empty, falling to roughly **340** when + migrating off a node holding ~1,660 — the cost scales with the length of the + contract vectors being rewritten, so the busiest nodes are the ones that batch + worst. Size batches per tenant group and this ceiling is never near. + +## Operational gate + +Conditions on the first mainnet migration, not on merging. + +1. **Data is copied off the source before the extrinsic is called.** The chain does + not check this and cannot; deprovisioning follows the move automatically. +2. **Destinations are certification-matched**, removing the repricing exposure + rather than documenting it. +3. **Batch per tenant group**, never all at once. +4. **Power the source down only after confirming the destination provisioned** — + never on `ActiveNodeContracts` being empty. diff --git a/docs/misc/migrate_node_contract.md b/docs/misc/migrate_node_contract.md new file mode 100644 index 000000000..ffe0fdfb4 --- /dev/null +++ b/docs/misc/migrate_node_contract.md @@ -0,0 +1,109 @@ +# Migrating a Node Contract + +See [ADR 0027](../architecture/0027-migrate-node-contract.md) for why this exists +and what was decided. + +## Overview + +`migrate_node_contract` moves a live node contract to another node **in the same +farm** without cancelling it. The contract keeps its id, billing state, reported +resources and reserved public IPs, so the tenant is left with something to deploy +against — which cancelling is not. + +It moves the **booking, not the workload**. Nothing is copied between nodes. Once +the contract leaves the source node's `ActiveNodeContracts`, that node deprovisions +its local copy on the next hourly reconciliation and deletes the disks. Getting the +data off the source beforehand is the caller's job. + +## Dispatchable + +``` +migrate_node_contract(contract_id: u64, node_id: u32, deployment_hash: Option) +``` + +Origin: root, or a 3/5 council motion. Pass `None` for `deployment_hash` to keep the +current one. + +## Preconditions checked on chain + +- the contract exists, is a node contract, and is in `Created` state +- destination is a different node, in the **same farm** +- neither node is opted out of v3 billing, nor under a rent contract +- destination is not in standby and not dedicated +- destination does not already hold a contract with the same deployment hash + +The **source** node's power state is not checked — migrating off a machine you are +about to shut down is the point. + +## Not checked on chain + +Read this section before planning a wave. + +- **Destination capacity.** Neither this call nor `create_node_contract` verifies + the destination can host the workload; ZOS decides that at deployment time. So an + oversubscribed destination is discovered *after* the contract has moved and the + source has been told to let go. Check real free memory on the destination, not + just reported usage. +- **That the data was preserved.** See Overview. +- **That anything is actually running.** `ActiveNodeContracts` for the source is + empty the instant the call lands. That is also what `change_power_target(Down)` + gates on, so the chain will report the machine safe to sleep while the workload + is still on it and not yet rebuilt anywhere else. + +## Before you call + +1. Get the data off the source. +2. Prefer a destination whose **node certification matches the source**. Certified + nodes bill 25% above Diy, and until the destination files its first resource + report the tenant is billed at the destination's rate against the source's + footprint. Matching certification makes that 1.0, and a farm whose nodes are all + one certification cannot hit this at all. +3. Confirm the contract still exists, still belongs to that twin and still sits on + the source — plans go stale, contracts get cancelled continuously. +4. Confirm the contract has a `ContractPaymentState`; without one `bill_contract` + fails and the migration cannot proceed. + +## Submitting through the council + +Propose → vote → close, as in [council.md](council.md). Notes specific to this call: + +- Use `utility.batch` **per tenant group**, not `batch_all` across everything. + `batch_all` is atomic, so one underfunded twin rolls back every other migration in + the batch along with its settlement. +- A motion is capped by `MaxProposalWeight` (50% of max block weight), which bounds + it at roughly 900–1,800 contracts. The weight scales with how many contracts the + source and destination nodes already hold. + +## Common errors + +| Error | Meaning | +| --- | --- | +| `ContractNotExists` | wrong id, or already cancelled | +| `InvalidContractType` | not a node contract (name or rent) | +| `ContractNotInCreatedState` | in grace or deleted — or it entered grace during this call's own billing, which means the twin is underfunded | +| `ContractAlreadyOnNode` | source and destination are the same | +| `NodeNotInSameFarm` | cross-farm move; not supported | +| `NodeIsOptedOutOfV3Billing` | either side is opted out | +| `NodeNotAvailableToDeploy` | destination is rented, standby, or dedicated | +| `ContractIsNotUnique` | destination already has a contract with this deployment hash — pass a fresh hash | +| `ContractPaymentStateNotExists` | contract is already unbillable; cannot migrate | +| `BadOrigin` | not submitted as root or through a council motion | + +## Verifying + +After the call: + +- `Contracts(contract_id).node_id` is the destination +- the contract is gone from `ActiveNodeContracts(source)` and present in + `ActiveNodeContracts(destination)` +- a `ContractUpdated` event carries the new node id and hash + +## After the call + +Deploy on the destination. The order is forced — a node rejects a deployment whose +contract does not already name it, so the destination cannot be prepared in advance. + +The source deprovisions its stale copy on its next reconciliation (hourly). + +**Power the source down only after confirming the destination is provisioned and +serving** — never on `ActiveNodeContracts` being empty. diff --git a/substrate-node/pallets/pallet-burning/src/weights.rs b/substrate-node/pallets/pallet-burning/src/weights.rs index 4ab7d2cf9..abf45ecaf 100644 --- a/substrate-node/pallets/pallet-burning/src/weights.rs +++ b/substrate-node/pallets/pallet-burning/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_burning //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -45,8 +45,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 87_028_000 picoseconds. - Weight::from_parts(90_638_000, 1594) + // Minimum execution time: 86_124_000 picoseconds. + Weight::from_parts(90_443_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -60,8 +60,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `109` // Estimated: `1594` - // Minimum execution time: 87_028_000 picoseconds. - Weight::from_parts(90_638_000, 1594) + // Minimum execution time: 86_124_000 picoseconds. + Weight::from_parts(90_443_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate-node/pallets/pallet-dao/creating_motion_council.md b/substrate-node/pallets/pallet-dao/creating_motion_council.md index 55a63c7d3..6099fc2af 100644 --- a/substrate-node/pallets/pallet-dao/creating_motion_council.md +++ b/substrate-node/pallets/pallet-dao/creating_motion_council.md @@ -76,6 +76,8 @@ Once the motion is closed it is removed from list and the `proposal` extrinsic i * `cancelContract()` (cancels a contract) * `approveSolutionProvider()` (approves a solution provider) * `changeBillingFrequency()` (changes the billing frequency) +* `cancelContractCollective()` (cancels a contract) +* `migrateNodeContract()` (moves a node contract to another node in the same farm) ### tftBridgeModule diff --git a/substrate-node/pallets/pallet-dao/src/weights.rs b/substrate-node/pallets/pallet-dao/src/weights.rs index dba784072..9ed1208a8 100644 --- a/substrate-node/pallets/pallet-dao/src/weights.rs +++ b/substrate-node/pallets/pallet-dao/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_dao //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -58,8 +58,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `208` // Estimated: `4687` - // Minimum execution time: 39_181_000 picoseconds. - Weight::from_parts(61_474_000, 4687) + // Minimum execution time: 38_887_000 picoseconds. + Weight::from_parts(65_234_000, 4687) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -77,8 +77,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `979` // Estimated: `4444` - // Minimum execution time: 50_866_000 picoseconds. - Weight::from_parts(79_104_000, 4444) + // Minimum execution time: 48_653_000 picoseconds. + Weight::from_parts(90_109_000, 4444) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -92,8 +92,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `487` // Estimated: `4687` - // Minimum execution time: 40_601_000 picoseconds. - Weight::from_parts(57_403_000, 4687) + // Minimum execution time: 49_894_000 picoseconds. + Weight::from_parts(55_437_000, 4687) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -111,8 +111,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `521` // Estimated: `4687` - // Minimum execution time: 48_263_000 picoseconds. - Weight::from_parts(76_166_000, 4687) + // Minimum execution time: 52_398_000 picoseconds. + Weight::from_parts(82_391_000, 4687) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -136,8 +136,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `208` // Estimated: `4687` - // Minimum execution time: 39_181_000 picoseconds. - Weight::from_parts(61_474_000, 4687) + // Minimum execution time: 38_887_000 picoseconds. + Weight::from_parts(65_234_000, 4687) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -155,8 +155,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `979` // Estimated: `4444` - // Minimum execution time: 50_866_000 picoseconds. - Weight::from_parts(79_104_000, 4444) + // Minimum execution time: 48_653_000 picoseconds. + Weight::from_parts(90_109_000, 4444) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -170,8 +170,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `487` // Estimated: `4687` - // Minimum execution time: 40_601_000 picoseconds. - Weight::from_parts(57_403_000, 4687) + // Minimum execution time: 49_894_000 picoseconds. + Weight::from_parts(55_437_000, 4687) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -189,8 +189,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `521` // Estimated: `4687` - // Minimum execution time: 48_263_000 picoseconds. - Weight::from_parts(76_166_000, 4687) + // Minimum execution time: 52_398_000 picoseconds. + Weight::from_parts(82_391_000, 4687) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/substrate-node/pallets/pallet-kvstore/src/weights.rs b/substrate-node/pallets/pallet-kvstore/src/weights.rs index 8ae6c0557..6a8ecac3d 100644 --- a/substrate-node/pallets/pallet-kvstore/src/weights.rs +++ b/substrate-node/pallets/pallet-kvstore/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_kvstore //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -46,8 +46,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 23_122_000 picoseconds. - Weight::from_parts(26_525_000, 0) + // Minimum execution time: 19_873_000 picoseconds. + Weight::from_parts(27_293_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `TFKVStore::TFKVStore` (r:1 w:1) @@ -56,8 +56,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `146` // Estimated: `3611` - // Minimum execution time: 23_369_000 picoseconds. - Weight::from_parts(24_237_000, 3611) + // Minimum execution time: 40_476_000 picoseconds. + Weight::from_parts(48_129_000, 3611) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -71,8 +71,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 23_122_000 picoseconds. - Weight::from_parts(26_525_000, 0) + // Minimum execution time: 19_873_000 picoseconds. + Weight::from_parts(27_293_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `TFKVStore::TFKVStore` (r:1 w:1) @@ -81,8 +81,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `146` // Estimated: `3611` - // Minimum execution time: 23_369_000 picoseconds. - Weight::from_parts(24_237_000, 3611) + // Minimum execution time: 40_476_000 picoseconds. + Weight::from_parts(48_129_000, 3611) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate-node/pallets/pallet-smart-contract/readme.md b/substrate-node/pallets/pallet-smart-contract/readme.md index cfefe6823..461b66b6b 100644 --- a/substrate-node/pallets/pallet-smart-contract/readme.md +++ b/substrate-node/pallets/pallet-smart-contract/readme.md @@ -53,3 +53,5 @@ Dispatchable functions of this pallet. * `change_billing_frequency`: Change the billing frequency of all contracts, the origin for this call is a configurable origin. * `attach_solution_provider_id`: Attach a solution provider id to a contract * `set_dedicated_node_extra_fee`: Set an extra fee for a dedicated node +* `cancel_contract_collective`: Cancel a contract, the origin for this call is a configurable origin +* `migrate_node_contract`: Move a node contract to another node in the same farm without cancelling it. This moves the booking, not the workload; the owner must redeploy on the destination. The origin for this call is a configurable origin diff --git a/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs b/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs index 60b67866c..ea1fe3d42 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/benchmarking.rs @@ -503,6 +503,84 @@ benchmarks! { }.into()); } + // migrate_node_contract() + // Setup mirrors bill_contract_for_block: the extrinsic calls bill_contract + // inline, and with a fresh contract seconds_elapsed == 0 so billing would take + // its zero-amount early return and the benchmark would miss the real cost. + migrate_node_contract { + // n = length of the SOURCE node's ActiveNodeContracts vector, which the + // extrinsic read-modify-writes. The weight closure passes max(source, dest) + // and the source is the crowded side by construction -- migrating OFF a full + // node is the point of the feature. Ranges here are inclusive on BOTH ends + // (frame/benchmarking v1), so this covers 1..=100. + let n in 1 .. 100; + + let farmer: T::AccountId = account("Alice", 0, 0); + _prepare_farm_with_node::(farmer.clone()); // farm 1, node 1 + + // A second node on the SAME farm needs its own twin: pallet-tfgrid enforces + // one node per twin (NodeIdByTwinID -> NodeWithTwinIdExists). _create_node + // only requires the farm to exist, not that the caller owns it. + let node_owner: T::AccountId = account("Charlie", 0, 2); + _create_twin::(node_owner.clone()); + _create_node::(node_owner); // node 2, farm 1 + + let user: T::AccountId = account("Bob", 0, 1); + let user_lookup = T::Lookup::unlookup(user.clone()); + let balance_init_amount = ::Balance::saturated_from(100000000 as u128); + Balances::::force_set_balance(RawOrigin::Root.into(), user_lookup, balance_init_amount).unwrap(); + _create_twin::(user.clone()); + _create_node_contract::(user.clone()); // contract 1 on node 1 + let contract_id = 1; // created FIRST, so id is stable + let destination_node_id = 2; + + // n - 1 siblings pinned to the source node, so the vector the extrinsic + // retains over is n long. Each needs its own deployment hash: the pair + // (node_id, hash) is unique in ContractIDByNodeIDAndHash, so reusing + // _create_node_contract's fixed hash would fail with ContractIsNotUnique + // on the second iteration. + for i in 1..n { + let mut sibling_hash = get_deployment_hash_input(b"00000000000000000000000000000000"); + sibling_hash[0] = (i % 256) as u8; + sibling_hash[1] = (i / 256) as u8; + assert_ok!(SmartContractModule::::create_node_contract( + RawOrigin::Signed(user.clone()).into(), + 1, + sibling_hash, + get_deployment_data_input::(b"some_data123"), + 0, + None, + )); + } + + // advance time and report usage so bill_contract does real work + let now = SmartContractModule::::get_current_timestamp_in_secs(); + let elapsed_seconds = 5; + let then: u64 = now + elapsed_seconds; + pallet_timestamp::Pallet::::set_timestamp((then * 1000).try_into().unwrap()); + + _push_contract_used_resources_report::(farmer.clone()); + _push_contract_nru_consumption_report::(farmer.clone(), then, elapsed_seconds); + + let new_deployment_hash = get_deployment_hash_input(b"858f8fb2184b15ecb8c0be8b95398c82"); + }: _(RawOrigin::Root, contract_id, destination_node_id, Some(new_deployment_hash)) + verify { + let contract = SmartContractModule::::contracts(contract_id).unwrap(); + assert_eq!(contract.get_node_id(), destination_node_id); + // the source keeps its n - 1 siblings; only the migrated contract leaves + let source_remaining = SmartContractModule::::active_node_contracts(1); + assert!(!source_remaining.contains(&contract_id)); + assert_eq!(source_remaining.len(), (n - 1) as usize); + assert_eq!(SmartContractModule::::active_node_contracts(destination_node_id), vec![contract_id]); + assert_eq!( + SmartContractModule::::node_contract_by_hash(destination_node_id, new_deployment_hash), + contract_id + ); + let old_hash = get_deployment_hash_input(b"858f8fb2184b15ecb8c0be8b95398c81"); + assert_eq!(SmartContractModule::::node_contract_by_hash(1, old_hash), 0); + assert_last_event::(Event::ContractUpdated(contract).into()); + } + // Calling the `impl_benchmark_test_suite` macro inside the `benchmarks` // block will generate one #[test] function per benchmark impl_benchmark_test_suite!(SmartContractModule, crate::mock::new_test_ext(), crate::mock::TestRuntime) diff --git a/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs b/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs index a85dbf82f..01339ca8d 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/grid_contract.rs @@ -299,6 +299,175 @@ impl Pallet { Ok(().into()) } + /// Number of contracts the weight of a migration should be charged against: + /// the migration read-modify-writes BOTH the source and destination + /// `ActiveNodeContracts` vectors, which are unbounded. Uses the max of the + /// two lengths -- never the destination alone, which would systematically + /// under-count migrating off a crowded node. + pub fn migrate_weight_contracts(contract_id: u64, node_id: u32) -> u32 { + let source_len = Contracts::::get(contract_id) + .map(|c| ActiveNodeContracts::::get(c.get_node_id()).len()) + .unwrap_or(0); + let destination_len = ActiveNodeContracts::::get(node_id).len(); + source_len.max(destination_len).try_into().unwrap_or(u32::MAX) + } + + /// Move a node contract to another node in the same farm without cancelling it. + /// + /// Origin is checked by the caller (`RestrictedOrigin`); this fn performs no + /// authorization of its own. + pub fn _migrate_node_contract( + contract_id: u64, + node_id: u32, + deployment_hash: Option, + ) -> DispatchResultWithPostInfo { + // --- validation (reads only) --- + let contract = Contracts::::get(contract_id).ok_or(Error::::ContractNotExists)?; + let node_contract = Self::get_node_contract(&contract)?; + // Named for the invariant, not for grace: this also rejects Deleted. + ensure!( + matches!(contract.state, types::ContractState::Created), + Error::::ContractNotInCreatedState + ); + + let source_node_id = node_contract.node_id; + ensure!( + source_node_id != node_id, + Error::::ContractAlreadyOnNode + ); + + let source_node = + pallet_tfgrid::Nodes::::get(source_node_id).ok_or(Error::::NodeNotExists)?; + let farm = pallet_tfgrid::Farms::::get(source_node.farm_id) + .ok_or(Error::::FarmNotExists)?; + + let destination_node = + pallet_tfgrid::Nodes::::get(node_id).ok_or(Error::::NodeNotExists)?; + ensure!( + destination_node.farm_id == source_node.farm_id, + Error::::NodeNotInSameFarm + ); + + // Excluded on BOTH sides: opting out waives billing, and a rent contract + // flips the cost basis. Either would make the move reprice the tenant. + ensure!( + !pallet_tfgrid::NodeV3BillingOptOut::::contains_key(source_node_id) + && !pallet_tfgrid::NodeV3BillingOptOut::::contains_key(node_id), + Error::::NodeIsOptedOutOfV3Billing + ); + ensure!( + !ActiveRentContractForNode::::contains_key(source_node_id) + && !ActiveRentContractForNode::::contains_key(node_id), + Error::::NodeNotAvailableToDeploy + ); + + // Destination must be able to accept a deployment. The SOURCE node's power + // state is deliberately not checked: migrating off a node that is about to + // be shut down is the point of this extrinsic. + let node_power = pallet_tfgrid::NodePower::::get(node_id); + ensure!( + !node_power.is_standby_phase(), + Error::::NodeNotAvailableToDeploy + ); + ensure!( + DedicatedNodesExtraFee::::get(node_id) == 0 && !farm.dedicated_farm, + Error::::NodeNotAvailableToDeploy + ); + + let target_hash = deployment_hash.unwrap_or(node_contract.deployment_hash); + // Stricter than _create_node_contract, which allows overwriting a Deleted + // entry: there is no restore semantic here, and that contract's eventual + // remove_contract would unconditionally delete the key we just claimed. + ensure!( + !ContractIDByNodeIDAndHash::::contains_key(node_id, &target_hash), + Error::::ContractIsNotUnique + ); + + // --- settle at the SOURCE node's cost basis before anything moves --- + Self::bill_contract(contract_id)?; + + // Billing may have mutated or removed the contract, so re-read it. + // NOTE: the two branches below deliberately answer differently. + // Two outcomes of the inline bill_contract, and they get opposite answers. + // Both are right; do not "fix" one to match the other. + // + // Grace => Err (LIVE path, covered by + // test_migrate_node_contract_fails_when_inline_billing_pushes_to_grace_period): + // an underfunded twin sends manage_contract_state Created -> GracePeriod + // inside this very call. Nothing was removed from the source yet, the + // contract still pins the node, and Ok would be a silent no-op that a batch + // caller cannot tell from success -- no ContractUpdated is emitted either + // way. The rolled-back cycle is redone by the offchain worker within the hour. + // + // Gone => Ok (currently UNREACHABLE, kept as a guard): bill_contract only + // deletes a contract once an EXISTING grace period has elapsed, and step 3 + // above already refused anything not in Created. A Created contract can at + // worst enter grace here, never leave it. The arm stays because it is the + // correct answer if that ever changes -- remove_contract would already have + // cleared the source's ActiveNodeContracts entry, so the node is drained and + // the goal is met, while Err would roll back a terminal settlement and its + // reward distribution for no gain. + let Some(mut contract) = Contracts::::get(contract_id) else { + return Ok(().into()); + }; + ensure!( + matches!(contract.state, types::ContractState::Created), + Error::::ContractNotInCreatedState + ); + let mut node_contract = Self::get_node_contract(&contract)?; + + // --- mutation --- + // Guarded: _update_node_contract never enforced hash uniqueness, so this key + // may already point at a DIFFERENT live contract. An unguarded remove would + // destroy that contract's index entry. + if ContractIDByNodeIDAndHash::::get(source_node_id, &node_contract.deployment_hash) + == contract_id + { + ContractIDByNodeIDAndHash::::remove( + source_node_id, + &node_contract.deployment_hash, + ); + } + ContractIDByNodeIDAndHash::::insert(node_id, &target_hash, contract_id); + + Self::remove_active_node_contract(source_node_id, contract_id); + + // Push open-coded, the way _create_node_contract does it: validation + // already refused source == destination, and the remove above just cleared + // the source, so the destination cannot already hold this id. + let mut destination_contracts = ActiveNodeContracts::::get(&node_id); + destination_contracts.push(contract_id); + ActiveNodeContracts::::insert(&node_id, &destination_contracts); + + // NodeContractResources is deliberately left alone. It is contract-keyed, so it + // survives the move, and that is what keeps billing continuous. Clearing it + // looks tidy and is a trap: calculate_resources_cost_units_usd (cost.rs) derives + // the whole node-contract cost from this entry, so zeroing it sends every + // caller of bill_contract -- including a manual bill_contract_for_block -- + // down the zero-amount early return. No overdraft means no grace period, which + // means no 14-day auto-delete: a free, immortal contract pinning the + // destination against node_has_no_active_contracts forever. + node_contract.node_id = node_id; + node_contract.deployment_hash = target_hash; + contract.contract_type = types::ContractData::NodeContract(node_contract); + Contracts::::insert(contract_id, &contract); + + // bill_contract skips this stamp on its zero-amount early return, leaving a + // stale clock. Harmless today (a zero-cost contract stays zero-cost), but the + // deployment_hash parameter above can change what the destination deploys, + // so stamp unconditionally. Nothing is forgiven: the window's cost was zero. + let mut contract_payment_state = ContractPaymentState::::get(contract_id) + .ok_or(Error::::ContractPaymentStateNotExists)?; + contract_payment_state.last_updated_seconds = Self::get_current_timestamp_in_secs(); + ContractPaymentState::::insert(contract_id, &contract_payment_state); + + // No dedicated migration event: ContractUpdated carries the new node id and + // deployment hash, which is exactly what the indexer writes. + Self::deposit_event(Event::ContractUpdated(contract)); + + Ok(().into()) + } + pub fn _cancel_contract( account_id: T::AccountId, contract_id: u64, @@ -621,11 +790,31 @@ impl Pallet { // we know contract exists, fetch it // if the node is trying to send garbage data we can throw an error here if let Some(contract) = Contracts::::get(contract_resource.contract_id) { - let node_contract = Self::get_node_contract(&contract)?; - ensure!( - node_contract.node_id == node_id, - Error::::NodeNotAuthorizedToComputeReport - ); + // Consistency, not a new policy: the branch above already skips a + // report naming a contract that does not exist -- silently, and for + // free, since this returns Ok(Pays::No). A report naming a contract + // that exists but sits on another node is the same class of mistake, + // so it gets the same treatment. Aborting the whole batch and + // charging for it was the odd case out. + // It also stops being unreachable with migrate_node_contract, which + // leaves a LIVE contract on another node until this one reconciles. + let Ok(node_contract) = Self::get_node_contract(&contract) else { + log::warn!( + "node {:?} reported resources for non-node contract {:?}, skipping", + node_id, + contract_resource.contract_id + ); + continue; + }; + if node_contract.node_id != node_id { + log::warn!( + "node {:?} reported resources for contract {:?} which now lives on node {:?}, skipping", + node_id, + contract_resource.contract_id, + node_contract.node_id + ); + continue; + } // Do insert NodeContractResources::::insert(contract.contract_id, &contract_resource); @@ -666,11 +855,31 @@ impl Pallet { // if the node is trying to send garbage data we can throw an error here let contract = Contracts::::get(report.contract_id).ok_or(Error::::ContractNotExists)?; - let node_contract = Self::get_node_contract(&contract)?; - ensure!( - node_contract.node_id == node_id, - Error::::NodeNotAuthorizedToComputeReport - ); + // Consistency, not a new policy: the two contains_key guards above + // already skip a report naming a contract that does not exist -- + // silently, and for free, since this returns Ok(Pays::No). A report + // naming a contract that exists but sits on another node is the same + // class of mistake, so it gets the same treatment. Aborting the whole + // batch and charging for it was the odd case out. + // It also stops being unreachable with migrate_node_contract, which + // leaves a LIVE contract on another node until this one reconciles. + let Ok(node_contract) = Self::get_node_contract(&contract) else { + log::warn!( + "node {:?} reported NRU for non-node contract {:?}, skipping", + node_id, + report.contract_id + ); + continue; + }; + if node_contract.node_id != node_id { + log::warn!( + "node {:?} reported NRU for contract {:?} which now lives on node {:?}, skipping", + node_id, + report.contract_id, + node_contract.node_id + ); + continue; + } report.calculate_report_cost_units_usd::(&pricing_policy); diff --git a/substrate-node/pallets/pallet-smart-contract/src/lib.rs b/substrate-node/pallets/pallet-smart-contract/src/lib.rs index 14ba20f11..2b99bcfc8 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/lib.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/lib.rs @@ -1,4 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +// The benchmarks! macro expands one test per benchmark via impl_bench_name_tests; +// the default limit of 128 is exceeded once the pallet has this many benchmarks. +#![recursion_limit = "256"] pub mod billing; pub mod cost; @@ -375,6 +378,11 @@ pub mod pallet { TwinNotAuthorizedToUpdateContract, TwinNotAuthorizedToCancelContract, NodeNotAuthorizedToDeployContract, + // RESERVED -- no longer constructed. The report handlers used to raise this + // and abort the whole batch; they now log::warn! and skip the item instead. + // Kept because indices are declaration order (see the note at the end of + // this enum): deleting it renumbers every variant below, so a client with a + // cached mapping would misname them. Do not reuse the slot either. NodeNotAuthorizedToComputeReport, PricingPolicyNotExists, ContractIsNotUnique, @@ -419,6 +427,12 @@ pub mod pallet { RewardDistributionError, ContractPaymentStateNotExists, OnlyTwinAdminCanDeployOnThisNode, + // Appended for migrate_node_contract. Error variants have no explicit + // index and are SCALE-encoded by declaration order: append only, never insert. + NodeNotInSameFarm, + ContractAlreadyOnNode, + NodeIsOptedOutOfV3Billing, + ContractNotInCreatedState, } #[pallet::genesis_config] @@ -703,6 +717,34 @@ pub mod pallet { ::RestrictedOrigin::ensure_origin(origin)?; Self::_cancel_contract_collective(contract_id, types::Cause::CanceledByCollective) } + + /// Move a node contract to another node within the same farm, keeping the + /// contract alive instead of cancelling it. + /// + /// This moves the booking, not the workload. The source node deprovisions its + /// copy once it sees the contract has left its list, deleting the disks; the + /// owner must redeploy on the destination. An emptied node is therefore NOT + /// safe to power off just because it now reports no active contracts. + /// + /// Council/root only for now; owner and farmer paths are deferred to a future + /// call index (never by widening this one's origin check). + /// + /// `deployment_hash` is `None` to keep the current hash. No dedicated event is + /// emitted: `ContractUpdated` already carries the new node id and hash, which is + /// what the indexer and grid proxy consume. + #[pallet::call_index(22)] + #[pallet::weight(::WeightInfo::migrate_node_contract( + Pallet::::migrate_weight_contracts(*contract_id, *node_id) + ))] + pub fn migrate_node_contract( + origin: OriginFor, + contract_id: u64, + node_id: u32, + deployment_hash: Option, + ) -> DispatchResultWithPostInfo { + ::RestrictedOrigin::ensure_origin(origin)?; + Self::_migrate_node_contract(contract_id, node_id, deployment_hash) + } } #[pallet::hooks] diff --git a/substrate-node/pallets/pallet-smart-contract/src/tests.rs b/substrate-node/pallets/pallet-smart-contract/src/tests.rs index 1ca11c5bc..1f13cd739 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/tests.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/tests.rs @@ -362,6 +362,659 @@ fn test_cancel_contract_collective_by_dao_approval_works() { }); } +// --------------------------------------------------------------------------- +// migrate_node_contract +// --------------------------------------------------------------------------- + +#[test] +fn test_migrate_node_contract_works() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let hash = prepare_two_nodes_and_node_contract(); + let contract_id = 1; + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + contract_id, + 2, + None + )); + + let contract = SmartContractModule::contracts(contract_id).unwrap(); + assert_eq!(contract.get_node_id(), 2); + assert_eq!(contract.state, types::ContractState::Created); + + // source drained, destination holds it + assert!(SmartContractModule::active_node_contracts(1).is_empty()); + assert_eq!( + SmartContractModule::active_node_contracts(2), + vec![contract_id] + ); + + // hash index re-keyed, source entry gone + assert_eq!(SmartContractModule::node_contract_by_hash(2, hash), contract_id); + assert_eq!(SmartContractModule::node_contract_by_hash(1, hash), 0); + }); +} + +#[test] +fn test_migrate_node_contract_by_council_approval_works() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_ok!(SmartContractModule::migrate_node_contract( + pallet_collective::RawOrigin::Members(3, 5).into(), + 1, + 2, + None + )); + + assert_eq!(SmartContractModule::contracts(1).unwrap().get_node_id(), 2); + }); +} + +#[test] +fn test_migrate_node_contract_with_new_deployment_hash_works() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let old_hash = prepare_two_nodes_and_node_contract(); + let contract_id = 1; + let new_hash: types::HexHash = [9u8; 32]; + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + contract_id, + 2, + Some(new_hash) + )); + + let contract = SmartContractModule::contracts(contract_id).unwrap(); + let node_contract = SmartContractModule::get_node_contract(&contract).unwrap(); + assert_eq!(node_contract.deployment_hash, new_hash); + + assert_eq!( + SmartContractModule::node_contract_by_hash(2, new_hash), + contract_id + ); + assert_eq!(SmartContractModule::node_contract_by_hash(2, old_hash), 0); + assert_eq!(SmartContractModule::node_contract_by_hash(1, old_hash), 0); + }); +} + +#[test] +fn test_migrate_node_contract_stamps_clock_when_nothing_billed_works() { + // Regression guard: bill_contract takes its zero-amount early return for a + // contract with no resources and no IPs, and that path does NOT stamp the + // clock. The extrinsic must stamp it anyway. + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + let contract_id = 1; + + let before = SmartContractModule::contract_payment_state(contract_id) + .unwrap() + .last_updated_seconds; + + run_to_block(100, None); + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + contract_id, + 2, + None + )); + + let after = SmartContractModule::contract_payment_state(contract_id) + .unwrap() + .last_updated_seconds; + assert!( + after > before, + "billing clock must advance even when nothing was billed" + ); + }); +} + +#[test] +fn test_migrate_node_contract_off_standby_source_node_works() { + // The SOURCE node's power state is deliberately not checked -- migrating off a + // node you are about to shut down is the point of the extrinsic. + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_ok!(TfgridModule::change_power_state( + RuntimeOrigin::signed(alice()), + tfchain_support::types::Power::Down + )); + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + 1, + 2, + None + )); + + assert_eq!(SmartContractModule::contracts(1).unwrap().get_node_id(), 2); + }); +} + +#[test] +fn test_migrate_node_contract_preserves_other_contracts_works() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_and_two_nodes(); + + for who in [bob(), charlie(), dave()] { + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(who), + 1, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); + } + // three contracts on node 1; move the middle one + assert_eq!(SmartContractModule::active_node_contracts(1).len(), 3); + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + 2, + 2, + None + )); + + assert_eq!(SmartContractModule::active_node_contracts(1), vec![1, 3]); + assert_eq!(SmartContractModule::active_node_contracts(2), vec![2]); + }); +} + +#[test] +fn test_migrate_node_contract_then_cancel_cleans_up_destination_works() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let hash = prepare_two_nodes_and_node_contract(); + let contract_id = 1; + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + contract_id, + 2, + None + )); + assert_ok!(SmartContractModule::cancel_contract( + RuntimeOrigin::signed(bob()), + contract_id + )); + + assert_eq!(SmartContractModule::contracts(contract_id), None); + assert!(SmartContractModule::active_node_contracts(2).is_empty()); + assert_eq!(SmartContractModule::node_contract_by_hash(2, hash), 0); + assert_eq!(SmartContractModule::contract_payment_state(contract_id), None); + }); +} + +#[test] +fn test_migrate_node_contract_not_exists_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_and_two_nodes(); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 2, None), + Error::::ContractNotExists + ); + }); +} + +#[test] +fn test_migrate_name_contract_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_and_two_nodes(); + assert_ok!(SmartContractModule::create_name_contract( + RuntimeOrigin::signed(bob()), + b"foobar".to_vec() + )); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 2, None), + Error::::InvalidContractType + ); + }); +} + +#[test] +fn test_migrate_node_contract_to_same_node_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 1, None), + Error::::ContractAlreadyOnNode + ); + }); +} + +#[test] +fn test_migrate_node_contract_to_unknown_node_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 99, None), + Error::::NodeNotExists + ); + }); +} + +#[test] +fn test_migrate_node_contract_to_standby_node_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + // charlie owns node 2 + assert_ok!(TfgridModule::change_power_state( + RuntimeOrigin::signed(charlie()), + tfchain_support::types::Power::Down + )); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 2, None), + Error::::NodeNotAvailableToDeploy + ); + }); +} + +#[test] +fn test_migrate_node_contract_from_opted_out_node_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_ok!(TfgridModule::opt_out_of_v3_billing( + RuntimeOrigin::signed(alice()), + 1, + )); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 2, None), + Error::::NodeIsOptedOutOfV3Billing + ); + }); +} + +#[test] +fn test_migrate_node_contract_to_opted_out_node_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_ok!(TfgridModule::opt_out_of_v3_billing( + RuntimeOrigin::signed(alice()), + 2, + )); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 2, None), + Error::::NodeIsOptedOutOfV3Billing + ); + }); +} + +#[test] +fn test_migrate_node_contract_to_dedicated_node_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_ok!(SmartContractModule::set_dedicated_node_extra_fee( + RuntimeOrigin::signed(alice()), + 2, + 1000 + )); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 2, None), + Error::::NodeNotAvailableToDeploy + ); + }); +} + +#[test] +fn test_migrate_node_contract_with_duplicate_hash_on_destination_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let hash = prepare_two_nodes_and_node_contract(); + + // same deployment hash already live on node 2 + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(dave()), + 2, + hash, + get_deployment_data(), + 0, + None + )); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 2, None), + Error::::ContractIsNotUnique + ); + }); +} + +#[test] +fn test_migrate_node_contract_from_signed_origin_fails() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let _ = prepare_two_nodes_and_node_contract(); + + assert_noop!( + SmartContractModule::migrate_node_contract( + RuntimeOrigin::signed(alice()), + 1, + 2, + None + ), + sp_runtime::traits::BadOrigin + ); + }); +} + +#[test] +fn test_migrate_node_contract_does_not_destroy_another_contracts_hash_index() { + // update_node_contract has no hash-uniqueness check (unlike create), so a second + // contract can claim a hash already indexed to a first one, orphaning the first. + // Migrating the orphaned contract must NOT remove the index entry that now + // belongs to the live second contract. + new_test_ext().execute_with(|| { + run_to_block(1, None); + let hash_a = prepare_two_nodes_and_node_contract(); // contract 1, bob, node 1 + + // contract 2 on the same node, owned by charlie + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(charlie()), + 1, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); + + // charlie points contract 2 at contract 1's hash; the index now maps + // (node 1, hash_a) -> 2, and contract 1 is orphaned in the index + assert_ok!(SmartContractModule::update_node_contract( + RuntimeOrigin::signed(charlie()), + 2, + hash_a, + get_deployment_data() + )); + assert_eq!(SmartContractModule::node_contract_by_hash(1, hash_a), 2); + + // migrating the orphaned contract 1 must leave contract 2's entry alone + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + 1, + 2, + None + )); + + assert_eq!( + SmartContractModule::node_contract_by_hash(1, hash_a), + 2, + "the live contract's index entry must survive the migration" + ); + assert_eq!(SmartContractModule::contracts(1).unwrap().get_node_id(), 2); + }); +} + +#[test] +fn test_migrate_node_contract_leaves_storage_untouched_on_rejection() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + let hash = prepare_two_nodes_and_node_contract(); + + assert_noop!( + SmartContractModule::migrate_node_contract(RawOrigin::Root.into(), 1, 1, None), + Error::::ContractAlreadyOnNode + ); + + assert_eq!(SmartContractModule::active_node_contracts(1), vec![1]); + assert!(SmartContractModule::active_node_contracts(2).is_empty()); + assert_eq!(SmartContractModule::node_contract_by_hash(1, hash), 1); + assert_eq!(SmartContractModule::contracts(1).unwrap().get_node_id(), 1); + }); +} + +/// F6 -- the report handlers must skip a stale entry, not abort the batch. +/// +/// Before migration existed this mismatch was unreachable: cancelling removes the +/// contract, and both loops already skip missing ones. `migrate_node_contract` is +/// the first construct that leaves a LIVE contract pointing at a different node, so +/// until the source node reconciles it keeps submitting batches naming a contract +/// that has moved. Aborting on those would freeze reporting for every OTHER contract +/// still on that node. +#[test] +fn test_report_contract_resources_skips_migrated_contract_and_keeps_the_rest() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_and_two_nodes(); + + // two contracts on node 1 + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(bob()), + 1, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(bob()), + 1, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); + let migrated = 1; + let stayed = 2; + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + migrated, + 2, + None + )); + + // node 1 (alice) reports for BOTH -- it has not reconciled yet + let reports = vec![ + types::ContractResources { + contract_id: migrated, + used: Resources { + cru: 2, + hru: 0, + mru: 2 * GIGABYTE, + sru: 60 * GIGABYTE, + }, + }, + types::ContractResources { + contract_id: stayed, + used: Resources { + cru: 4, + hru: 0, + mru: 4 * GIGABYTE, + sru: 80 * GIGABYTE, + }, + }, + ]; + + // the batch succeeds rather than reverting + assert_ok!(SmartContractModule::report_contract_resources( + RuntimeOrigin::signed(alice()), + reports + )); + + // the stale entry was dropped ... + assert_eq!( + SmartContractModule::node_contract_resources(migrated).used, + Resources::empty() + ); + // ... and the contract still on node 1 was NOT collateral damage + assert_eq!( + SmartContractModule::node_contract_resources(stayed).used.cru, + 4 + ); + }); +} + +/// F6 -- same guarantee for the NRU batch (`add_nru_reports` -> `_compute_reports`). +/// This is the periodic report, so an abort here would freeze `amount_unbilled` for +/// every other contract on the node on every submission. +#[test] +fn test_add_nru_reports_skips_migrated_contract_and_keeps_the_rest() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_and_two_nodes(); + + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(bob()), + 1, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(bob()), + 1, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); + let migrated = 1; + let stayed = 2; + + assert_ok!(SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + migrated, + 2, + None + )); + + let gigabyte = 1000 * 1000 * 1000; + let reports = vec![ + super::types::NruConsumption { + contract_id: migrated, + nru: 3 * gigabyte, + timestamp: get_timestamp_in_seconds_for_block(1), + window: 6, + }, + super::types::NruConsumption { + contract_id: stayed, + nru: 3 * gigabyte, + timestamp: get_timestamp_in_seconds_for_block(1), + window: 6, + }, + ]; + + assert_ok!(SmartContractModule::add_nru_reports( + RuntimeOrigin::signed(alice()), + reports + )); + + // stale entry contributed nothing + assert_eq!( + SmartContractModule::contract_billing_information_by_id(migrated).amount_unbilled, + 0 + ); + // the other contract on the node was still billed for its traffic + assert!( + SmartContractModule::contract_billing_information_by_id(stayed).amount_unbilled > 0 + ); + }); +} + +/// F5 -- the post-billing state re-check is reachable, and this is the branch. +/// +/// `migrate_node_contract` bills inline, between validation and mutation. If that +/// billing finds the twin short of funds, `manage_contract_state` drops the contract +/// to `GracePeriod` and the re-read then fails the `Created` check. Returning Err is +/// deliberate: nothing was removed from the source yet, the contract still pins the +/// node, and Ok would be a silent no-op a batch caller could not distinguish from +/// success. The rolled-back billing cycle is redone by the offchain worker. +#[test] +fn test_migrate_node_contract_fails_when_inline_billing_pushes_to_grace_period() { + new_test_ext().execute_with(|| { + run_to_block(1, None); + prepare_farm_and_two_nodes(); + + TFTPriceModule::set_prices(RuntimeOrigin::signed(alice()), 50, 101).unwrap(); + + // charlie is the underfunded twin the grace-period suite uses: it can afford + // one billing cycle, not two + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(charlie()), + 1, + generate_deployment_hash(), + get_deployment_data(), + 0, + None + )); + let contract_id = 1; + + // Report the whole node as used, so a single short cycle already costs more + // than charlie's 150_000. The standard fixture footprint is affordable for + // about one cycle, which is not enough to force the transition below. + assert_ok!(SmartContractModule::report_contract_resources( + RuntimeOrigin::signed(alice()), + vec![types::ContractResources { + contract_id, + used: Resources { + cru: 8, + hru: 1024 * GIGABYTE, + mru: 16 * GIGABYTE, + sru: 512 * GIGABYTE, + }, + }] + )); + + // Stop short of block 11. Contract 1 is billed on blocks where + // `block % BillingFrequency == 1`, and crossing one would make the offchain + // worker sign -- which panics without a keystore, and would also settle the + // contract before the extrinsic gets to it. Staying below 11 leaves the cost + // accrued but unbilled and the contract in `Created`, so validation step 3 + // passes and the state change happens inside the extrinsic's own + // bill_contract, which is the branch under test. + run_to_block(10, None); + assert_eq!( + SmartContractModule::contracts(contract_id).unwrap().state, + types::ContractState::Created + ); + + assert_noop!( + SmartContractModule::migrate_node_contract( + RawOrigin::Root.into(), + contract_id, + 2, + None + ), + Error::::ContractNotInCreatedState + ); + + // assert_noop already proves storage is untouched; assert the intent too -- + // the contract still pins the SOURCE node, which is what makes Err correct + assert_eq!(SmartContractModule::active_node_contracts(1), vec![contract_id]); + assert!(SmartContractModule::active_node_contracts(2).is_empty()); + }); +} + #[test] fn test_cancel_contract_collective_by_council_approval_works() { new_test_ext().execute_with(|| { @@ -5159,6 +5812,58 @@ pub fn prepare_farm_and_node() { .unwrap(); } +/// farm 1 with node 1 (alice's twin) AND node 2 (charlie's twin), same farm. +/// A separate helper on purpose: adding a node to `prepare_farm_and_node` would +/// emit an extra NodeStored event and break the exact event-count assertions that +/// most of this suite relies on. +pub fn prepare_farm_and_two_nodes() { + prepare_farm_and_node(); + + let resources = ResourcesInput { + hru: 1024 * GIGABYTE, + sru: 512 * GIGABYTE, + cru: 8, + mru: 16 * GIGABYTE, + }; + + let location = LocationInput { + city: get_city_name_input(b"Ghent"), + country: get_country_name_input(b"Belgium"), + latitude: get_latitude_input(b"12.233213231"), + longitude: get_longitude_input(b"32.323112123"), + }; + + // pallet-tfgrid enforces one node per twin, so node 2 needs its own twin. + TfgridModule::create_node( + RuntimeOrigin::signed(charlie()), + 1, + resources, + location, + bounded_vec![], + false, + false, + None, + ) + .unwrap(); +} + +/// farm 1, two nodes, and a node contract owned by bob on node 1. +/// Distinct from `prepare_farm_node_and_node_contract`, where alice is farmer, +/// node owner AND contract owner. +pub fn prepare_two_nodes_and_node_contract() -> types::HexHash { + prepare_farm_and_two_nodes(); + let hash = generate_deployment_hash(); + assert_ok!(SmartContractModule::create_node_contract( + RuntimeOrigin::signed(bob()), + 1, + hash, + get_deployment_data(), + 0, + None + )); + hash +} + pub fn prepare_farm_node_and_node_contract() { prepare_farm_and_node(); let node_id = 1; diff --git a/substrate-node/pallets/pallet-smart-contract/src/weights.rs b/substrate-node/pallets/pallet-smart-contract/src/weights.rs index 715755161..4fda720e8 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/weights.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_smart_contract //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -56,6 +56,7 @@ pub trait WeightInfo { fn attach_solution_provider_id() -> Weight; fn set_dedicated_node_extra_fee() -> Weight; fn cancel_contract_collective() -> Weight; + fn migrate_node_contract(n: u32, ) -> Weight; } /// Weights for pallet_smart_contract using the Substrate node and recommended hardware. @@ -97,8 +98,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `868` // Estimated: `4333` - // Minimum execution time: 94_195_000 picoseconds. - Weight::from_parts(97_943_000, 4333) + // Minimum execution time: 153_191_000 picoseconds. + Weight::from_parts(175_883_000, 4333) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -112,8 +113,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `869` // Estimated: `4334` - // Minimum execution time: 45_239_000 picoseconds. - Weight::from_parts(66_665_000, 4334) + // Minimum execution time: 76_922_000 picoseconds. + Weight::from_parts(83_803_000, 4334) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -153,8 +154,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1650` // Estimated: `7590` - // Minimum execution time: 147_718_000 picoseconds. - Weight::from_parts(245_368_000, 7590) + // Minimum execution time: 202_502_000 picoseconds. + Weight::from_parts(290_227_000, 7590) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -178,8 +179,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `340` // Estimated: `3805` - // Minimum execution time: 47_262_000 picoseconds. - Weight::from_parts(48_046_000, 3805) + // Minimum execution time: 63_406_000 picoseconds. + Weight::from_parts(81_571_000, 3805) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -205,8 +206,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `949` // Estimated: `4414` - // Minimum execution time: 98_979_000 picoseconds. - Weight::from_parts(159_048_000, 4414) + // Minimum execution time: 164_903_000 picoseconds. + Weight::from_parts(191_094_000, 4414) .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -228,8 +229,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1286` // Estimated: `4751` - // Minimum execution time: 69_958_000 picoseconds. - Weight::from_parts(98_651_000, 4751) + // Minimum execution time: 118_126_000 picoseconds. + Weight::from_parts(149_359_000, 4751) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -245,8 +246,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `765` // Estimated: `4230` - // Minimum execution time: 43_491_000 picoseconds. - Weight::from_parts(62_625_000, 4230) + // Minimum execution time: 71_420_000 picoseconds. + Weight::from_parts(90_818_000, 4230) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -278,8 +279,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `776` // Estimated: `4241` - // Minimum execution time: 75_727_000 picoseconds. - Weight::from_parts(113_461_000, 4241) + // Minimum execution time: 96_038_000 picoseconds. + Weight::from_parts(126_167_000, 4241) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -317,8 +318,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1607` // Estimated: `7547` - // Minimum execution time: 140_505_000 picoseconds. - Weight::from_parts(219_590_000, 7547) + // Minimum execution time: 234_876_000 picoseconds. + Weight::from_parts(278_583_000, 7547) .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -330,8 +331,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `37` // Estimated: `1522` - // Minimum execution time: 20_215_000 picoseconds. - Weight::from_parts(30_997_000, 1522) + // Minimum execution time: 33_676_000 picoseconds. + Weight::from_parts(38_408_000, 1522) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -341,8 +342,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `215` // Estimated: `3680` - // Minimum execution time: 38_327_000 picoseconds. - Weight::from_parts(50_416_000, 3680) + // Minimum execution time: 40_855_000 picoseconds. + Weight::from_parts(47_531_000, 3680) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -384,8 +385,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `2077` // Estimated: `8017` - // Minimum execution time: 259_746_000 picoseconds. - Weight::from_parts(297_625_000, 8017) + // Minimum execution time: 290_205_000 picoseconds. + Weight::from_parts(318_180_000, 8017) .saturating_add(T::DbWeight::get().reads(18_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -399,8 +400,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `395` // Estimated: `6335` - // Minimum execution time: 34_359_000 picoseconds. - Weight::from_parts(36_044_000, 6335) + // Minimum execution time: 55_623_000 picoseconds. + Weight::from_parts(71_866_000, 6335) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -412,8 +413,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `489` // Estimated: `3954` - // Minimum execution time: 38_570_000 picoseconds. - Weight::from_parts(50_746_000, 3954) + // Minimum execution time: 42_779_000 picoseconds. + Weight::from_parts(52_030_000, 3954) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -425,8 +426,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `489` // Estimated: `3954` - // Minimum execution time: 30_886_000 picoseconds. - Weight::from_parts(31_463_000, 3954) + // Minimum execution time: 44_066_000 picoseconds. + Weight::from_parts(51_799_000, 3954) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -438,8 +439,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `502` // Estimated: `3967` - // Minimum execution time: 30_791_000 picoseconds. - Weight::from_parts(47_616_000, 3967) + // Minimum execution time: 48_441_000 picoseconds. + Weight::from_parts(63_189_000, 3967) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -451,8 +452,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `502` // Estimated: `3967` - // Minimum execution time: 47_084_000 picoseconds. - Weight::from_parts(49_834_000, 3967) + // Minimum execution time: 54_190_000 picoseconds. + Weight::from_parts(57_535_000, 3967) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -464,8 +465,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `502` // Estimated: `3967` - // Minimum execution time: 30_282_000 picoseconds. - Weight::from_parts(32_405_000, 3967) + // Minimum execution time: 47_053_000 picoseconds. + Weight::from_parts(58_284_000, 3967) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -483,8 +484,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `841` // Estimated: `6781` - // Minimum execution time: 55_583_000 picoseconds. - Weight::from_parts(58_894_000, 6781) + // Minimum execution time: 96_733_000 picoseconds. + Weight::from_parts(104_297_000, 6781) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -494,8 +495,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `37` // Estimated: `1522` - // Minimum execution time: 15_559_000 picoseconds. - Weight::from_parts(25_002_000, 1522) + // Minimum execution time: 23_071_000 picoseconds. + Weight::from_parts(32_634_000, 1522) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -509,8 +510,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `965` // Estimated: `4430` - // Minimum execution time: 41_314_000 picoseconds. - Weight::from_parts(51_323_000, 4430) + // Minimum execution time: 65_322_000 picoseconds. + Weight::from_parts(81_424_000, 4430) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -530,8 +531,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `733` // Estimated: `4198` - // Minimum execution time: 45_506_000 picoseconds. - Weight::from_parts(63_919_000, 4198) + // Minimum execution time: 64_562_000 picoseconds. + Weight::from_parts(73_705_000, 4198) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -571,11 +572,64 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1650` // Estimated: `7590` - // Minimum execution time: 142_843_000 picoseconds. - Weight::from_parts(233_363_000, 7590) + // Minimum execution time: 230_804_000 picoseconds. + Weight::from_parts(293_910_000, 7590) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } + /// Storage: `SmartContractModule::Contracts` (r:1 w:1) + /// Proof: `SmartContractModule::Contracts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::Nodes` (r:2 w:0) + /// Proof: `TfgridModule::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::Farms` (r:1 w:0) + /// Proof: `TfgridModule::Farms` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::NodeV3BillingOptOut` (r:2 w:0) + /// Proof: `TfgridModule::NodeV3BillingOptOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ActiveRentContractForNode` (r:2 w:0) + /// Proof: `SmartContractModule::ActiveRentContractForNode` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::NodePower` (r:1 w:0) + /// Proof: `TfgridModule::NodePower` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::DedicatedNodesExtraFee` (r:1 w:0) + /// Proof: `SmartContractModule::DedicatedNodesExtraFee` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ContractIDByNodeIDAndHash` (r:2 w:2) + /// Proof: `SmartContractModule::ContractIDByNodeIDAndHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::Twins` (r:2 w:0) + /// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::PricingPolicies` (r:1 w:0) + /// Proof: `TfgridModule::PricingPolicies` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ContractPaymentState` (r:1 w:1) + /// Proof: `SmartContractModule::ContractPaymentState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `TfgridModule::TwinBoundedAccountID` (r:1 w:0) + /// Proof: `TfgridModule::TwinBoundedAccountID` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `SmartContractModule::ContractBillingInformationByID` (r:1 w:1) + /// Proof: `SmartContractModule::ContractBillingInformationByID` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::NodeContractResources` (r:1 w:0) + /// Proof: `SmartContractModule::NodeContractResources` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TFTPriceModule::AverageTftPrice` (r:1 w:0) + /// Proof: `TFTPriceModule::AverageTftPrice` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TFTPriceModule::MinTftPrice` (r:1 w:0) + /// Proof: `TFTPriceModule::MinTftPrice` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TFTPriceModule::MaxTftPrice` (r:1 w:0) + /// Proof: `TFTPriceModule::MaxTftPrice` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ActiveNodeContracts` (r:2 w:2) + /// Proof: `SmartContractModule::ActiveNodeContracts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[1, 100]`. + fn migrate_node_contract(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3484 + n * (26 ±0)` + // Estimated: `9090 + n * (31 ±0)` + // Minimum execution time: 351_309_000 picoseconds. + Weight::from_parts(492_079_512, 9090) + // Standard Error: 54_763 + .saturating_add(Weight::from_parts(1_449_325, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(26_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_parts(0, 31).saturating_mul(n.into())) + } } // For backwards compatibility and tests @@ -616,8 +670,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `868` // Estimated: `4333` - // Minimum execution time: 94_195_000 picoseconds. - Weight::from_parts(97_943_000, 4333) + // Minimum execution time: 153_191_000 picoseconds. + Weight::from_parts(175_883_000, 4333) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -631,8 +685,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `869` // Estimated: `4334` - // Minimum execution time: 45_239_000 picoseconds. - Weight::from_parts(66_665_000, 4334) + // Minimum execution time: 76_922_000 picoseconds. + Weight::from_parts(83_803_000, 4334) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -672,8 +726,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1650` // Estimated: `7590` - // Minimum execution time: 147_718_000 picoseconds. - Weight::from_parts(245_368_000, 7590) + // Minimum execution time: 202_502_000 picoseconds. + Weight::from_parts(290_227_000, 7590) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -697,8 +751,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `340` // Estimated: `3805` - // Minimum execution time: 47_262_000 picoseconds. - Weight::from_parts(48_046_000, 3805) + // Minimum execution time: 63_406_000 picoseconds. + Weight::from_parts(81_571_000, 3805) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -724,8 +778,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `949` // Estimated: `4414` - // Minimum execution time: 98_979_000 picoseconds. - Weight::from_parts(159_048_000, 4414) + // Minimum execution time: 164_903_000 picoseconds. + Weight::from_parts(191_094_000, 4414) .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -747,8 +801,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1286` // Estimated: `4751` - // Minimum execution time: 69_958_000 picoseconds. - Weight::from_parts(98_651_000, 4751) + // Minimum execution time: 118_126_000 picoseconds. + Weight::from_parts(149_359_000, 4751) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -764,8 +818,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `765` // Estimated: `4230` - // Minimum execution time: 43_491_000 picoseconds. - Weight::from_parts(62_625_000, 4230) + // Minimum execution time: 71_420_000 picoseconds. + Weight::from_parts(90_818_000, 4230) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -797,8 +851,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `776` // Estimated: `4241` - // Minimum execution time: 75_727_000 picoseconds. - Weight::from_parts(113_461_000, 4241) + // Minimum execution time: 96_038_000 picoseconds. + Weight::from_parts(126_167_000, 4241) .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -836,8 +890,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1607` // Estimated: `7547` - // Minimum execution time: 140_505_000 picoseconds. - Weight::from_parts(219_590_000, 7547) + // Minimum execution time: 234_876_000 picoseconds. + Weight::from_parts(278_583_000, 7547) .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -849,8 +903,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `37` // Estimated: `1522` - // Minimum execution time: 20_215_000 picoseconds. - Weight::from_parts(30_997_000, 1522) + // Minimum execution time: 33_676_000 picoseconds. + Weight::from_parts(38_408_000, 1522) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -860,8 +914,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `215` // Estimated: `3680` - // Minimum execution time: 38_327_000 picoseconds. - Weight::from_parts(50_416_000, 3680) + // Minimum execution time: 40_855_000 picoseconds. + Weight::from_parts(47_531_000, 3680) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -903,8 +957,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `2077` // Estimated: `8017` - // Minimum execution time: 259_746_000 picoseconds. - Weight::from_parts(297_625_000, 8017) + // Minimum execution time: 290_205_000 picoseconds. + Weight::from_parts(318_180_000, 8017) .saturating_add(RocksDbWeight::get().reads(18_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -918,8 +972,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `395` // Estimated: `6335` - // Minimum execution time: 34_359_000 picoseconds. - Weight::from_parts(36_044_000, 6335) + // Minimum execution time: 55_623_000 picoseconds. + Weight::from_parts(71_866_000, 6335) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -931,8 +985,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `489` // Estimated: `3954` - // Minimum execution time: 38_570_000 picoseconds. - Weight::from_parts(50_746_000, 3954) + // Minimum execution time: 42_779_000 picoseconds. + Weight::from_parts(52_030_000, 3954) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -944,8 +998,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `489` // Estimated: `3954` - // Minimum execution time: 30_886_000 picoseconds. - Weight::from_parts(31_463_000, 3954) + // Minimum execution time: 44_066_000 picoseconds. + Weight::from_parts(51_799_000, 3954) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -957,8 +1011,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `502` // Estimated: `3967` - // Minimum execution time: 30_791_000 picoseconds. - Weight::from_parts(47_616_000, 3967) + // Minimum execution time: 48_441_000 picoseconds. + Weight::from_parts(63_189_000, 3967) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -970,8 +1024,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `502` // Estimated: `3967` - // Minimum execution time: 47_084_000 picoseconds. - Weight::from_parts(49_834_000, 3967) + // Minimum execution time: 54_190_000 picoseconds. + Weight::from_parts(57_535_000, 3967) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -983,8 +1037,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `502` // Estimated: `3967` - // Minimum execution time: 30_282_000 picoseconds. - Weight::from_parts(32_405_000, 3967) + // Minimum execution time: 47_053_000 picoseconds. + Weight::from_parts(58_284_000, 3967) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1002,8 +1056,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `841` // Estimated: `6781` - // Minimum execution time: 55_583_000 picoseconds. - Weight::from_parts(58_894_000, 6781) + // Minimum execution time: 96_733_000 picoseconds. + Weight::from_parts(104_297_000, 6781) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1013,8 +1067,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `37` // Estimated: `1522` - // Minimum execution time: 15_559_000 picoseconds. - Weight::from_parts(25_002_000, 1522) + // Minimum execution time: 23_071_000 picoseconds. + Weight::from_parts(32_634_000, 1522) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1028,8 +1082,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `965` // Estimated: `4430` - // Minimum execution time: 41_314_000 picoseconds. - Weight::from_parts(51_323_000, 4430) + // Minimum execution time: 65_322_000 picoseconds. + Weight::from_parts(81_424_000, 4430) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1049,8 +1103,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `733` // Estimated: `4198` - // Minimum execution time: 45_506_000 picoseconds. - Weight::from_parts(63_919_000, 4198) + // Minimum execution time: 64_562_000 picoseconds. + Weight::from_parts(73_705_000, 4198) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1090,9 +1144,62 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1650` // Estimated: `7590` - // Minimum execution time: 142_843_000 picoseconds. - Weight::from_parts(233_363_000, 7590) + // Minimum execution time: 230_804_000 picoseconds. + Weight::from_parts(293_910_000, 7590) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } + /// Storage: `SmartContractModule::Contracts` (r:1 w:1) + /// Proof: `SmartContractModule::Contracts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::Nodes` (r:2 w:0) + /// Proof: `TfgridModule::Nodes` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::Farms` (r:1 w:0) + /// Proof: `TfgridModule::Farms` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::NodeV3BillingOptOut` (r:2 w:0) + /// Proof: `TfgridModule::NodeV3BillingOptOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ActiveRentContractForNode` (r:2 w:0) + /// Proof: `SmartContractModule::ActiveRentContractForNode` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::NodePower` (r:1 w:0) + /// Proof: `TfgridModule::NodePower` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::DedicatedNodesExtraFee` (r:1 w:0) + /// Proof: `SmartContractModule::DedicatedNodesExtraFee` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ContractIDByNodeIDAndHash` (r:2 w:2) + /// Proof: `SmartContractModule::ContractIDByNodeIDAndHash` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::Twins` (r:2 w:0) + /// Proof: `TfgridModule::Twins` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TfgridModule::PricingPolicies` (r:1 w:0) + /// Proof: `TfgridModule::PricingPolicies` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ContractPaymentState` (r:1 w:1) + /// Proof: `SmartContractModule::ContractPaymentState` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `TfgridModule::TwinBoundedAccountID` (r:1 w:0) + /// Proof: `TfgridModule::TwinBoundedAccountID` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `SmartContractModule::ContractBillingInformationByID` (r:1 w:1) + /// Proof: `SmartContractModule::ContractBillingInformationByID` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::NodeContractResources` (r:1 w:0) + /// Proof: `SmartContractModule::NodeContractResources` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `TFTPriceModule::AverageTftPrice` (r:1 w:0) + /// Proof: `TFTPriceModule::AverageTftPrice` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TFTPriceModule::MinTftPrice` (r:1 w:0) + /// Proof: `TFTPriceModule::MinTftPrice` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TFTPriceModule::MaxTftPrice` (r:1 w:0) + /// Proof: `TFTPriceModule::MaxTftPrice` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SmartContractModule::ActiveNodeContracts` (r:2 w:2) + /// Proof: `SmartContractModule::ActiveNodeContracts` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[1, 100]`. + fn migrate_node_contract(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3484 + n * (26 ±0)` + // Estimated: `9090 + n * (31 ±0)` + // Minimum execution time: 351_309_000 picoseconds. + Weight::from_parts(492_079_512, 9090) + // Standard Error: 54_763 + .saturating_add(Weight::from_parts(1_449_325, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(26_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + .saturating_add(Weight::from_parts(0, 31).saturating_mul(n.into())) + } } diff --git a/substrate-node/pallets/pallet-tfgrid/src/weights.rs b/substrate-node/pallets/pallet-tfgrid/src/weights.rs index 6f79d9f5c..4d6c08af4 100644 --- a/substrate-node/pallets/pallet-tfgrid/src/weights.rs +++ b/substrate-node/pallets/pallet-tfgrid/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_tfgrid //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -83,8 +83,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_037_000 picoseconds. - Weight::from_parts(8_853_000, 0) + // Minimum execution time: 8_622_000 picoseconds. + Weight::from_parts(10_885_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `TfgridModule::TwinIdByAccountID` (r:1 w:0) @@ -101,8 +101,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3961` - // Minimum execution time: 56_543_000 picoseconds. - Weight::from_parts(64_323_000, 3961) + // Minimum execution time: 57_664_000 picoseconds. + Weight::from_parts(68_736_000, 3961) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -116,8 +116,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `507` // Estimated: `3972` - // Minimum execution time: 40_766_000 picoseconds. - Weight::from_parts(43_995_000, 3972) + // Minimum execution time: 64_695_000 picoseconds. + Weight::from_parts(90_119_000, 3972) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -131,8 +131,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `453` // Estimated: `3918` - // Minimum execution time: 29_838_000 picoseconds. - Weight::from_parts(42_549_000, 3918) + // Minimum execution time: 46_483_000 picoseconds. + Weight::from_parts(56_644_000, 3918) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -142,8 +142,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 24_842_000 picoseconds. - Weight::from_parts(37_699_000, 3877) + // Minimum execution time: 30_352_000 picoseconds. + Weight::from_parts(32_672_000, 3877) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -155,8 +155,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `569` // Estimated: `4034` - // Minimum execution time: 33_701_000 picoseconds. - Weight::from_parts(36_234_000, 4034) + // Minimum execution time: 52_653_000 picoseconds. + Weight::from_parts(64_534_000, 4034) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -168,8 +168,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `569` // Estimated: `4034` - // Minimum execution time: 33_533_000 picoseconds. - Weight::from_parts(48_821_000, 4034) + // Minimum execution time: 46_864_000 picoseconds. + Weight::from_parts(50_744_000, 4034) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -197,8 +197,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `905` // Estimated: `11795` - // Minimum execution time: 104_408_000 picoseconds. - Weight::from_parts(148_094_000, 11795) + // Minimum execution time: 141_578_000 picoseconds. + Weight::from_parts(172_488_000, 11795) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -214,8 +214,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `661` // Estimated: `4126` - // Minimum execution time: 72_750_000 picoseconds. - Weight::from_parts(82_373_000, 4126) + // Minimum execution time: 63_203_000 picoseconds. + Weight::from_parts(83_834_000, 4126) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -229,8 +229,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `956` // Estimated: `11846` - // Minimum execution time: 67_047_000 picoseconds. - Weight::from_parts(104_596_000, 11846) + // Minimum execution time: 74_754_000 picoseconds. + Weight::from_parts(124_676_000, 11846) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -246,8 +246,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `454` // Estimated: `3919` - // Minimum execution time: 34_426_000 picoseconds. - Weight::from_parts(51_946_000, 3919) + // Minimum execution time: 34_590_000 picoseconds. + Weight::from_parts(54_836_000, 3919) .saturating_add(T::DbWeight::get().reads(4_u64)) } /// Storage: `TfgridModule::Farms` (r:1 w:0) @@ -260,8 +260,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `779` // Estimated: `4244` - // Minimum execution time: 66_917_000 picoseconds. - Weight::from_parts(78_524_000, 4244) + // Minimum execution time: 74_676_000 picoseconds. + Weight::from_parts(89_718_000, 4244) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -283,8 +283,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `682` // Estimated: `4147` - // Minimum execution time: 76_379_000 picoseconds. - Weight::from_parts(80_683_000, 4147) + // Minimum execution time: 84_753_000 picoseconds. + Weight::from_parts(86_517_000, 4147) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -300,8 +300,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `235` // Estimated: `3700` - // Minimum execution time: 28_966_000 picoseconds. - Weight::from_parts(45_814_000, 3700) + // Minimum execution time: 43_809_000 picoseconds. + Weight::from_parts(55_195_000, 3700) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -313,8 +313,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `428` // Estimated: `3893` - // Minimum execution time: 41_237_000 picoseconds. - Weight::from_parts(44_071_000, 3893) + // Minimum execution time: 43_850_000 picoseconds. + Weight::from_parts(56_457_000, 3893) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -328,8 +328,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `134` // Estimated: `3599` - // Minimum execution time: 35_674_000 picoseconds. - Weight::from_parts(39_693_000, 3599) + // Minimum execution time: 39_719_000 picoseconds. + Weight::from_parts(48_301_000, 3599) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -343,8 +343,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `382` // Estimated: `3847` - // Minimum execution time: 47_788_000 picoseconds. - Weight::from_parts(54_375_000, 3847) + // Minimum execution time: 49_277_000 picoseconds. + Weight::from_parts(58_610_000, 3847) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -356,8 +356,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `134` // Estimated: `1619` - // Minimum execution time: 29_849_000 picoseconds. - Weight::from_parts(37_821_000, 1619) + // Minimum execution time: 32_763_000 picoseconds. + Weight::from_parts(43_841_000, 1619) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -369,8 +369,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `140` // Estimated: `3605` - // Minimum execution time: 21_272_000 picoseconds. - Weight::from_parts(21_848_000, 3605) + // Minimum execution time: 22_449_000 picoseconds. + Weight::from_parts(29_820_000, 3605) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -390,8 +390,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `527` // Estimated: `6467` - // Minimum execution time: 65_335_000 picoseconds. - Weight::from_parts(72_204_000, 6467) + // Minimum execution time: 68_352_000 picoseconds. + Weight::from_parts(79_888_000, 6467) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -407,8 +407,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `605` // Estimated: `4070` - // Minimum execution time: 51_071_000 picoseconds. - Weight::from_parts(77_885_000, 4070) + // Minimum execution time: 73_330_000 picoseconds. + Weight::from_parts(81_796_000, 4070) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -420,8 +420,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `403` // Estimated: `3868` - // Minimum execution time: 36_730_000 picoseconds. - Weight::from_parts(39_859_000, 3868) + // Minimum execution time: 38_526_000 picoseconds. + Weight::from_parts(43_979_000, 3868) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -445,8 +445,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `828` // Estimated: `4293` - // Minimum execution time: 59_940_000 picoseconds. - Weight::from_parts(64_555_000, 4293) + // Minimum execution time: 64_707_000 picoseconds. + Weight::from_parts(102_333_000, 4293) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -456,8 +456,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 34_415_000 picoseconds. - Weight::from_parts(42_555_000, 3877) + // Minimum execution time: 33_621_000 picoseconds. + Weight::from_parts(48_442_000, 3877) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -467,8 +467,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 25_866_000 picoseconds. - Weight::from_parts(39_327_000, 3877) + // Minimum execution time: 41_263_000 picoseconds. + Weight::from_parts(46_689_000, 3877) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -478,8 +478,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 18_400_000 picoseconds. - Weight::from_parts(20_841_000, 0) + // Minimum execution time: 18_951_000 picoseconds. + Weight::from_parts(22_346_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `TfgridModule::AllowedNodeCertifiers` (r:1 w:1) @@ -488,8 +488,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `355` // Estimated: `1840` - // Minimum execution time: 26_819_000 picoseconds. - Weight::from_parts(31_986_000, 1840) + // Minimum execution time: 29_396_000 picoseconds. + Weight::from_parts(40_416_000, 1840) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -499,8 +499,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `413` // Estimated: `1898` - // Minimum execution time: 32_667_000 picoseconds. - Weight::from_parts(36_118_000, 1898) + // Minimum execution time: 37_295_000 picoseconds. + Weight::from_parts(42_963_000, 1898) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -510,8 +510,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `294` // Estimated: `3759` - // Minimum execution time: 37_886_000 picoseconds. - Weight::from_parts(40_113_000, 3759) + // Minimum execution time: 41_049_000 picoseconds. + Weight::from_parts(49_667_000, 3759) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -525,8 +525,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `572` // Estimated: `4037` - // Minimum execution time: 44_149_000 picoseconds. - Weight::from_parts(68_043_000, 4037) + // Minimum execution time: 66_354_000 picoseconds. + Weight::from_parts(82_894_000, 4037) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -536,8 +536,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `134` // Estimated: `1619` - // Minimum execution time: 15_444_000 picoseconds. - Weight::from_parts(16_388_000, 1619) + // Minimum execution time: 26_178_000 picoseconds. + Weight::from_parts(30_372_000, 1619) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -553,8 +553,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `621` // Estimated: `4086` - // Minimum execution time: 42_023_000 picoseconds. - Weight::from_parts(61_865_000, 4086) + // Minimum execution time: 69_122_000 picoseconds. + Weight::from_parts(77_614_000, 4086) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -574,8 +574,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `792` // Estimated: `4257` - // Minimum execution time: 48_568_000 picoseconds. - Weight::from_parts(52_485_000, 4257) + // Minimum execution time: 74_176_000 picoseconds. + Weight::from_parts(87_230_000, 4257) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -587,8 +587,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `387` // Estimated: `3852` - // Minimum execution time: 23_170_000 picoseconds. - Weight::from_parts(35_217_000, 3852) + // Minimum execution time: 29_146_000 picoseconds. + Weight::from_parts(30_845_000, 3852) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -604,8 +604,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `454` // Estimated: `3919` - // Minimum execution time: 52_982_000 picoseconds. - Weight::from_parts(60_041_000, 3919) + // Minimum execution time: 41_495_000 picoseconds. + Weight::from_parts(42_582_000, 3919) .saturating_add(T::DbWeight::get().reads(4_u64)) } /// Storage: `TfgridModule::TwinIdByAccountID` (r:1 w:0) @@ -622,8 +622,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `669` // Estimated: `4134` - // Minimum execution time: 40_710_000 picoseconds. - Weight::from_parts(44_264_000, 4134) + // Minimum execution time: 66_632_000 picoseconds. + Weight::from_parts(82_801_000, 4134) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -641,8 +641,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `712` // Estimated: `4177` - // Minimum execution time: 41_384_000 picoseconds. - Weight::from_parts(42_486_000, 4177) + // Minimum execution time: 68_549_000 picoseconds. + Weight::from_parts(86_693_000, 4177) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -653,10 +653,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `158 + n * (32 ±0)` // Estimated: `1641 + n * (32 ±0)` - // Minimum execution time: 15_950_000 picoseconds. - Weight::from_parts(20_782_738, 1641) - // Standard Error: 9_817 - .saturating_add(Weight::from_parts(212_894, 0).saturating_mul(n.into())) + // Minimum execution time: 24_856_000 picoseconds. + Weight::from_parts(35_556_955, 1641) + // Standard Error: 6_339 + .saturating_add(Weight::from_parts(183_896, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(n.into())) @@ -668,10 +668,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `158 + n * (32 ±0)` // Estimated: `1643 + n * (32 ±0)` - // Minimum execution time: 19_152_000 picoseconds. - Weight::from_parts(27_969_719, 1643) - // Standard Error: 11_828 - .saturating_add(Weight::from_parts(95_628, 0).saturating_mul(n.into())) + // Minimum execution time: 19_847_000 picoseconds. + Weight::from_parts(33_853_519, 1643) + // Standard Error: 6_288 + .saturating_add(Weight::from_parts(166_315, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(n.into())) @@ -686,8 +686,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_037_000 picoseconds. - Weight::from_parts(8_853_000, 0) + // Minimum execution time: 8_622_000 picoseconds. + Weight::from_parts(10_885_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `TfgridModule::TwinIdByAccountID` (r:1 w:0) @@ -704,8 +704,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `496` // Estimated: `3961` - // Minimum execution time: 56_543_000 picoseconds. - Weight::from_parts(64_323_000, 3961) + // Minimum execution time: 57_664_000 picoseconds. + Weight::from_parts(68_736_000, 3961) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -719,8 +719,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `507` // Estimated: `3972` - // Minimum execution time: 40_766_000 picoseconds. - Weight::from_parts(43_995_000, 3972) + // Minimum execution time: 64_695_000 picoseconds. + Weight::from_parts(90_119_000, 3972) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -734,8 +734,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `453` // Estimated: `3918` - // Minimum execution time: 29_838_000 picoseconds. - Weight::from_parts(42_549_000, 3918) + // Minimum execution time: 46_483_000 picoseconds. + Weight::from_parts(56_644_000, 3918) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -745,8 +745,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 24_842_000 picoseconds. - Weight::from_parts(37_699_000, 3877) + // Minimum execution time: 30_352_000 picoseconds. + Weight::from_parts(32_672_000, 3877) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -758,8 +758,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `569` // Estimated: `4034` - // Minimum execution time: 33_701_000 picoseconds. - Weight::from_parts(36_234_000, 4034) + // Minimum execution time: 52_653_000 picoseconds. + Weight::from_parts(64_534_000, 4034) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -771,8 +771,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `569` // Estimated: `4034` - // Minimum execution time: 33_533_000 picoseconds. - Weight::from_parts(48_821_000, 4034) + // Minimum execution time: 46_864_000 picoseconds. + Weight::from_parts(50_744_000, 4034) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -800,8 +800,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `905` // Estimated: `11795` - // Minimum execution time: 104_408_000 picoseconds. - Weight::from_parts(148_094_000, 11795) + // Minimum execution time: 141_578_000 picoseconds. + Weight::from_parts(172_488_000, 11795) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -817,8 +817,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `661` // Estimated: `4126` - // Minimum execution time: 72_750_000 picoseconds. - Weight::from_parts(82_373_000, 4126) + // Minimum execution time: 63_203_000 picoseconds. + Weight::from_parts(83_834_000, 4126) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -832,8 +832,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `956` // Estimated: `11846` - // Minimum execution time: 67_047_000 picoseconds. - Weight::from_parts(104_596_000, 11846) + // Minimum execution time: 74_754_000 picoseconds. + Weight::from_parts(124_676_000, 11846) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -849,8 +849,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `454` // Estimated: `3919` - // Minimum execution time: 34_426_000 picoseconds. - Weight::from_parts(51_946_000, 3919) + // Minimum execution time: 34_590_000 picoseconds. + Weight::from_parts(54_836_000, 3919) .saturating_add(RocksDbWeight::get().reads(4_u64)) } /// Storage: `TfgridModule::Farms` (r:1 w:0) @@ -863,8 +863,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `779` // Estimated: `4244` - // Minimum execution time: 66_917_000 picoseconds. - Weight::from_parts(78_524_000, 4244) + // Minimum execution time: 74_676_000 picoseconds. + Weight::from_parts(89_718_000, 4244) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -886,8 +886,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `682` // Estimated: `4147` - // Minimum execution time: 76_379_000 picoseconds. - Weight::from_parts(80_683_000, 4147) + // Minimum execution time: 84_753_000 picoseconds. + Weight::from_parts(86_517_000, 4147) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -903,8 +903,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `235` // Estimated: `3700` - // Minimum execution time: 28_966_000 picoseconds. - Weight::from_parts(45_814_000, 3700) + // Minimum execution time: 43_809_000 picoseconds. + Weight::from_parts(55_195_000, 3700) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -916,8 +916,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `428` // Estimated: `3893` - // Minimum execution time: 41_237_000 picoseconds. - Weight::from_parts(44_071_000, 3893) + // Minimum execution time: 43_850_000 picoseconds. + Weight::from_parts(56_457_000, 3893) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -931,8 +931,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `134` // Estimated: `3599` - // Minimum execution time: 35_674_000 picoseconds. - Weight::from_parts(39_693_000, 3599) + // Minimum execution time: 39_719_000 picoseconds. + Weight::from_parts(48_301_000, 3599) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -946,8 +946,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `382` // Estimated: `3847` - // Minimum execution time: 47_788_000 picoseconds. - Weight::from_parts(54_375_000, 3847) + // Minimum execution time: 49_277_000 picoseconds. + Weight::from_parts(58_610_000, 3847) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -959,8 +959,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `134` // Estimated: `1619` - // Minimum execution time: 29_849_000 picoseconds. - Weight::from_parts(37_821_000, 1619) + // Minimum execution time: 32_763_000 picoseconds. + Weight::from_parts(43_841_000, 1619) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -972,8 +972,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `140` // Estimated: `3605` - // Minimum execution time: 21_272_000 picoseconds. - Weight::from_parts(21_848_000, 3605) + // Minimum execution time: 22_449_000 picoseconds. + Weight::from_parts(29_820_000, 3605) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -993,8 +993,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `527` // Estimated: `6467` - // Minimum execution time: 65_335_000 picoseconds. - Weight::from_parts(72_204_000, 6467) + // Minimum execution time: 68_352_000 picoseconds. + Weight::from_parts(79_888_000, 6467) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1010,8 +1010,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `605` // Estimated: `4070` - // Minimum execution time: 51_071_000 picoseconds. - Weight::from_parts(77_885_000, 4070) + // Minimum execution time: 73_330_000 picoseconds. + Weight::from_parts(81_796_000, 4070) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -1023,8 +1023,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `403` // Estimated: `3868` - // Minimum execution time: 36_730_000 picoseconds. - Weight::from_parts(39_859_000, 3868) + // Minimum execution time: 38_526_000 picoseconds. + Weight::from_parts(43_979_000, 3868) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1048,8 +1048,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `828` // Estimated: `4293` - // Minimum execution time: 59_940_000 picoseconds. - Weight::from_parts(64_555_000, 4293) + // Minimum execution time: 64_707_000 picoseconds. + Weight::from_parts(102_333_000, 4293) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -1059,8 +1059,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 34_415_000 picoseconds. - Weight::from_parts(42_555_000, 3877) + // Minimum execution time: 33_621_000 picoseconds. + Weight::from_parts(48_442_000, 3877) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1070,8 +1070,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `3877` - // Minimum execution time: 25_866_000 picoseconds. - Weight::from_parts(39_327_000, 3877) + // Minimum execution time: 41_263_000 picoseconds. + Weight::from_parts(46_689_000, 3877) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1081,8 +1081,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 18_400_000 picoseconds. - Weight::from_parts(20_841_000, 0) + // Minimum execution time: 18_951_000 picoseconds. + Weight::from_parts(22_346_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `TfgridModule::AllowedNodeCertifiers` (r:1 w:1) @@ -1091,8 +1091,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `355` // Estimated: `1840` - // Minimum execution time: 26_819_000 picoseconds. - Weight::from_parts(31_986_000, 1840) + // Minimum execution time: 29_396_000 picoseconds. + Weight::from_parts(40_416_000, 1840) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1102,8 +1102,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `413` // Estimated: `1898` - // Minimum execution time: 32_667_000 picoseconds. - Weight::from_parts(36_118_000, 1898) + // Minimum execution time: 37_295_000 picoseconds. + Weight::from_parts(42_963_000, 1898) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1113,8 +1113,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `294` // Estimated: `3759` - // Minimum execution time: 37_886_000 picoseconds. - Weight::from_parts(40_113_000, 3759) + // Minimum execution time: 41_049_000 picoseconds. + Weight::from_parts(49_667_000, 3759) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1128,8 +1128,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `572` // Estimated: `4037` - // Minimum execution time: 44_149_000 picoseconds. - Weight::from_parts(68_043_000, 4037) + // Minimum execution time: 66_354_000 picoseconds. + Weight::from_parts(82_894_000, 4037) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1139,8 +1139,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `134` // Estimated: `1619` - // Minimum execution time: 15_444_000 picoseconds. - Weight::from_parts(16_388_000, 1619) + // Minimum execution time: 26_178_000 picoseconds. + Weight::from_parts(30_372_000, 1619) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1156,8 +1156,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `621` // Estimated: `4086` - // Minimum execution time: 42_023_000 picoseconds. - Weight::from_parts(61_865_000, 4086) + // Minimum execution time: 69_122_000 picoseconds. + Weight::from_parts(77_614_000, 4086) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1177,8 +1177,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `792` // Estimated: `4257` - // Minimum execution time: 48_568_000 picoseconds. - Weight::from_parts(52_485_000, 4257) + // Minimum execution time: 74_176_000 picoseconds. + Weight::from_parts(87_230_000, 4257) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1190,8 +1190,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `387` // Estimated: `3852` - // Minimum execution time: 23_170_000 picoseconds. - Weight::from_parts(35_217_000, 3852) + // Minimum execution time: 29_146_000 picoseconds. + Weight::from_parts(30_845_000, 3852) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1207,8 +1207,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `454` // Estimated: `3919` - // Minimum execution time: 52_982_000 picoseconds. - Weight::from_parts(60_041_000, 3919) + // Minimum execution time: 41_495_000 picoseconds. + Weight::from_parts(42_582_000, 3919) .saturating_add(RocksDbWeight::get().reads(4_u64)) } /// Storage: `TfgridModule::TwinIdByAccountID` (r:1 w:0) @@ -1225,8 +1225,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `669` // Estimated: `4134` - // Minimum execution time: 40_710_000 picoseconds. - Weight::from_parts(44_264_000, 4134) + // Minimum execution time: 66_632_000 picoseconds. + Weight::from_parts(82_801_000, 4134) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1244,8 +1244,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `712` // Estimated: `4177` - // Minimum execution time: 41_384_000 picoseconds. - Weight::from_parts(42_486_000, 4177) + // Minimum execution time: 68_549_000 picoseconds. + Weight::from_parts(86_693_000, 4177) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1256,10 +1256,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `158 + n * (32 ±0)` // Estimated: `1641 + n * (32 ±0)` - // Minimum execution time: 15_950_000 picoseconds. - Weight::from_parts(20_782_738, 1641) - // Standard Error: 9_817 - .saturating_add(Weight::from_parts(212_894, 0).saturating_mul(n.into())) + // Minimum execution time: 24_856_000 picoseconds. + Weight::from_parts(35_556_955, 1641) + // Standard Error: 6_339 + .saturating_add(Weight::from_parts(183_896, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(n.into())) @@ -1271,10 +1271,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `158 + n * (32 ±0)` // Estimated: `1643 + n * (32 ±0)` - // Minimum execution time: 19_152_000 picoseconds. - Weight::from_parts(27_969_719, 1643) - // Standard Error: 11_828 - .saturating_add(Weight::from_parts(95_628, 0).saturating_mul(n.into())) + // Minimum execution time: 19_847_000 picoseconds. + Weight::from_parts(33_853_519, 1643) + // Standard Error: 6_288 + .saturating_add(Weight::from_parts(166_315, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(n.into())) diff --git a/substrate-node/pallets/pallet-tft-bridge/src/weights.rs b/substrate-node/pallets/pallet-tft-bridge/src/weights.rs index 42e980175..634460968 100644 --- a/substrate-node/pallets/pallet-tft-bridge/src/weights.rs +++ b/substrate-node/pallets/pallet-tft-bridge/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_tft_bridge //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -55,8 +55,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `256` // Estimated: `1741` - // Minimum execution time: 13_362_000 picoseconds. - Weight::from_parts(20_041_000, 1741) + // Minimum execution time: 20_283_000 picoseconds. + Weight::from_parts(23_172_000, 1741) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -66,8 +66,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `289` // Estimated: `1774` - // Minimum execution time: 12_793_000 picoseconds. - Weight::from_parts(19_337_000, 1774) + // Minimum execution time: 20_723_000 picoseconds. + Weight::from_parts(22_034_000, 1774) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -77,8 +77,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_354_000 picoseconds. - Weight::from_parts(8_247_000, 0) + // Minimum execution time: 9_114_000 picoseconds. + Weight::from_parts(13_691_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::WithdrawFee` (r:0 w:1) @@ -87,8 +87,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_267_000 picoseconds. - Weight::from_parts(8_445_000, 0) + // Minimum execution time: 7_833_000 picoseconds. + Weight::from_parts(10_677_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::DepositFee` (r:0 w:1) @@ -97,8 +97,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_535_000 picoseconds. - Weight::from_parts(4_880_000, 0) + // Minimum execution time: 8_303_000 picoseconds. + Weight::from_parts(10_761_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::WithdrawFee` (r:1 w:0) @@ -115,8 +115,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `253` // Estimated: `3593` - // Minimum execution time: 84_028_000 picoseconds. - Weight::from_parts(146_829_000, 3593) + // Minimum execution time: 143_749_000 picoseconds. + Weight::from_parts(160_539_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -136,8 +136,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `499` // Estimated: `3964` - // Minimum execution time: 114_895_000 picoseconds. - Weight::from_parts(170_544_000, 3964) + // Minimum execution time: 206_260_000 picoseconds. + Weight::from_parts(245_016_000, 3964) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -151,8 +151,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `631` // Estimated: `4096` - // Minimum execution time: 47_733_000 picoseconds. - Weight::from_parts(75_616_000, 4096) + // Minimum execution time: 74_904_000 picoseconds. + Weight::from_parts(90_304_000, 4096) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -166,22 +166,24 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `571` // Estimated: `4036` - // Minimum execution time: 35_470_000 picoseconds. - Weight::from_parts(38_994_000, 4036) + // Minimum execution time: 51_667_000 picoseconds. + Weight::from_parts(70_089_000, 4036) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `TFTBridgeModule::Validators` (r:1 w:0) /// Proof: `TFTBridgeModule::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TFTBridgeModule::ExecutedRefundTransactions` (r:1 w:0) + /// Proof: `TFTBridgeModule::ExecutedRefundTransactions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `TFTBridgeModule::RefundTransactions` (r:1 w:1) /// Proof: `TFTBridgeModule::RefundTransactions` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_refund_transaction_or_add_sig() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3850` - // Minimum execution time: 44_425_000 picoseconds. - Weight::from_parts(68_629_000, 3850) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Minimum execution time: 54_449_000 picoseconds. + Weight::from_parts(81_549_000, 3850) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::Validators` (r:1 w:0) @@ -194,8 +196,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `560` // Estimated: `4025` - // Minimum execution time: 35_428_000 picoseconds. - Weight::from_parts(54_951_000, 4025) + // Minimum execution time: 54_326_000 picoseconds. + Weight::from_parts(63_873_000, 4025) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -209,8 +211,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `256` // Estimated: `1741` - // Minimum execution time: 13_362_000 picoseconds. - Weight::from_parts(20_041_000, 1741) + // Minimum execution time: 20_283_000 picoseconds. + Weight::from_parts(23_172_000, 1741) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -220,8 +222,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `289` // Estimated: `1774` - // Minimum execution time: 12_793_000 picoseconds. - Weight::from_parts(19_337_000, 1774) + // Minimum execution time: 20_723_000 picoseconds. + Weight::from_parts(22_034_000, 1774) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -231,8 +233,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_354_000 picoseconds. - Weight::from_parts(8_247_000, 0) + // Minimum execution time: 9_114_000 picoseconds. + Weight::from_parts(13_691_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::WithdrawFee` (r:0 w:1) @@ -241,8 +243,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_267_000 picoseconds. - Weight::from_parts(8_445_000, 0) + // Minimum execution time: 7_833_000 picoseconds. + Weight::from_parts(10_677_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::DepositFee` (r:0 w:1) @@ -251,8 +253,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_535_000 picoseconds. - Weight::from_parts(4_880_000, 0) + // Minimum execution time: 8_303_000 picoseconds. + Weight::from_parts(10_761_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::WithdrawFee` (r:1 w:0) @@ -269,8 +271,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `253` // Estimated: `3593` - // Minimum execution time: 84_028_000 picoseconds. - Weight::from_parts(146_829_000, 3593) + // Minimum execution time: 143_749_000 picoseconds. + Weight::from_parts(160_539_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -290,8 +292,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `499` // Estimated: `3964` - // Minimum execution time: 114_895_000 picoseconds. - Weight::from_parts(170_544_000, 3964) + // Minimum execution time: 206_260_000 picoseconds. + Weight::from_parts(245_016_000, 3964) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -305,8 +307,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `631` // Estimated: `4096` - // Minimum execution time: 47_733_000 picoseconds. - Weight::from_parts(75_616_000, 4096) + // Minimum execution time: 74_904_000 picoseconds. + Weight::from_parts(90_304_000, 4096) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -320,22 +322,24 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `571` // Estimated: `4036` - // Minimum execution time: 35_470_000 picoseconds. - Weight::from_parts(38_994_000, 4036) + // Minimum execution time: 51_667_000 picoseconds. + Weight::from_parts(70_089_000, 4036) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `TFTBridgeModule::Validators` (r:1 w:0) /// Proof: `TFTBridgeModule::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `TFTBridgeModule::ExecutedRefundTransactions` (r:1 w:0) + /// Proof: `TFTBridgeModule::ExecutedRefundTransactions` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `TFTBridgeModule::RefundTransactions` (r:1 w:1) /// Proof: `TFTBridgeModule::RefundTransactions` (`max_values`: None, `max_size`: None, mode: `Measured`) fn create_refund_transaction_or_add_sig() -> Weight { // Proof Size summary in bytes: // Measured: `385` // Estimated: `3850` - // Minimum execution time: 44_425_000 picoseconds. - Weight::from_parts(68_629_000, 3850) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Minimum execution time: 54_449_000 picoseconds. + Weight::from_parts(81_549_000, 3850) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `TFTBridgeModule::Validators` (r:1 w:0) @@ -348,8 +352,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `560` // Estimated: `4025` - // Minimum execution time: 35_428_000 picoseconds. - Weight::from_parts(54_951_000, 4025) + // Minimum execution time: 54_326_000 picoseconds. + Weight::from_parts(63_873_000, 4025) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/substrate-node/pallets/pallet-tft-price/src/weights.rs b/substrate-node/pallets/pallet-tft-price/src/weights.rs index 18b3ebe7f..2c79f5987 100644 --- a/substrate-node/pallets/pallet-tft-price/src/weights.rs +++ b/substrate-node/pallets/pallet-tft-price/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_tft_price //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -61,8 +61,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `235` // Estimated: `6175` - // Minimum execution time: 83_896_000 picoseconds. - Weight::from_parts(98_515_000, 6175) + // Minimum execution time: 91_464_000 picoseconds. + Weight::from_parts(110_359_000, 6175) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `93` // Estimated: `1578` - // Minimum execution time: 10_248_000 picoseconds. - Weight::from_parts(14_157_000, 1578) + // Minimum execution time: 14_440_000 picoseconds. + Weight::from_parts(14_903_000, 1578) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -87,8 +87,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `93` // Estimated: `1578` - // Minimum execution time: 9_079_000 picoseconds. - Weight::from_parts(9_349_000, 1578) + // Minimum execution time: 14_827_000 picoseconds. + Weight::from_parts(16_694_000, 1578) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -116,8 +116,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `235` // Estimated: `6175` - // Minimum execution time: 83_896_000 picoseconds. - Weight::from_parts(98_515_000, 6175) + // Minimum execution time: 91_464_000 picoseconds. + Weight::from_parts(110_359_000, 6175) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -129,8 +129,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `93` // Estimated: `1578` - // Minimum execution time: 10_248_000 picoseconds. - Weight::from_parts(14_157_000, 1578) + // Minimum execution time: 14_440_000 picoseconds. + Weight::from_parts(14_903_000, 1578) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -142,8 +142,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `93` // Estimated: `1578` - // Minimum execution time: 9_079_000 picoseconds. - Weight::from_parts(9_349_000, 1578) + // Minimum execution time: 14_827_000 picoseconds. + Weight::from_parts(16_694_000, 1578) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate-node/pallets/pallet-validator/src/weights.rs b/substrate-node/pallets/pallet-validator/src/weights.rs index affed8c5b..83ebdd680 100644 --- a/substrate-node/pallets/pallet-validator/src/weights.rs +++ b/substrate-node/pallets/pallet-validator/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for pallet_validator //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -50,8 +50,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 30_422_000 picoseconds. - Weight::from_parts(33_328_000, 3507) + // Minimum execution time: 30_923_000 picoseconds. + Weight::from_parts(35_120_000, 3507) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -65,8 +65,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `365` // Estimated: `3830` - // Minimum execution time: 73_955_000 picoseconds. - Weight::from_parts(80_040_000, 3830) + // Minimum execution time: 77_325_000 picoseconds. + Weight::from_parts(87_716_000, 3830) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -80,8 +80,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `431` // Estimated: `3896` - // Minimum execution time: 67_710_000 picoseconds. - Weight::from_parts(110_017_000, 3896) + // Minimum execution time: 111_901_000 picoseconds. + Weight::from_parts(135_294_000, 3896) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -91,8 +91,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 17_537_000 picoseconds. - Weight::from_parts(27_341_000, 3507) + // Minimum execution time: 24_694_000 picoseconds. + Weight::from_parts(27_765_000, 3507) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -110,8 +110,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `494` // Estimated: `4687` - // Minimum execution time: 49_255_000 picoseconds. - Weight::from_parts(50_552_000, 4687) + // Minimum execution time: 73_970_000 picoseconds. + Weight::from_parts(84_533_000, 4687) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -131,8 +131,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `558` // Estimated: `4687` - // Minimum execution time: 65_252_000 picoseconds. - Weight::from_parts(69_396_000, 4687) + // Minimum execution time: 66_678_000 picoseconds. + Weight::from_parts(80_158_000, 4687) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -146,8 +146,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 30_422_000 picoseconds. - Weight::from_parts(33_328_000, 3507) + // Minimum execution time: 30_923_000 picoseconds. + Weight::from_parts(35_120_000, 3507) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -161,8 +161,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `365` // Estimated: `3830` - // Minimum execution time: 73_955_000 picoseconds. - Weight::from_parts(80_040_000, 3830) + // Minimum execution time: 77_325_000 picoseconds. + Weight::from_parts(87_716_000, 3830) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -176,8 +176,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `431` // Estimated: `3896` - // Minimum execution time: 67_710_000 picoseconds. - Weight::from_parts(110_017_000, 3896) + // Minimum execution time: 111_901_000 picoseconds. + Weight::from_parts(135_294_000, 3896) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -187,8 +187,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 17_537_000 picoseconds. - Weight::from_parts(27_341_000, 3507) + // Minimum execution time: 24_694_000 picoseconds. + Weight::from_parts(27_765_000, 3507) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -206,8 +206,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `494` // Estimated: `4687` - // Minimum execution time: 49_255_000 picoseconds. - Weight::from_parts(50_552_000, 4687) + // Minimum execution time: 73_970_000 picoseconds. + Weight::from_parts(84_533_000, 4687) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -227,8 +227,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `558` // Estimated: `4687` - // Minimum execution time: 65_252_000 picoseconds. - Weight::from_parts(69_396_000, 4687) + // Minimum execution time: 66_678_000 picoseconds. + Weight::from_parts(80_158_000, 4687) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } diff --git a/substrate-node/pallets/substrate-validator-set/src/weights.rs b/substrate-node/pallets/substrate-validator-set/src/weights.rs index ccdc7b271..28d727689 100644 --- a/substrate-node/pallets/substrate-validator-set/src/weights.rs +++ b/substrate-node/pallets/substrate-validator-set/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for substrate_validator_set //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2026-03-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-08-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `cc3955fca132`, CPU: `DO-Regular` +//! HOSTNAME: `0cd0ec2d76e9`, CPU: `DO-Regular` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -49,8 +49,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `139` // Estimated: `1624` - // Minimum execution time: 41_673_000 picoseconds. - Weight::from_parts(46_756_000, 1624) + // Minimum execution time: 50_354_000 picoseconds. + Weight::from_parts(54_149_000, 1624) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -62,8 +62,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `205` // Estimated: `1690` - // Minimum execution time: 32_364_000 picoseconds. - Weight::from_parts(33_414_000, 1690) + // Minimum execution time: 40_982_000 picoseconds. + Weight::from_parts(43_128_000, 1690) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -75,8 +75,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `172` // Estimated: `1657` - // Minimum execution time: 34_943_000 picoseconds. - Weight::from_parts(39_459_000, 1657) + // Minimum execution time: 41_280_000 picoseconds. + Weight::from_parts(45_255_000, 1657) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -92,8 +92,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `139` // Estimated: `1624` - // Minimum execution time: 41_673_000 picoseconds. - Weight::from_parts(46_756_000, 1624) + // Minimum execution time: 50_354_000 picoseconds. + Weight::from_parts(54_149_000, 1624) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -105,8 +105,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `205` // Estimated: `1690` - // Minimum execution time: 32_364_000 picoseconds. - Weight::from_parts(33_414_000, 1690) + // Minimum execution time: 40_982_000 picoseconds. + Weight::from_parts(43_128_000, 1690) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -118,8 +118,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `172` // Estimated: `1657` - // Minimum execution time: 34_943_000 picoseconds. - Weight::from_parts(39_459_000, 1657) + // Minimum execution time: 41_280_000 picoseconds. + Weight::from_parts(45_255_000, 1657) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/substrate-node/tests/SubstrateNetwork.py b/substrate-node/tests/SubstrateNetwork.py index d6b8794bc..bb50e0236 100644 --- a/substrate-node/tests/SubstrateNetwork.py +++ b/substrate-node/tests/SubstrateNetwork.py @@ -13,7 +13,10 @@ SUBSTRATE_NODE_DIR = dirname(os.getcwd()) -TMP_DIR = "\tmp" +# NOTE: this was previously "\tmp", which Python reads as TAB + "mp" -- so every +# local run created a directory named mp inside tests/ instead of using the +# system temp dir, leaving untracked chain data in the working tree. +TMP_DIR = tempfile.gettempdir() TFCHAIN_EXE = join(SUBSTRATE_NODE_DIR, "target", "release", "tfchain") RE_NODE_STARTED = re.compile("Running JSON-RPC server") diff --git a/substrate-node/tests/TfChainClient.py b/substrate-node/tests/TfChainClient.py index 1b205ae2c..040fad0bb 100644 --- a/substrate-node/tests/TfChainClient.py +++ b/substrate-node/tests/TfChainClient.py @@ -428,6 +428,35 @@ def get_node(self, id: int = 1, port: int = DEFAULT_PORT): q = substrate.query("TfgridModule", "Nodes", [id]) return q.value + def change_power_target(self, node_id: int = 1, power_target: str = "Down", port: int = DEFAULT_PORT, + who: str = DEFAULT_SIGNER): + """Ask a node to power up/down. Only the farm owner twin may call this, and + powering down is refused while the node still holds active contracts.""" + substrate = self._connect_to_server(f"ws://127.0.0.1:{port}") + + call = substrate.compose_call("TfgridModule", "change_power_target", { + "node_id": node_id, + "power_target": power_target + }) + expected_events = [{ + "module_id": "TfgridModule", + "event_id": "PowerTargetChanged" + }] + self._sign_extrinsic_submit_check_response( + substrate, call, who, expected_events=expected_events) + + def get_contract(self, id: int = 1, port: int = DEFAULT_PORT): + substrate = self._connect_to_server(f"ws://127.0.0.1:{port}") + + q = substrate.query("SmartContractModule", "Contracts", [id]) + return q.value + + def get_active_node_contracts(self, node_id: int = 1, port: int = DEFAULT_PORT): + substrate = self._connect_to_server(f"ws://127.0.0.1:{port}") + + q = substrate.query("SmartContractModule", "ActiveNodeContracts", [node_id]) + return q.value + def create_node_contract(self, node_id: int = 1, deployment_data: bytes = randbytes(32), deployment_hash: bytes = randbytes(32), public_ips: int = 0, solution_provider_id: int | None = None, port: int = DEFAULT_PORT, who: str = DEFAULT_SIGNER): @@ -465,6 +494,29 @@ def update_node_contract(self, contract_id: int = 1, deployment_data: bytes = ra self._sign_extrinsic_submit_check_response( substrate, call, who, expected_events=expected_events) + def migrate_node_contract(self, contract_id: int = 1, node_id: int = 2, deployment_hash: bytes | None = None, + port: int = DEFAULT_PORT, who: str = "Council"): + """Move a node contract to another node in the same farm. + + Takes a configurable origin (root or council), so `who` defaults to + Council. `deployment_hash=None` keeps the contract's current hash. + No dedicated event is emitted: ContractUpdated already carries the new + node id and hash. + """ + substrate = self._connect_to_server(f"ws://127.0.0.1:{port}") + + call = substrate.compose_call("SmartContractModule", "migrate_node_contract", { + "contract_id": contract_id, + "node_id": node_id, + "deployment_hash": deployment_hash + }) + expected_events = [{ + "module_id": "SmartContractModule", + "event_id": "ContractUpdated" + }] + self._sign_extrinsic_submit_check_response( + substrate, call, who, expected_events=expected_events) + def create_rent_contract(self, node_id: int = 1, solution_provider_id: int | None = None, port: int = DEFAULT_PORT, who: str = DEFAULT_SIGNER): substrate = self._connect_to_server(f"ws://127.0.0.1:{port}") diff --git a/substrate-node/tests/integration_tests.robot b/substrate-node/tests/integration_tests.robot index 5ceb4c7ca..ad4aca341 100644 --- a/substrate-node/tests/integration_tests.robot +++ b/substrate-node/tests/integration_tests.robot @@ -309,6 +309,48 @@ Test Create Update Cancel Node Contract: Success Tear Down Multi Node Network +Test Migrate Node Contract: Success + [Documentation] Move a node contract to a second node in the same farm without cancelling it + Setup Multi Node Network log_name=test_migrate_node_contract amt=${2} + + Setup Predefined Account who=Alice + Setup Predefined Account who=Bob + Setup Predefined Account who=Charlie + + Create Farm name=alice_farm + + ${interface_ips} = Create List 10.2.3.3 + ${interface_1} = Create Interface name=zos mac=00:00:5e:00:53:af ips=${interface_ips} + ${interfaces} = Create List ${interface_1} + Create Node farm_id=${1} hru=${1024} sru=${512} cru=${8} mru=${16} longitude=2.17403 latitude=41.40338 country=Belgium city=Ghent interfaces=${interfaces} + + # a second node on the SAME farm needs its own twin + Create Node farm_id=${1} hru=${1024} sru=${512} cru=${8} mru=${16} longitude=2.17403 latitude=41.40338 country=Belgium city=Ghent interfaces=${interfaces} who=Charlie + + # Bob owns the contract; the migration is a council action + Create Node Contract node_id=${1} who=Bob port=9945 + + ${contract} = Get Contract ${1} + Should Be Equal ${contract}[contract_type][NodeContract][node_id] ${1} msg=The contract should start on node 1 + + Migrate Node Contract contract_id=${1} node_id=${2} who=Council + + ${contract} = Get Contract ${1} + Should Be Equal ${contract}[contract_type][NodeContract][node_id] ${2} msg=The contract should have moved to node 2 + + ${on_node_1} = Get Active Node Contracts ${1} + Should Be Empty ${on_node_1} msg=Node 1 should have no active contracts left + ${on_node_2} = Get Active Node Contracts ${2} + Should Contain ${on_node_2} ${1} msg=Node 2 should now hold the contract + + # the point of the whole feature: the drained node can now be powered down, + # which fails with NodeHasActiveContracts while it still holds a contract + Change Power Target node_id=${1} power_target=Down who=Alice + + Cancel Node Contract contract_id=${1} who=Bob port=${9945} + + Tear Down Multi Node Network + Test Create Node Contract: Failure Not Enough Public Ips [Documentation] Testing creating a node contract and requesting too much pub ips Setup Multi Node Network log_name=test_create_node_contract_failure_notenoughpubips