From 441d2f90ac6ba199c15f5b4ae5e242ff5da302b5 Mon Sep 17 00:00:00 2001 From: IvanMahda Date: Wed, 22 May 2024 12:16:19 +0300 Subject: [PATCH 1/4] [JSS-101] Estimate smart-contract update energy costs Added new method getContractUpdateEnergyCost Get contract update energy cost Estimated by calculateEnergyCost, where transactionSpecificCost received from invokeContract used energy Updated README.md for node-sdk examples --- README.md | 2 ++ examples/nodejs/README.md | 4 +++ packages/sdk/CHANGELOG.md | 6 ++++ packages/sdk/package.json | 2 +- packages/sdk/src/contractHelpers.ts | 13 +++++++++ packages/sdk/src/energyCost.ts | 44 +++++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 25f478a57..5406185a8 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ npm_config_target_arch=x64 yarn ### Building for a release +Note: you need rustup in order to build rust-bindings (can be installed from [rust-lang.org](https://www.rust-lang.org/tools/install)) + To build the project run ```shell diff --git a/examples/nodejs/README.md b/examples/nodejs/README.md index 64412ed8c..71479b17b 100644 --- a/examples/nodejs/README.md +++ b/examples/nodejs/README.md @@ -48,3 +48,7 @@ yarn run-example /path/to/example.ts [opts] ``` Where opts are any arguments that the example script takes. + +Default endpoint for node is 'localhost:20000', +but instead of installing local node, +can be used testnet node https://node.testnet.concordium.com:20000 diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 6f7aa9cfe..a77d89a2e 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 7.4.1 + +### Added + +- Method (`getContractUpdateEnergyCost`) for estimating energy usage of contract update. + ## 7.4.0 ### Added diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 98f4bd0df..1fa546c4c 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@concordium/web-sdk", - "version": "7.4.0", + "version": "7.4.1", "license": "Apache-2.0", "engines": { "node": ">=16" diff --git a/packages/sdk/src/contractHelpers.ts b/packages/sdk/src/contractHelpers.ts index ad311ec7c..0e21abb25 100644 --- a/packages/sdk/src/contractHelpers.ts +++ b/packages/sdk/src/contractHelpers.ts @@ -91,3 +91,16 @@ export function getNamesFromReceive(receiveName: string): { entrypointName: receiveName.substring(splitPoint + 1), }; } + +/** + * Get contract update payload size by adding reserved offsets to parameter size and receive name size + * Amount (8 bytes), Contract address (16 bytes), Receive name size (2 bytes), Parameter size (2 bytes) + */ +export function getUpdatePayloadSize( + parameterSize: number, + receiveNameLength: number +) { + return ( + 8n + 16n + 2n + BigInt(parameterSize) + 2n + BigInt(receiveNameLength) + ); +} diff --git a/packages/sdk/src/energyCost.ts b/packages/sdk/src/energyCost.ts index d08c03b98..d94668a10 100644 --- a/packages/sdk/src/energyCost.ts +++ b/packages/sdk/src/energyCost.ts @@ -1,3 +1,4 @@ +import { ConcordiumGRPCClient } from './grpc/GRPCClient.js'; import { getAccountTransactionHandler } from './accountTransactions.js'; import { collapseRatio, multiplyRatio } from './ratioHelpers.js'; import { @@ -8,6 +9,13 @@ import { } from './types.js'; import * as Energy from './types/Energy.js'; import * as CcdAmount from './types/CcdAmount.js'; +import { + AccountAddress, + ContractAddress, + Parameter, + ReceiveName, +} from './pub/types.js'; +import { getUpdatePayloadSize } from './contractHelpers.js'; /** * These constants must be consistent with constA and constB in: @@ -60,6 +68,42 @@ export function getEnergyCost( ); } +/** + * Get contract update energy cost + * Estimated by calculateEnergyCost, where transactionSpecificCost received from invokeContract used energy + * @param {ConcordiumGRPCClient} grpcClient - The client to be used for the query + * @param {ContractAddress.Type} contractAddress - The address of the contract to query + * @param {AccountAddress.Type} invoker - Representation of an account address + * @param {Parameter.Type} parameter - Input for contract function + * @param {ReceiveName.Type} method - Represents a receive-function in a smart contract module + * @param {bigint} signatureCount - Number of expected signatures + */ +export async function getContractUpdateEnergyCost( + grpcClient: ConcordiumGRPCClient, + contractAddress: ContractAddress.Type, + invoker: AccountAddress.Type, + parameter: Parameter.Type, + method: ReceiveName.Type, + signatureCount: bigint +): Promise { + const res = await grpcClient.invokeContract({ + contract: contractAddress, + invoker, + parameter, + method, + }); + + if (!res || res.tag === 'failure') { + throw new Error(res?.reason?.tag || 'no response'); + } + + return calculateEnergyCost( + signatureCount, + getUpdatePayloadSize(parameter.buffer.length, method.toString().length), + res.usedEnergy.value + ); +} + /** * Given the current blockchain parameters, return the microCCD per NRG exchange rate of the chain. * @returns the microCCD per NRG exchange rate as a ratio. From 07edcd484be468d7a7edfa2c3e0ae6e4c60bb3d2 Mon Sep 17 00:00:00 2001 From: IvanMahda Date: Wed, 22 May 2024 12:59:51 +0300 Subject: [PATCH 2/4] [JSS-101] Estimate smart-contract update energy costs fix markdown-lint --- .markdown-linkcheck.json | 3 +++ examples/nodejs/README.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.markdown-linkcheck.json b/.markdown-linkcheck.json index 72dc9011a..02fb7c1bf 100644 --- a/.markdown-linkcheck.json +++ b/.markdown-linkcheck.json @@ -2,6 +2,9 @@ "ignorePatterns": [ { "pattern": "classes/grpc.ConcordiumGRPCClient.html" + }, + { + "pattern": "https://node.testnet.concordium.com:20000" } ] } diff --git a/examples/nodejs/README.md b/examples/nodejs/README.md index 71479b17b..d2f5d9359 100644 --- a/examples/nodejs/README.md +++ b/examples/nodejs/README.md @@ -49,6 +49,6 @@ yarn run-example /path/to/example.ts [opts] Where opts are any arguments that the example script takes. -Default endpoint for node is 'localhost:20000', +Default endpoint for node is 'localhost:20000', but instead of installing local node, -can be used testnet node https://node.testnet.concordium.com:20000 +can be used testnet node From 87b3adabbfcb910c3b7d0ce51a5af85f9eda8553 Mon Sep 17 00:00:00 2001 From: IvanMahda Date: Wed, 22 May 2024 21:40:27 +0300 Subject: [PATCH 3/4] [JSS-101] Estimate smart-contract update energy costs changed test node link updated getContractUpdateEnergyCost description --- .markdown-linkcheck.json | 2 +- examples/nodejs/README.md | 2 +- packages/sdk/src/energyCost.ts | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.markdown-linkcheck.json b/.markdown-linkcheck.json index 02fb7c1bf..6db98e89c 100644 --- a/.markdown-linkcheck.json +++ b/.markdown-linkcheck.json @@ -4,7 +4,7 @@ "pattern": "classes/grpc.ConcordiumGRPCClient.html" }, { - "pattern": "https://node.testnet.concordium.com:20000" + "pattern": "https://grpc.testnet.concordium.com:20000" } ] } diff --git a/examples/nodejs/README.md b/examples/nodejs/README.md index d2f5d9359..ccaba779f 100644 --- a/examples/nodejs/README.md +++ b/examples/nodejs/README.md @@ -51,4 +51,4 @@ Where opts are any arguments that the example script takes. Default endpoint for node is 'localhost:20000', but instead of installing local node, -can be used testnet node +can be used testnet node diff --git a/packages/sdk/src/energyCost.ts b/packages/sdk/src/energyCost.ts index d94668a10..2cfbcc78f 100644 --- a/packages/sdk/src/energyCost.ts +++ b/packages/sdk/src/energyCost.ts @@ -77,6 +77,13 @@ export function getEnergyCost( * @param {Parameter.Type} parameter - Input for contract function * @param {ReceiveName.Type} method - Represents a receive-function in a smart contract module * @param {bigint} signatureCount - Number of expected signatures + * + * @throws {Error} 'no response' if either the block does not exist, or then node fails to parse any of the inputs + * If the response tag is `failure`, then error contains a response message + * + * @returns {Energy} estimated amount of energy for the last finalized block according to the node, + * this means that the actual energy cost might be different depending on the implementation of the smart contract + * and the interaction with the instance, since this was estimated */ export async function getContractUpdateEnergyCost( grpcClient: ConcordiumGRPCClient, From 82335c309a1f1864bff2754e6b51602ac0bbe614 Mon Sep 17 00:00:00 2001 From: IvanMahda Date: Thu, 23 May 2024 17:13:47 +0300 Subject: [PATCH 4/4] [JSS-101] Estimate smart-contract update energy costs changed version to unreleased added optional blockHash param --- packages/sdk/CHANGELOG.md | 2 +- packages/sdk/package.json | 2 +- packages/sdk/src/contractHelpers.ts | 2 +- packages/sdk/src/energyCost.ts | 20 +++++++++++++------- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index a77d89a2e..90d269fcc 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 7.4.1 +## Unreleased ### Added diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 1fa546c4c..98f4bd0df 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@concordium/web-sdk", - "version": "7.4.1", + "version": "7.4.0", "license": "Apache-2.0", "engines": { "node": ">=16" diff --git a/packages/sdk/src/contractHelpers.ts b/packages/sdk/src/contractHelpers.ts index 0e21abb25..7b8cf1830 100644 --- a/packages/sdk/src/contractHelpers.ts +++ b/packages/sdk/src/contractHelpers.ts @@ -99,7 +99,7 @@ export function getNamesFromReceive(receiveName: string): { export function getUpdatePayloadSize( parameterSize: number, receiveNameLength: number -) { +): bigint { return ( 8n + 16n + 2n + BigInt(parameterSize) + 2n + BigInt(receiveNameLength) ); diff --git a/packages/sdk/src/energyCost.ts b/packages/sdk/src/energyCost.ts index 2cfbcc78f..0f56da1b4 100644 --- a/packages/sdk/src/energyCost.ts +++ b/packages/sdk/src/energyCost.ts @@ -9,6 +9,7 @@ import { } from './types.js'; import * as Energy from './types/Energy.js'; import * as CcdAmount from './types/CcdAmount.js'; +import * as BlockHash from './types/BlockHash.js'; import { AccountAddress, ContractAddress, @@ -77,6 +78,7 @@ export function getEnergyCost( * @param {Parameter.Type} parameter - Input for contract function * @param {ReceiveName.Type} method - Represents a receive-function in a smart contract module * @param {bigint} signatureCount - Number of expected signatures + * @param {BlockHash.Type} [blockHash] - Optional block hash allowing for dry-running the contract update at the end of a specific block. * * @throws {Error} 'no response' if either the block does not exist, or then node fails to parse any of the inputs * If the response tag is `failure`, then error contains a response message @@ -91,14 +93,18 @@ export async function getContractUpdateEnergyCost( invoker: AccountAddress.Type, parameter: Parameter.Type, method: ReceiveName.Type, - signatureCount: bigint + signatureCount: bigint, + blockHash?: BlockHash.Type ): Promise { - const res = await grpcClient.invokeContract({ - contract: contractAddress, - invoker, - parameter, - method, - }); + const res = await grpcClient.invokeContract( + { + contract: contractAddress, + invoker, + parameter, + method, + }, + blockHash + ); if (!res || res.tag === 'failure') { throw new Error(res?.reason?.tag || 'no response');