COOPENOMICS  v1
Кооперативная Экономика
Действия ledger2\ingroup public_ledger2

Пространства имен

namespace  anonymous_namespace{migrate.cpp}
 Миграция остатков с legacy-ledger на ledger2 (пересмотр 2026-04-20).
 

Функции

void ledger2::apply (eosio::name coopname, eosio::name initiator, eosio::name operation_code, eosio::asset amount, eosio::name username, eosio::checksum256 process_hash, std::string memo)
 Единая точка входа финансовых движений ledger2 (orchestrator). Подробнее...
 
void ledger2::walletop (eosio::name coopname, uint8_t op_code, eosio::name wallet_from, eosio::name wallet_to, eosio::asset amount, eosio::checksum256 process_hash, std::string memo)
 Атомарная операция по кошельку (issue/transfer/block/unblock). Подробнее...
 
void ledger2::debit (eosio::name coopname, uint64_t account_id, eosio::asset amount, eosio::checksum256 process_hash, std::string memo)
 Атомарная дебетовая проводка на счёт + пересчёт сальдо. Подробнее...
 
void ledger2::credit (eosio::name coopname, uint64_t account_id, eosio::asset amount, eosio::checksum256 process_hash, std::string memo)
 Атомарная кредитовая проводка на счёт + пересчёт сальдо. Подробнее...
 
void ledger2::migrate (uint64_t from_coop_index, uint64_t limit)
 Миграция остатков с legacy-ledger в ledger2 (курсорный режим). Подробнее...
 
void ledger2::walmove (eosio::name coopname, eosio::name initiator, eosio::name username, eosio::name from_wallet, eosio::name to_wallet, eosio::asset amount, eosio::checksum256 process_hash, std::string memo)
 Перевод между кошельками внутри одного бух.счёта (operation o.adj.walmove). Подробнее...
 
void ledger2::revert (eosio::name coopname, eosio::name initiator, uint64_t original_operation_id, eosio::name original_operation_code, eosio::name username, eosio::asset amount, uint8_t mirror_wallet_op, eosio::name mirror_wallet_from, eosio::name mirror_wallet_to, uint64_t mirror_debit_account_id, uint64_t mirror_credit_account_id, eosio::checksum256 process_hash, std::string memo)
 Откат ранее проведённой операции (operation o.adj.rev). Подробнее...
 

Подробное описание

Функции

◆ apply()

void ledger2::apply ( eosio::name  coopname,
eosio::name  initiator,
eosio::name  operation_code,
eosio::asset  amount,
eosio::name  username,
eosio::checksum256  process_hash,
std::string  memo 
)

Единая точка входа финансовых движений ledger2 (orchestrator).

Единая точка входа ledger2 для финансовых движений (orchestrator).

Не пишет в state напрямую: рассылает 3 atomic inline action (walletop, debit, credit), связанных общим process_hash.

Пересмотр 2026-04-18 (Epic 1 addendum): apply ничего не пишет в state напрямую. Это orchestrator, который рассылает 3 атомарных inline action:

  1. ledger2::walletop — issue/transfer/block/unblock на wallets2
  2. ledger2::debit — +debit_balance на accounts2[Dr] + пересчёт сальдо
  3. ledger2::credit — +credit_balance на accounts2[Cr] + пересчёт сальдо

Все три inline-actions передают единый process_hash, что позволяет бэкенду собрать «тройку» из blockchain_actions: WHERE account = 'ledger2' AND data->>'process_hash' = X

История проводок не хранится в RAM-таблицах — она целиком восстанавливается на бэкенде из blockchain_actions (для apply/walletop/debit/credit) и blockchain_deltas (для accounts2 и wallets2). Это даёт:

  • дешевле RAM (O(1) state на счёт + кошелёк, не O(N) проводок);
  • проще контракт (нет emplace/backfill в журналы);
  • лучше observability (каждое движение — отдельная action в трейсах).

◆ credit()

void ledger2::credit ( eosio::name  coopname,
uint64_t  account_id,
eosio::asset  amount,
eosio::checksum256  process_hash,
std::string  memo 
)

Атомарная кредитовая проводка на счёт + пересчёт сальдо.

Атомарная кредитовая проводка на счёт ledger2.

Внутренний action — вызывается только через inline из apply().

Внутренний action ledger2 — вызывается только через inline из apply(). Auth: только сам ledger2 (require_auth(get_self())).

Прибавляет amount к accounts2[account_id].credit_balance, затем пересчитывает balance согласно account_type. Парная debit-проводка приходит отдельным inline (action debit) с тем же process_hash — бэкенд связывает их в пару.

TODO(payer, 2026-04-18): payer = get_self() → RAM ledger2. Перевести на coopname при общем переходе на coopname-eosio.code permissions. См. Decision #D2 в code review Epic 1.

◆ debit()

void ledger2::debit ( eosio::name  coopname,
uint64_t  account_id,
eosio::asset  amount,
eosio::checksum256  process_hash,
std::string  memo 
)

Атомарная дебетовая проводка на счёт + пересчёт сальдо.

Атомарная дебетовая проводка на счёт ledger2.

Внутренний action — вызывается только через inline из apply().

Внутренний action ledger2 — вызывается только через inline из apply(). Auth: только сам ledger2 (require_auth(get_self())).

Прибавляет amount к accounts2[account_id].debit_balance, затем пересчитывает balance согласно account_type. Парная credit-проводка приходит отдельным inline (action credit) с тем же process_hash — бэкенд связывает их в пару.

TODO(payer, 2026-04-18): payer = get_self() → RAM ledger2. Перевести на coopname при общем переходе на coopname-eosio.code permissions. См. Decision #D2 в code review Epic 1.

◆ migrate()

void ledger2::migrate ( uint64_t  from_coop_index,
uint64_t  limit 
)

Миграция остатков с legacy-ledger в ledger2 (курсорный режим).

Аргументы
from_coop_indexначальный индекс в таблице registrator::coops (0 — с начала)
limitмаксимум кооп. за один вызов (UINT64_MAX — до конца)

Полный прогон: migrate(0, UINT64_MAX). Продовый прогон порциями: migrate(0, 10), migrate(10, 10), ... Мета-таблица хранит last_migrated_coop_index для возобновления.

Все балансы вносятся через inline apply(operations::migration::*) — единый путь учёта с полной двойной проводкой и audit-trail (пересмотр 2026-04-20: детерминированное разнесение на 6 целевых кошельков без транзитного счёта 99 и без зеркала CASH_MAIN).

◆ revert()

void ledger2::revert ( eosio::name  coopname,
eosio::name  initiator,
uint64_t  original_operation_id,
eosio::name  original_operation_code,
eosio::name  username,
eosio::asset  amount,
uint8_t  mirror_wallet_op,
eosio::name  mirror_wallet_from,
eosio::name  mirror_wallet_to,
uint64_t  mirror_debit_account_id,
uint64_t  mirror_credit_account_id,
eosio::checksum256  process_hash,
std::string  memo 
)

Откат ранее проведённой операции (operation o.adj.rev).

Создаёт зеркальную проводку по операции original_operation_id: меняет местами Dr/Cr счета и (для wallet_op) wallet_from/wallet_to. Для исходного ISSUE используется новый WalletOp::REVOKE (изъятие с wallet_from без увеличения куда-либо).

Параметры зеркала готовит backend из своей БД (по записи оригинала в blockchain_actions/state) — контракт не имеет доступа к истории операций.

Запрещено откатывать миграционные операции (operation_code starts with o.mig.). Повторный откат отката (revert от revert) разрешён.

Auth: coopname@active (председатель).

Аргументы
coopnameкооператив (payer auth)
initiatorинициатор отката (для аудита)
original_operation_idid оригинальной записи в blockchain_actions
original_operation_codeoperation_code оригинала (для запрета o.mig.*)
usernameusername оригинала (для аналитики)
amountсумма (как в оригинале)
mirror_wallet_opтип wallet-операции зеркала (REVOKE/TRANSFER/WALLET_ONLY)
mirror_wallet_fromкошелёк-источник зеркала
mirror_wallet_toкошелёк-получатель зеркала (пустое имя для REVOKE)
mirror_debit_account_idDr-счёт зеркала (0 для WALLET_ONLY)
mirror_credit_account_idCr-счёт зеркала (0 для WALLET_ONLY)
process_hashуникальный хэш для зеркальной операции
memoобязательное обоснование (≤ 255 символов)

Contract-only: top-level вызов председателем (coopname@active) запрещён. Action принимает только подпись от whitelisted контрактов (contracts_whitelist) — то есть зеркальная проводка возможна только когда её инициирует другой контракт-инициатор (registrator/wallet/capital/...), который параллельно откатывает свой собственный state.

Why this restriction: ledger2 — учётный слой; зеркальная проводка не трогает сущности контрактов-инициаторов (participants/deposits/contributors). Top-level откат председателем рассинхронизирует учёт и состояние домена (например, participant остаётся accepted, а минимальный паевой «вернулся»).

Контракт не имеет доступа к истории blockchain_actions — параметры зеркала собирает контракт-инициатор (он знает свой исходный operation_code и соответствующие Dr/Cr/wallet через operations.hpp registry).

Запрет на откат миграционных операций (o.mig.*) сохранён. Повторный откат отката (revert от revert) разрешён.

◆ walletop()

void ledger2::walletop ( eosio::name  coopname,
uint8_t  op_code,
eosio::name  wallet_from,
eosio::name  wallet_to,
eosio::asset  amount,
eosio::checksum256  process_hash,
std::string  memo 
)

Атомарная операция по кошельку (issue/transfer/block/unblock).

Внутренний action — вызывается только через inline из apply().

Внутренний action ledger2 — вызывается только через inline из apply(). Auth: только сам ledger2 (require_auth(get_self())).

Ровно одна мутация wallets2: ISSUE добавляет на wallet_to, TRANSFER перемещает wallet_from → wallet_to, BLOCK/UNBLOCK переносит между available и blocked на wallet_from.

История этого вызова автоматически попадает в blockchain_actions с полями (op_code, wallet_from, wallet_to, amount, process_hash, memo) — этого достаточно бэкенду для восстановления wjournal-эквивалента.

TODO(payer, 2026-04-18): сейчас payer = get_self() (ledger2), что даёт неограниченный рост RAM контракта. Перевести на payer = coopname, когда все caller-контракты возьмут у coopname разрешение eosio.codeledger2 через linkauth. Решение по code review Decision #D2.

◆ walmove()

void ledger2::walmove ( eosio::name  coopname,
eosio::name  initiator,
eosio::name  username,
eosio::name  from_wallet,
eosio::name  to_wallet,
eosio::asset  amount,
eosio::checksum256  process_hash,
std::string  memo 
)

Перевод между кошельками внутри одного бух.счёта (operation o.adj.walmove).

Ручная корректировка председателя: переносит amount с from_wallet на to_wallet БЕЗ движения по бух.счетам (debit/credit не вызываются). Применение: разнесение по аналитическим кошелькам после криво легшей миграции (типичный кейс voskhod), мелкие исправления в рамках одного фонда.

Auth: coopname@active (председатель). Audit: action+inline walletop попадают в blockchain_actions с общим process_hash, бэкенд видит как один процесс processes::adjustment::CORRECTION (p.adj.fix).

Валидация: from_wallet ≠ to_wallet, оба в LEDGER2_WALLET_REGISTRY, memo не пуст. Соответствие account_id для двух кошельков обеспечивает UI/backend (контракт не хранит wallet→account mapping).

Аргументы
coopnameкооператив (одновременно payer auth)
initiatorинициатор корректировки (для аудита; обычно совпадает с coopname)
usernameвладелец кошельков (для аналитики; обычно coopname для коллективных кошельков)
from_walletкошелёк-источник
to_walletкошелёк-получатель
amountсумма в _root_govern_symbol
process_hashуникальный хэш корректировки (генерирует backend)
memoобязательное обоснование (≤ 255 символов)

Top-level action — председатель подписывает сам, никаких caller-контрактов. Делает один inline walletop с op_code = WALLET_ONLY (TRANSFER без Dr/Cr) — так как корректировка между кошельками одного бух.счёта не меняет балансы самих счетов (accounts2.balance), только их аналитику.

Связь wallet→account не хранится в LEDGER2_WALLET_REGISTRY (она выводится из OPERATION_REGISTRY по месту использования и может теоретически быть многозначной), поэтому соответствие двух кошельков одному account_id проверяется на стороне backend (резолвер walmoveWallets смотрит Ledger2.LEDGER2_OPERATION_REGISTRY и отказывает на разные account_id).