implement compact confirmed transaction struct in C

This commit is contained in:
einhornimmond 2026-01-25 17:36:24 +01:00
parent 7fe7f4cb11
commit 015481cee1
18 changed files with 612 additions and 1 deletions

View File

@ -3,6 +3,8 @@
/.env.bak
/build/
/locales/
lib
.zigar-cache
package-json.lock
coverage
# emacs

View File

@ -0,0 +1,8 @@
optimize = "ReleaseSmall"
[modules."lib/hello.zigar"]
source = "zig/hello.zig"
[[targets]]
arch = "x64"
platform = "linux"

View File

@ -5,6 +5,7 @@
"": {
"name": "dlt-connector",
"dependencies": {
"bun-zigar": "^0.15.2",
"cross-env": "^7.0.3",
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js#f265dbb1780a912cf8b0418dfe3eaf5cdc5b51cf",
},
@ -392,6 +393,8 @@
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
"bun-zigar": ["bun-zigar@0.15.2", "", { "dependencies": { "node-zigar-addon": "0.15.2", "zigar-compiler": "^0.15.2" }, "bin": { "zigar": "bin/cli.js", "bun-zigar": "bin/cli.js" } }, "sha512-slEHTEapQEIqB86OeiToPuuFXe39DCIYISTPzbIMBTZL34vRzCIa5wFn5ATudauHFFwl5/y5JYv8tluk2QL9Eg=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="],
@ -784,7 +787,7 @@
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
"node-api-headers": ["node-api-headers@1.6.0", "", {}, "sha512-81T99+mWLZnxX0LlZPYuafyFlxVVaWKQ0BDAbSrOqLO+v+gzCzu0GTAVNeVK8lucqjqo9L/1UcK9cpkem8Py4Q=="],
"node-api-headers": ["node-api-headers@1.8.0", "", {}, "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ=="],
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
@ -792,6 +795,8 @@
"node-releases": ["node-releases@2.0.26", "", {}, "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA=="],
"node-zigar-addon": ["node-zigar-addon@0.15.2", "", { "dependencies": { "node-api-headers": "^1.7.0" } }, "sha512-QjJcPRtUZLkULaFXapAvTzLKKRddgaupr7wQqgDUQo541FMCXAhgWdZJtNcIgCNykJG0bG0Fza5VTKBdSvyavQ=="],
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
"npm-path": ["npm-path@2.0.4", "", { "dependencies": { "which": "^1.2.10" }, "bin": { "npm-path": "bin/npm-path" } }, "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw=="],
@ -1060,6 +1065,8 @@
"yoctocolors-cjs": ["yoctocolors-cjs@2.1.3", "", {}, "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw=="],
"zigar-compiler": ["zigar-compiler@0.15.2", "", {}, "sha512-zlJ8kUwndwrLl4iRlIWEcidC2rcSsfeWM0jvbSoxUVf+SEKd4bVik3z4YDivuyX3SUiUjpCMNyp65etD6BKRmQ=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@babel/core/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
@ -1104,6 +1111,8 @@
"cmake-js/fs-extra": ["fs-extra@11.3.2", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A=="],
"cmake-js/node-api-headers": ["node-api-headers@1.6.0", "", {}, "sha512-81T99+mWLZnxX0LlZPYuafyFlxVVaWKQ0BDAbSrOqLO+v+gzCzu0GTAVNeVK8lucqjqo9L/1UcK9cpkem8Py4Q=="],
"connect/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"elliptic/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="],

View File

@ -0,0 +1 @@
preload = ["bun-zigar"]

View File

@ -0,0 +1,23 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_COMPACT_ACCOUNT_BALANCE_H
#define __GRADIDO_BLOCKCHAIN_C_COMPACT_ACCOUNT_BALANCE_H
#include <stdint.h>
#include "grdc_public_key_index.h"
#include "../grdl/grdl_unit.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct grdc_account_balance
{
grdl_unit balance;
grdc_public_key_index publicKeyIndex;
} grdc_account_balance;
#ifdef __cplusplus
}
#endif
#endif // __GRADIDO_BLOCKCHAIN_C_COMPACT_ACCOUNT_BALANCE_H

View File

@ -0,0 +1,104 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_COMPACT_CONFIRMED_TRANSACTION_COMPACT_H
#define __GRADIDO_BLOCKCHAIN_C_COMPACT_CONFIRMED_TRANSACTION_COMPACT_H
#include <stdint.h>
#include "grdc_account_balance.h"
#include "grdc_public_key_index.h"
#include "grdc_tx_id.h"
#include "grdc_tx_timestamps.h"
#include "../grdd/grdd_address_type.h"
#include "../grdd/grdd_balance_derivation_type.h"
#include "../grdd/grdd_cross_group_type.h"
#include "../grdd/grdd_transaction_type.h"
#include "../grdl/grdl_unit.h"
#ifdef __cplusplus
extern "C" {
#endif
// Gradido Compact Confirmed Transaction
struct grdc_confirmed_tx
{
grdc_tx_timestamps timestamps;
// inline Timestamp getConfirmedAt() const { return timestamps.getConfirmedAt(); }
// inline Timestamp getCreatedAt() const { return timestamps.getCreatedAt(); }
// txId and pairingTxId packed to save 8 Bytes padding
uint64_t txNrs[2];
uint32_t communityIdIndex[2];
// inline TxId getTxId() const { return TxId(txNrs[0], communityIdIndex[0]); }
// for cross group transactions, else empty
// inline TxId getPairingTxId() const { return TxId(txNrs[1], communityIdIndex[1]); }
// account balances and memos via txId in separate list/manager/thingy
// enums, usually uint8_t
uint8_t crossGroupType; // grdd_cross_group_type
uint8_t transactionType; // grdd_transaction_type
uint8_t balanceDerivationType; // grdd_balance_derivation_type
uint8_t accountBalanceCount;
grdc_account_balance accountBalances[2];
// common fields for most transactions
union { // 24 Bytes
struct {
grdl_unit amount; // 8 Bytes
grdc_public_key_index recipientPublicKeyIndex; // 8 Bytes
uint64_t targetDate; // 8 Bytes
} creation;
struct {
grdl_unit amount; // 8 Bytes
grdc_public_key_index senderPublicKeyIndex; // 8 Bytes
grdc_public_key_index recipientPublicKeyIndex; // 8 Bytes
} transfer; // also used for redeem deferred transfer, and deferredTransferTransactionNr is stored in extra dictionary
struct {
grdl_unit amount; // 8 Bytes
// work only on local, take communityIdIndex from txId
uint32_t senderPublicKeyIndex; // 4 Bytes
uint32_t recipientPublicKeyIndex; // 4 Bytes
uint32_t timeoutDuration; // 4 Bytes
} deferredTransfer; // fund deferred transfer address only on your own community
struct {
grdc_tx_id deferredTransferTransactionNr; // 16 Bytes, contain 4 Bytes padding
} timeoutDeferredTransfer;
struct {
uint8_t addressType; // grdd_address_type // 1 Byte
uint16_t derivationIndex; // 2 Byte (for the time beeing, update if more than 65535 are needed)
uint32_t nameHashIndex; // 4 Bytes
grdc_public_key_index userPublicKeyIndex; // 8 Bytes
grdc_public_key_index accountPublicKeyIndex; // 8 Bytes
} registerAddress;
struct {
grdc_public_key_index communityPublicKeyIndex; // 8 Bytes
grdc_public_key_index communityAufPublicKeyIndex; // 8 Bytes
grdc_public_key_index communityGmwPublicKeyIndex; // 8 Bytes
} communityRoot;
};
};
inline grdd_timestamp grdc_confirmed_tx_get_confirmed_at(const grdc_confirmed_tx* c) {
return grdc_tx_timestamps_get_confirmed_at(&c->timestamps);
}
inline grdd_timestamp grdc_confirmed_tx_get_created_at(const grdc_confirmed_tx* c) {
return grdc_tx_timestamps_get_created_at(&c->timestamps);
}
inline grdc_tx_id grdc_confirmed_tx_get_tx_id(const grdc_confirmed_tx* c) {
grdc_tx_id txId{.nr = c->txNrs[0], .communityIdIndex = c->communityIdIndex[0]};
return txId;
}
inline grdc_tx_id grdc_confirmed_tx_get_pairing_tx_id(const grdc_confirmed_tx* c) {
grdc_tx_id pairingTxId{.nr = c->txNrs[1], .communityIdIndex = c->communityIdIndex[1]};
return pairingTxId;
}
#ifdef __cplusplus
}
#endif
#endif //__GRADIDO_BLOCKCHAIN_C_COMPACT_CONFIRMED_TRANSACTION_COMPACT_H

View File

@ -0,0 +1,21 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_COMPACT_PUBLIC_KEY_INDEX_H
#define __GRADIDO_BLOCKCHAIN_C_COMPACT_PUBLIC_KEY_INDEX_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct grdc_public_key_index
{
uint32_t publicKeyIndex;
uint32_t communityIdIndex;
} grdc_public_key_index;
#ifdef __cplusplus
}
#endif
#endif // __GRADIDO_BLOCKCHAIN_C_COMPACT_PUBLIC_KEY_INDEX_H

View File

@ -0,0 +1,25 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_COMPACT_TRANSACTION_ID
#define __GRADIDO_BLOCKCHAIN_C_COMPACT_TRANSACTION_ID
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct grdc_tx_id
{
uint64_t nr;
uint32_t communityIdIndex;
} grdc_tx_id;
inline bool grdc_tx_id_empty(const grdc_tx_id* id) {
return !id->nr && !id->communityIdIndex;
}
#ifdef __cplusplus
}
#endif
#endif // __GRADIDO_BLOCKCHAIN_C_COMPACT_TRANSACTION_ID

View File

@ -0,0 +1,42 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_COMPACT_TRANSACTION_TIMESTAMPS_H
#define __GRADIDO_BLOCKCHAIN_C_COMPACT_TRANSACTION_TIMESTAMPS_H
#include <stdint.h>
#include "../grdd/grdd_timestamp.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct grdc_tx_timestamps
{
uint64_t confirmedSeconds;
uint32_t confirmedNanos;
int32_t deltaMs; // add to confirmed second and nanos to get created_at
} grdc_tx_timestamps;
inline grdd_timestamp grdc_tx_timestamps_get_confirmed_at(const grdc_tx_timestamps* in) {
grdd_timestamp t;
t.seconds = in->confirmedSeconds;
t.nanos = in->confirmedNanos;
return t;
}
inline grdd_timestamp grdc_tx_timestamps_get_created_at(const grdc_tx_timestamps* in) {
grdd_timestamp t;
uint64_t sec = (in->confirmedSeconds + in->deltaMs) / 1000;
uint32_t ns = (in->confirmedNanos + (in->deltaMs % 1000)) * 1000000;
if (ns >= 1000000000) {
sec += 1;
ns -= 1000000000;
}
t.seconds = sec;
t.nanos = ns;
return t;
}
#ifdef __cplusplus
}
#endif
#endif //__GRADIDO_BLOCKCHAIN_C_COMPACT_TRANSACTION_TIMESTAMPS_H

View File

@ -0,0 +1,23 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_DATA_ADDRESS_TYPE_H
#define __GRADIDO_BLOCKCHAIN_C_DATA_ADDRESS_TYPE_H
#ifdef __cplusplus
extern "C" {
#endif
enum grdd_address_type {
GRDD_ADDRESS_TYPE_NONE = 0, // if no address was found
GRDD_ADDRESS_TYPE_COMMUNITY_HUMAN = 1, // creation account for human
GRDD_ADDRESS_TYPE_COMMUNITY_GMW = 2, // community public budget account
GRDD_ADDRESS_TYPE_COMMUNITY_AUF = 3, // community compensation and environment founds account
GRDD_ADDRESS_TYPE_COMMUNITY_PROJECT = 4, // no creations allowed
GRDD_ADDRESS_TYPE_SUBACCOUNT = 5, // no creations allowed
GRDD_ADDRESS_TYPE_CRYPTO_ACCOUNT = 6, // user control his keys, no creations
GRDD_ADDRESS_TYPE_DEFERRED_TRANSFER = 7 // special type, no need for register address
};
#ifdef __cplusplus
}
#endif
#endif //__GRADIDO_BLOCKCHAIN_C_DATA_ADDRESS_TYPE_H

View File

@ -0,0 +1,26 @@
#ifndef GRADIDO_BLOCKCHAIN_C_DATA_BALANCE_DERIVATION_TYPE_H
#define GRADIDO_BLOCKCHAIN_C_DATA_BALANCE_DERIVATION_TYPE_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @enum grdd_balance_derivation_type
*
* Flag to decide if Node calculates balance and decay
* or trusts external data.
*/
enum grdd_balance_derivation_type {
GRDD_BALANCE_DERIVATION_UNSPECIFIED = 0,
/* Balances & decay can be recalculated deterministically */
GRDD_BALANCE_DERIVATION_NODE = 1,
/* Balances are accepted as-is from external / legacy system */
GRDD_BALANCE_DERIVATION_EXTERN = 2
};
#ifdef __cplusplus
}
#endif
#endif /* GRADIDO_BLOCKCHAIN_C_DATA_BALANCE_DERIVATION_TYPE_H */

View File

@ -0,0 +1,20 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_DATA_CROSS_GROUP_TYPE_H
#define __GRADIDO_BLOCKCHAIN_C_DATA_CROSS_GROUP_TYPE_H
#ifdef __cplusplus
extern "C" {
#endif
enum grdd_cross_group_type
{
GRDD_CROSS_GROUP_TYPE_LOCAL = 0,
GRDD_CROSS_GROUP_TYPE_INBOUND = 1,
GRDD_CROSS_GROUP_TYPE_OUTBOUND = 2,
GRDD_CROSS_GROUP_TYPE_CROSS = 3
};
#ifdef __cplusplus
}
#endif
#endif // __GRADIDO_BLOCKCHAIN_C_DATA_CROSS_GROUP_TYPE_H

View File

@ -0,0 +1,38 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_DATA_TIMESTAMP_H
#define __GRADIDO_BLOCKCHAIN_C_DATA_TIMESTAMP_H
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct grdd_timestamp
{
int64_t seconds;
int32_t nanos;
} grdd_timestamp;
inline bool grdd_timestamp_empty(const grdd_timestamp* timestamp) {
return !timestamp->seconds && !timestamp->nanos;
}
inline bool grdd_timestamp_eq(const grdd_timestamp* t1, grdd_timestamp* t2) {
return t1->seconds == t2->seconds && t1->nanos == t2->nanos;
}
inline bool grdd_timestamp_gt(const grdd_timestamp* t1, const grdd_timestamp* t2) {
return t1->seconds > t2->seconds || t1->seconds == t2->seconds && t1->nanos > t2->nanos;
}
inline bool grdd_timestamp_lt(const grdd_timestamp* t1, const grdd_timestamp* t2) {
return t1->seconds < t2->seconds || t1->seconds == t2->seconds && t1->nanos < t2->nanos;
}
inline grdd_timestamp grdd_timestamp_sub(const grdd_timestamp* t1, grdd_timestamp* t2) {
grdd_timestamp result{.seconds = t1->seconds - t2->seconds, .nanos = t1->nanos - t2->nanos};
return result;
}
#ifdef __cplusplus
}
#endif
#endif // __GRADIDO_BLOCKCHAIN_C_DATA_TIMESTAMP_H

View File

@ -0,0 +1,56 @@
#ifndef GRADIDO_BLOCKCHAIN_C_DATA_TRANSACTION_TYPE_H
#define GRADIDO_BLOCKCHAIN_C_DATA_TRANSACTION_TYPE_H
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \addtogroup enums
* @{
*/
/*!
* \enum grdd_transaction_type
* Enum for different transaction types
* !!! don't change order
*/
enum grdd_transaction_type {
//! Invalid or Empty Transaction
GRDD_TRANSACTION_TYPE_NONE = 0,
//! Creation Transaction, creates new Gradidos
GRDD_TRANSACTION_TYPE_CREATION,
//! Transfer Transaction, move Gradidos from one account to another
GRDD_TRANSACTION_TYPE_TRANSFER,
//! Group Friends Update Transaction, update relationship between groups
GRDD_TRANSACTION_TYPE_COMMUNITY_FRIENDS_UPDATE,
//! Register new address or sub address to group or move address to another group
GRDD_TRANSACTION_TYPE_REGISTER_ADDRESS,
//! Special Transfer Transaction with timeout used for Gradido Link
GRDD_TRANSACTION_TYPE_DEFERRED_TRANSFER,
//! First Transaction in Blockchain
GRDD_TRANSACTION_TYPE_COMMUNITY_ROOT,
//! Redeeming deferred transfer
GRDD_TRANSACTION_TYPE_REDEEM_DEFERRED_TRANSFER,
//! Timeout deferred transfer, send back locked gdds
GRDD_TRANSACTION_TYPE_TIMEOUT_DEFERRED_TRANSFER,
//! Technical type for using it in for loops, as max index
GRDD_TRANSACTION_TYPE_MAX_VALUE
};
/*! @} */
#ifdef __cplusplus
}
#endif
#endif /* GRADIDO_BLOCKCHAIN_C_DATA_TRANSACTION_TYPE_H */

View File

@ -0,0 +1,123 @@
#include "grdl_unit.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
const double SECONDS_PER_YEAR = 31556952.0; // seconds in a year in gregorian calender
const grdd_timestamp DECAY_START_TIME = (grdd_timestamp){.seconds = 1620927991, .nanos = 0};
double roundToPrecision(double gdd, uint8_t precision)
{
double factor = pow(10.0, precision);
return round(gdd * factor) / factor;
}
grdl_unit grdl_unit_from_decimal(double gdd)
{
grdl_unit gradidoUnit = {
(int64_t)(roundToPrecision(gdd, 4) * 10000.0)
};
return gradidoUnit;
}
grdl_unit grdl_unit_from_string(const char* gdd_string)
{
if (!gdd_string) return (grdl_unit){0};
char* end;
double gdd_double = strtod(gdd_string, &end);
if (end == gdd_string || *end != '\0') {
// invalid string
return (grdl_unit){0};
}
return grdl_unit_from_decimal(gdd_double);
}
int grdl_unit_to_string(const grdl_unit* u, char* buffer, size_t bufferSize, uint8_t precision)
{
if (precision > 4) return 1; // C hat keine Exceptions
// Convert to double
double decimal = (double)(u->gradidoCent) / 10000.0;
// Round down like Node.js if precision < 4
if (precision < 4) {
double factor = pow(10.0, precision);
decimal = round(decimal * factor) / factor;
}
// Write to buffer
int written = snprintf(buffer, bufferSize, "%.*f", precision, decimal);
// snprintf returns number of chars that would have been written (excluding null)
// snprintf return negative value on encoding error
if (written < 0) return 2;
if ((size_t)written < bufferSize) {
return 0;
}
return bufferSize - written;
}
grdl_unit grdl_unit_calculate_decay(const grdl_unit* u, int64_t seconds)
{
if (seconds == 0) return (grdl_unit){u->gradidoCent};
// decay for one year is 50%
/*
* while (seconds >= SECONDS_PER_YEAR) {
mGradidoCent *= 0.5;
seconds -= SECONDS_PER_YEAR;
}
*/
int64_t gradidoCent = u->gradidoCent;
// optimize version from above
if (seconds >= SECONDS_PER_YEAR) {
uint64_t times = (uint64_t)(seconds / SECONDS_PER_YEAR);
seconds = seconds - times * SECONDS_PER_YEAR;
gradidoCent = u->gradidoCent >> times;
if (!seconds) return (grdl_unit){gradidoCent};
}
// */
/*!
* calculate decay factor with compound interest formula converted to q <br>
* n = (lg Kn - lg K0) / lg q => <br>
* lg q = (lg Kn - lg K0) / n => <br>
* q = e^((lg Kn - lg K0) / n) <br>
* <br>
* with:
* <ul>
* <li>q = decay_factor</li>
* <li>n = days_per_year * 60 * 60 * 24 = seconds per year</li>
* <li>Kn = 50 (capital after a year)</li>
* <li>K0 = 100 (capital at start)</li>
* </ul>
* further simplified:
* lg 50 - lg 100 = lg 2 =>
* q = e^(lg 2 / n) = 2^(x/n)
* with x as seconds in which decay occured
*/
// https://www.wolframalpha.com/input?i=%28e%5E%28lg%282%29+%2F+31556952%29%29%5Ex&assumption=%7B%22FunClash%22%2C+%22lg%22%7D+-%3E+%7B%22Log%22%7D
// from wolframalpha, based on the interest rate formula
return (grdl_unit){((int64_t)((double)(gradidoCent) * pow(2.0, (double)((double)(-seconds) / SECONDS_PER_YEAR))))};
}
bool grdl_unit_calculate_duration_seconds(
const grdd_timestamp* startTime,
const grdd_timestamp* endTime,
int64_t* outSeconds
) {
if (!outSeconds) {
return false;
}
if(grdd_timestamp_gt(startTime, endTime) {
return false;
}
grdd_timestamp start = grdd_timestamp_gt(startTime, &DECAY_START_TIME) ? *startTime : DECAY_START_TIME;
grdd_timestamp end = grdd_timestamp_gt(endTime, &DECAY_START_TIME) ? *endTime : DECAY_START_TIME;
if (grdd_timestamp_eq(&start, &end)) {
*outSeconds = 0;
return true;
}
*outSeconds = grdd_timestamp_sub(&end, &start).seconds;
return true;
}

View File

@ -0,0 +1,84 @@
#ifndef __GRADIDO_BLOCKCHAIN_C_LOGIC_UNIT_H
#define __GRADIDO_BLOCKCHAIN_C_LOGIC_UNIT_H
#include "../grdd/grdd_timestamp.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct grdl_unit
{
int64_t gradidoCent;
} grdl_unit;
grdl_unit grdl_unit_from_decimal(double gdd);
grdl_unit grdl_unit_from_string(const char* gdd_string);
//! \return 0 for ok, 1 for invalid precision, 2 for printf encoding error
// and -x if string buffer wasn't big enough where x is the number of missing bytes
int grdl_unit_to_string(const grdl_unit* u, char* buffer, size_t bufferSize, uint8_t precision);
inline grdl_unit grdl_unit_negated(const grdl_unit* u)
{
grdl_unit gradidoUnit = {u->gradidoCent};
gradidoUnit.gradidoCent *= -1;
return gradidoUnit;
}
inline void grdl_unit_negate(grdl_unit* u)
{
if (u) u->gradidoCent = -u->gradidoCent;
}
static inline grdl_unit grdl_unit_zero()
{
return (grdl_unit){0};
}
//! return false if startTime > endTime
bool grdl_unit_calculate_duration_seconds(
const grdd_timestamp* startTime,
const grdd_timestamp* endTime,
int64_t* outSeconds
);
grdl_unit grdl_unit_calculate_decay(const grdl_unit* u, int64_t seconds);
inline grdl_unit grdl_unit_calculate_decay_timestamp(
const grdl_unit* u,
const grdd_timestamp* startTime,
const grdd_timestamp* endTime
) {
int64_t seconds = 0;
if(!grdl_unit_calculate_duration_seconds(startTime, endTime, &seconds)) {
return (grdl_unit){0};
}
return grdl_unit_calculate_decay(u, seconds);
}
inline grdl_unit grld_unit_calculate_compound_interest(const grdl_unit* u, int64_t seconds) {
return grdl_unit_calculate_decay(u, -seconds);
}
inline grdl_unit grld_unit_calculate_compound_interest_timestamp(
const grdl_unit* u,
const grdd_timestamp* startTime,
const grdd_timestamp* endTime
) {
int64_t seconds = 0;
if(!grdl_unit_calculate_duration_seconds(startTime, endTime, &seconds)) {
return (grdl_unit){0};
}
return grdl_unit_calculate_decay(u, -seconds);
}
#ifdef __cplusplus
}
#endif
#endif // __GRADIDO_BLOCKCHAIN_C_LOGIC_UNIT_H

View File

@ -18,6 +18,7 @@
"lint:fix": "biome check --error-on-warnings . --write"
},
"dependencies": {
"bun-zigar": "^0.15.2",
"cross-env": "^7.0.3",
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js#f265dbb1780a912cf8b0418dfe3eaf5cdc5b51cf"
},

View File

@ -0,0 +1,5 @@
const std = @import("std");
pub fn hello() void {
std.debug.print("Hello world!", .{});
}