Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pallet): allow farmer to set extra fee on its nodes #726

Merged
merged 27 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
95f8880
feat: dedicated node extra fee
renauter Jun 7, 2023
bb6d2f0
chore: merge development
DylanVerstraete Jun 7, 2023
75bbda4
fix: ci
DylanVerstraete Jun 7, 2023
99359cc
feat: rework check before creating node contract
DylanVerstraete Jun 7, 2023
03e3e94
chore: rework syntax
DylanVerstraete Jun 7, 2023
9ca99fa
chore: revert farmer_share concept
renauter Jun 7, 2023
8e6295d
feat: dedicated node extra fee billing
renauter Jun 7, 2023
f37d57a
test: set dedicated node extra fee
renauter Jun 7, 2023
5d2483b
feat: add migration for locks
DylanVerstraete Jun 8, 2023
29d230a
chore: merge development
DylanVerstraete Jun 8, 2023
4abcd0b
chore: complete adr
DylanVerstraete Jun 8, 2023
5d4fe9a
fix: some general fixes and refactor
DylanVerstraete Jun 8, 2023
92c74d7
fix: rework test
DylanVerstraete Jun 9, 2023
0f00251
fix: go client bill event
DylanVerstraete Jun 9, 2023
c02830a
feat: add js client methods
DylanVerstraete Jun 9, 2023
4cadce0
feat: add go client methods
DylanVerstraete Jun 9, 2023
06950b2
fix: js client
DylanVerstraete Jun 9, 2023
7a14d2f
fix: go integration tests
DylanVerstraete Jun 9, 2023
8924cf1
chore: revert contract bill event type
DylanVerstraete Jun 12, 2023
ab050c0
chore: remove println
DylanVerstraete Jun 12, 2023
3bfe18c
chore: merge development
DylanVerstraete Jun 12, 2023
ebe5870
reviews and test rework
renauter Jun 12, 2023
1d8b942
chore: rework migration v11
renauter Jun 12, 2023
1ceaad4
feat: round costs before converting back to integer
renauter Jun 12, 2023
f41931f
chore: remove todo comment
renauter Jun 13, 2023
930f5cc
Merge remote-tracking branch 'origin' into development_feat_node_extr…
renauter Jun 13, 2023
e1cb885
chore: merge development
DylanVerstraete Jun 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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