Skip to content

Commit

Permalink
feat(pallet): allow farmer to set extra fee on its nodes (#726)
Browse files Browse the repository at this point in the history
  • Loading branch information
renauter authored Jun 13, 2023
1 parent 8db9214 commit d06c022
Show file tree
Hide file tree
Showing 21 changed files with 991 additions and 147 deletions.
7 changes: 7 additions & 0 deletions clients/tfchain-client-go/contract_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,10 @@ type BillingFrequencyChanged struct {
Frequency types.U64 `json:"frequency"`
Topics []types.Hash
}

type NodeExtraFeeSet struct {
Phase types.Phase
NodeID types.U32 `json:"node_id"`
ExtraFee types.U64 `json:"extra_fee"`
Topics []types.Hash
}
3 changes: 3 additions & 0 deletions clients/tfchain-client-go/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ func TestCreateBatch(t *testing.T) {
// third contract should not be created
_, err = cl.GetContractIDByNameRegistration(contracts[2].Name)
require.Error(t, err)

err = cl.BatchCancelContract(identity, c)
require.NoError(t, err)
})

}
1 change: 1 addition & 0 deletions clients/tfchain-client-go/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ type EventRecords struct {
SmartContractModule_ServiceContractCanceled []ServiceContractCanceled //nolint:stylecheck,golint
SmartContractModule_ServiceContractBilled []ServiceContractBilled //nolint:stylecheck,golint
SmartContractModule_BillingFrequencyChanged []BillingFrequencyChanged //nolint:stylecheck,golint
SmartContractModule_NodeExtraFeeSet []NodeExtraFeeSet //nolint:stylecheck,golint

// farm events
TfgridModule_FarmStored []FarmStored //nolint:stylecheck,golint
Expand Down
55 changes: 55 additions & 0 deletions clients/tfchain-client-go/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ func (s *Substrate) SetNodePowerState(identity Identity, up bool) (hash types.Ha
return callResponse.Hash, nil
}

// GetPowerTarget returns the power target for a node
func (s *Substrate) GetPowerTarget(nodeID uint32) (power NodePower, err error) {
cl, meta, err := s.GetClient()
if err != nil {
Expand Down Expand Up @@ -750,6 +751,60 @@ func (s *Substrate) GetPowerTarget(nodeID uint32) (power NodePower, err error) {
return power, nil
}

// SetDedicatedNodePrice sets an extra price on a node expressed in mUSD
// This price will be distributed back to the farmer if the node is rented
// Setting this price also makes the node only available to rent as dedicated
func (s *Substrate) SetDedicatedNodePrice(identity Identity, nodeId uint32, price uint64) (hash types.Hash, err error) {
cl, meta, err := s.GetClient()
if err != nil {
return hash, err
}

c, err := types.NewCall(meta, "SmartContractModule.set_dedicated_node_extra_fee", nodeId, price)

if err != nil {
return hash, errors.Wrap(err, "failed to create call")
}

callResponse, err := s.Call(cl, meta, identity, c)
if err != nil {
return hash, errors.Wrap(err, "failed to update node extra price")
}

return callResponse.Hash, nil
}

// GetDedicatedeNodePrice returns the price of a dedicated node
func (s *Substrate) GetDedicatedNodePrice(nodeID uint32) (uint64, error) {
cl, meta, err := s.GetClient()
if err != nil {
return 0, err
}

bytes, err := Encode(nodeID)
if err != nil {
return 0, errors.Wrap(err, "substrate: encoding error building query arguments")
}

key, err := types.CreateStorageKey(meta, "SmartContractModule", "DedicatedNodesExtraFee", bytes)
if err != nil {
return 0, errors.Wrap(err, "failed to create substrate query key")
}

var price types.U64

ok, err := cl.RPC.State.GetStorageLatest(key, &price)
if err != nil {
return 0, err
}

if !ok {
return 0, nil
}

return uint64(price), nil
}

func (s *Substrate) SetNodeGpuStatus(identity Identity, state bool) (hash types.Hash, err error) {
cl, meta, err := s.GetClient()
if err != nil {
Expand Down
23 changes: 23 additions & 0 deletions clients/tfchain-client-go/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ func TestGetNodes(t *testing.T) {
require.Greater(t, len(nodes), 0)
}

func TestSetDedicatedNodePrice(t *testing.T) {
var nodeID uint32

cl := startLocalConnection(t)
defer cl.Close()

identity, err := NewIdentityFromSr25519Phrase(BobMnemonics)
require.NoError(t, err)

farmID, twinID := assertCreateFarm(t, cl)

nodeID = assertCreateNode(t, cl, farmID, twinID, identity)

price := 100000000
_, err = cl.SetDedicatedNodePrice(identity, nodeID, uint64(price))
require.NoError(t, err)

priceSet, err := cl.GetDedicatedNodePrice(nodeID)
require.NoError(t, err)

require.Equal(t, uint64(price), priceSet)
}

func TestUptimeReport(t *testing.T) {
cl := startLocalConnection(t)
defer cl.Close()
Expand Down
5 changes: 3 additions & 2 deletions clients/tfchain-client-go/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ var (
// https://github.com/threefoldtech/tfchain/blob/development/substrate-node/runtime/src/lib.rs#L701
var moduleErrors = [][]string{
nil, // System
nil, // RandomnessCollectiveFlip
nil, // Timestamp
nil, // Balances
nil, // ValidatorSet
Expand All @@ -44,6 +43,7 @@ var moduleErrors = [][]string{
nil, // Validator
nil, // Dao
nil, // Utility
nil, // Historical
}

// https://github.com/threefoldtech/tfchain_pallets/blob/bc9c5d322463aaf735212e428da4ea32b117dc24/pallet-smart-contract/src/lib.rs#L58
Expand Down Expand Up @@ -72,7 +72,7 @@ var smartContractModuleErrors = []string{
"MethodIsDeprecated",
"NodeHasActiveContracts",
"NodeHasRentContract",
"NodeIsNotDedicated",
"FarmIsNotDedicated",
"NodeNotAvailableToDeploy",
"CannotUpdateContractInGraceState",
"NumOverflow",
Expand All @@ -99,6 +99,7 @@ var smartContractModuleErrors = []string{
"IsNotAnAuthority",
"WrongAuthority",
"UnauthorizedToChangeSolutionProviderId",
"UnauthorizedToSetExtraFee",
}

// https://github.com/threefoldtech/tfchain/blob/development/substrate-node/pallets/pallet-smart-contract/src/lib.rs#L321
Expand Down
14 changes: 13 additions & 1 deletion clients/tfchain-client-js/lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const {
} = require('./twin')
const {
createFarm, getFarm, deleteFarm,
listFarms, addFarmIP, deleteFarmIP, setNodePower
listFarms, addFarmIP, deleteFarmIP, setNodePower, setDedicatedNodePrice
} = require('./farms')
const {
createNode, updateNode, getNode,
Expand Down Expand Up @@ -352,6 +352,18 @@ class Client {
async getServiceContract(serviceContractId) {
return getServiceContract(this, serviceContractId)
}

// setDedicatedNodePrice sets an extra price on a node expressed in mUSD
// This price will be distributed back to the farmer if the node is rented
// Setting this price also makes the node only available to rent as dedicated
async setDedicatedNodePrice(nodeId, price, callback) {
return setDedicatedNodePrice(this, nodeId, price, callback)
}

// getDedicatedNodePrice returns the extra price on a node expressed in mUSD
async getDedicatedNodePrice(nodeId) {
return getDedicatedNodePrice(this, nodeId)
}
}
async function getPolkaAPI(url) {
if (!url || url === '') {
Expand Down
16 changes: 15 additions & 1 deletion clients/tfchain-client-js/lib/farms.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ async function setNodePower (self, nodeID, power, callback) {
.signAndSend(self.key, { nonce }, callback)
}

async function setDedicatedNodePrice(self, nodeID, price, callback) {
const nonce = await self.api.rpc.system.accountNextIndex(self.address)
return self.api.tx.smartContractModule
.setDedicatedNodeExtraFee(nodeID, price)
.signAndSend(self.key, { nonce }, callback)
}

async function getDedicatedNodePrice(self, nodeID) {
const price = await self.api.query.smartContractModule.dedicatedNodesExtraFee(nodeID)
return price.toNumber()
}

async function validateFarm (self, name) {
if (name === '') {
throw Error('farm should have a name')
Expand All @@ -132,5 +144,7 @@ module.exports = {
listFarms,
addFarmIP,
deleteFarmIP,
setNodePower
setNodePower,
setDedicatedNodePrice,
getDedicatedNodePrice
}
2 changes: 1 addition & 1 deletion clients/tfchain-client-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tfgrid-api-client",
"version": "1.27.0",
"version": "1.28.0",
"description": "API client for the TF Grid",
"main": "index.js",
"scripts": {
Expand Down
20 changes: 20 additions & 0 deletions docs/architecture/0011-dedicated-nodes-extra-fee.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 11. Dedicated nodes extra fee

Date: 2023-06-06

## Status

Accepted

## Context

For supporting GPU on the grid (https://github.com/threefoldtech/home/issues/1392) we are adding an option for a farmer to set an extra fee for his nodes that have a GPU or other special feautures enabled. This fee will be added to the price of the capacity price. The full fee will be paid to the farmer.

By setting a price on a node, this node becomes dedicated and can only be rented by creating a `RentContract` on that node. This means that when a farmer sets an extra price on a node, this node cannot be shared anymore.

## Decision

Added a new storage map `DedicatedNodesExtraFee` on `pallet-smart-contract` which can be set by the owner of that node (the farmer) by calling `set_dedicated_node_extra_fee`.
The input of this call is `node_id` and `fee` which is expressed in mUSD (USD * 1000). This fee is expressed in a fee per month.

We also changed the `ContractLock` storage to include a new field `extra_amount_locked` which is the total fee that is locked by this contract.
Loading

0 comments on commit d06c022

Please sign in to comment.