mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-21 20:25:51 +00:00
Information Architecture v3 (#1934)
* Update look up escrows to remove redundant info about lookups via sender/destination. Modify cancel expired escrow for brevity. * Cancel escrow: fix notes * Add draft of updated cancel-escrow.js. * Update intro to escrows. * Add Escrow Tutorial * Minor corrections * Fix headings, add HTML * Update escrow docs This commit re-createsf205a92db2with some adjustments: - Omit the accidentally-created dir full of junk - Fix some typos and one mistake in the Escrow limitations section - Add a table to the EscrowCreate ref to clarify valid combos of fields. * Concept info from send-a-time-held-escrow added to escrow.md * IA: Move "Consensus Network" files This re-creates some work from the original commit56fffe0b9f* Rewrite escrows article (re-created) This commit re-creates relevant work from the following commits:9a4a588f2bUpdate escrow.md context infoe1b017dc83Remove references to using escrow for interledger payments. * IA: Move "XRPL servers" files This re-creates some work from original commit7611979abf* IA: move "production readiness" files. Re-creates work from the following commit:692438693aMove tutorials to concepts * New intro articles Original commit:56fffe0b9f* IA: Reorg account concepts Re-creates some work from original commit56fffe0b9f* IA: reorg transaction concepts Original commits:9d4eff9940WIP - reorg accounts7611979abfWIP dir. reorg * IA: reorg consensus concepts Original commit:56fffe0b9f* IA: Reorg ledger docs Original commit:56fffe0b9f- Rephrased some details of the section * IA: rename issuing/operational addresses page Original commit:56fffe0b9f* Moving use cases * Fleshing out Use Cases Note, the dactyl-config.yml file has not been fully updated. * Clean up checks conceptual info. * Remove redundant checks use case section Original commit:3c29e9c05e* IA: move Dex under tokens Original commit:d08b3ba7d7* Touch up stablecoin issuer use case (#1856) * Consolidate stablecoin use case * Stablecoin issuer: cleanup progress through sending * Stablecoin issuer: reorg second half (Note: the dactyl-config.yml is not fully reconciled yet) * Move rippled and clio tutorials into infrastructure * Remove link to checks amendement. * Add note to account_objects.md about commandline interface type field. * Merge expiration case with lifecycle section. * Interoperability Use Cases * Add graphics to intro * Move escrow use cases to dedicated page. * Update use case page intros and corresponding concept info. * Clarify meaning of direct XRP payments. * Intro link updates * Payment use cases * Remove some unnecessary links in transactions section Original commit:e6fcf4a4dc* Link cleanup in Tokens section Original commit:9588dd5e70* Touch up 'Configure Peering' section Original commit:fc8f0990b8* Clean up links in accounts section Original commit:3da5fde7a8* Add NFT mkt use case * p2p payments: edits to Wallets * Clean up payments use cases * Refine history description * IA: use case cleanup * IA: reconcile servers, ledgers sections * IA: reconcile payment types, tx, tokens * IA: reconcile accounts section * IA: reconcile infra * IA: Fix most broken links * Full Docs Index: omit from sidebar * IA: fix up most broken links * fix Absolute path link to internal content * Quick updates to Software Ecosystem * Remove some absolute links to internal resources * Fix remaining broken links in JA target * Contributing: tweak formatting * Tutorials: fix some minor issues * remove interop use cases * remove intro image and personal references to dennis * alphabetize-transaction-nav * Remove unused files * Add QS escrow tutorials * IA: move ledgers, consensus protocol files around * IA: update nav for new page hierarchy * reordering of topics under new networks and servers top-nav * Move "Naming" to "What is XRP?" * Update dactyl-config.yml Remove xrp.md from the TOC. * Update list-xrp-as-an-exchange.md Update link to what-is-xrp * Update list-xrp-as-an-exchange.ja.md Change link to what-is-xrp * Update currency-formats.md Change link to what-is-xrp * Update currency-formats.ja.md Change link to what-is-xrp * Update cancel-an-expired-escrow.md Change link to what-is-xrp * Update paymentchannelfund.md Change link to what-is-xml * Update look-up-escrows.md Change link to what-is-xrp * Update tokens.md change link to what-is-xrp * Update use-payment-channels.md * Update send-a-time-held-escrow.md Update link to what-is-xml * fix broken links * Update parallel-networks.md Change link to what-is-xml * Update parallel-networks.ja.md * Update invariant-checking.md Remove link to xrp.html * Update invariant-checking.ja.md Remove link to xrp.html * Update transaction-cost.md Change link to what-is-xrp * Update transaction-cost.ja.md Change link to what-is-xrp * Update send-a-conditionally-held-escrow.md Change link to what-is-xrp * Update stablecoin-issuer.md Change link to what-is-xrp * Update tokens.ja.md Change link to what-is-xml * Update autobridging.ja.md Change link to what-is-xrp * Update currency-formats.md update text * reorganize infrastructure nav section * Update currency-formats.md Try removing link altogether. * Update currency-formats.ja.md Remove link to what-is-xrp.html * move commandline usage topic to infrastructure * initial intro rewrite * minor update to language * IA.v3: rm Production Readiness * Delete xrp.md * Update xrp link in snippet * Add redirect for old xrp.html URL * Small edits to 'What is XRP?' article * Add missing imgs * XRP - copy edit per @DennisDawson * restructure tutorials nav and pages * fix broken links * more broken link fixes * Algo trading: 1st draft * Algo trading: notes on taxes * Algo trading: edits per review * algo trading: fix broken link * Ledger structure: rewrite for accuracy and clarity * Update links to removed 'tree format' header * Ledger Structure: Update diagrams * Re-gen CSS for ledger structure changes * Ledger structure: edits per review * IA.v3: fix broken NFT links introduced by rebase * Desktop Wallet (py): update little stuff * Update some capacity/storage details * contribute doc nav update * fix image link in create diagram page * IAv3: Fix 'Ledgers' blurb * Update full history requirements with details from community members * add reviewer suggestions * Edits per @trippled review * Apply suggestions from peer review Co-authored-by: oeggert <117319296+oeggert@users.noreply.github.com> * FH: reword file size limit note per review * Update software ecosystem * updates per review * Minor tweaks to graphics * fixTypos * Update content/concepts/introduction/software-ecosystem.md Co-authored-by: Amarantha Kulkarni <amarantha-k@users.noreply.github.com> * Update content/concepts/introduction/software-ecosystem.md Co-authored-by: Amarantha Kulkarni <amarantha-k@users.noreply.github.com> * [JA] update AccountDelete cost * custom transactors doc * add doc to dactyl config * [JA] fix NonFungibleTokensV1_1 amendment status * [JA] update NFTokenOffer page * Remove old, unused XRP article (#2039) * add reviewer suggestions * Add tooling to check for file/nav consistency - From the repo top, run tool/check_file_consistency.py to look for Markdown files that exist in the "content/" directory but aren't used in the documentation. - New "enforce_filenames" filter prints a warning to console when building, if a file's path and filename don't match expectations based on its place in the nav and top heading. * File consistency checker: correctly handle filenames starting in _ * Remove unused old 'get started' and associated code * Create Resources section & reorg some files - Rename some files/folders based on their place in the nav - Move a bunch of non-documentation stuff, and docs on contributing code and/or docs to the new "Resources" section. - Known issue: nav spills into a second row on page widths between 993px-1110px. To be fixed in a later CSS update, maybe along with making the Resources dropdown multi-column. * Fix #2078 code tab bug CSS not built yet, to reduce merge conflicts. Won't have any effect until that happens. * fix Transaction JSON * [JA] translate contributing contents * fix contributing-to-documentation parent * fix contribute-code blurb * Top nav: add cols for Resources, fix broken links * CSS: fix top nav overflows * Fix broken link from redirect not in JA target * Top nav: add Infra to article types * Update contrib info & rename intro file * [ja] Update link to suggested first page to translate * [ja] fix contribute docs organization * Run private network with docker tutorial (#2065) * [NO-ISSUE] Run private network with docker tutorial Adds a tutorial page in the Infrastructure section on how to run a private XRPL network with Docker. Please let me know if you think this is a useful page to include for developers, whether the steps are clear or not, and if you have suggestions on what can be added to it. * Add minor link fixes and Japanese target * Apply suggestions from code review Co-authored-by: Amarantha Kulkarni <amarantha-k@users.noreply.github.com> * Add link to ripple-docker-testnet setup scripts in See Also section * Update repo URL --------- Co-authored-by: Amarantha Kulkarni <amarantha-k@users.noreply.github.com> * add intro gfx (#2036) * add intro gfx * Move graphic up * Update some graphics with their revised versions * Add updated version of the custodial vs non-custodial graphic --------- Co-authored-by: Amarantha Kulkarni <amarantha-k@users.noreply.github.com> Co-authored-by: Amarantha Kulkarni <akulkarni@ripple.com> * Update to reflect current UNL publishers * [ja] update contributing Co-authored-by: tequ <git@tequ.dev> * Incorporate feedback on "What is XRP" page. (#2099) * Add trademark info for XRP * Revert section to previous state * Fix broken link (#2101) --------- Co-authored-by: Oliver Eggert <oeggert@ripple.com> Co-authored-by: ddawson <dennis.s.dawson@gmail.com> Co-authored-by: Maria Shodunke <mshodunke@ripple.com> Co-authored-by: tequ <git@tequ.dev> Co-authored-by: oeggert <117319296+oeggert@users.noreply.github.com> Co-authored-by: Amarantha Kulkarni <amarantha-k@users.noreply.github.com> Co-authored-by: develoQ <develoQ.jp@gmail.com> Co-authored-by: Maria Shodunke <maria-robobug@users.noreply.github.com> Co-authored-by: Amarantha Kulkarni <akulkarni@ripple.com>
This commit is contained in:
94
content/resources/contribute-code/contribute-code.ja.md
Normal file
94
content/resources/contribute-code/contribute-code.ja.md
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
html: contribute-code.html
|
||||
parent: resources.html
|
||||
blurb: XRP Ledgerプロトコルにどのように機能を実装するかを学びます。
|
||||
labels:
|
||||
- ブロックチェーン
|
||||
---
|
||||
# コードへの貢献
|
||||
|
||||
XRP Ledgerを動かすソフトウェアはオープンソースです。誰でもダウンロードし、変更し、拡張し、調査することができます。もしあなたがコードに貢献したいのであれば、コミュニティと協力してあなたの変更の仕様を定義し、XRP Ledgerのプロトコルとブロックチェーンの一部になる前にコードをテストすることが重要です。
|
||||
|
||||
# コアサーバのソースコード
|
||||
|
||||
XRP Ledgerを動かすソフトウェアはオープンソースです。コミュニティが参加することで、より良いものが生まれます。[ドキュメント](docs.html)内の"[Source]"リンクから関連するソースコードに直接ジャンプしたり、GitHubでソースコードを閲覧することができます:
|
||||
|
||||
| XRP Ledger ソースコード | |
|
||||
|:-----------------------|:----------------------------------------------------|
|
||||
| リポジトリ | <https://github.com/XRPLF/rippled> |
|
||||
| ライセンス | [Multiple; ISC (permissive)](https://github.com/XRPLF/rippled/blob/develop/LICENSE.md) |
|
||||
| プログラム言語 | C++ |
|
||||
|
||||
何から始めたらいいか分からないという方のために、Dev Null Productionsは、XRP Ledgerサーバー(`rippled`)のコア実装の仕組みや機能を説明した、詳細かつ充実した[**ソースコード・ガイド**](https://xrpintel.com/source)を提供しています。
|
||||
|
||||
|
||||
## XRP Ledgerの規格
|
||||
|
||||
`rippled`に対する変更はXRP Ledger Standard (XLS)、つまり変更の仕様を特定し詳細に記述した文書によって管理されます。開発にコミットする前に、[XRPL-Standardsリポジトリ](https://github.com/XRPLF/XRPL-Standards/discussions)で議論を始める必要があります。これにより、コミュニティはあなたの変更に関して議論し、フィードバックを提供する機会を得ることができます。
|
||||
|
||||
**注記:*** バグ修正にはXLSは必要ありませんが、Amendmentが必要になる場合があります。
|
||||
|
||||
XLSの作成には独自のプロセスがありますが、簡単にまとめると次のようになります:
|
||||
|
||||
1. ディスカッションを開始し、フィードバックを集めます。
|
||||
2. StandardリポジトリにXLSドラフトを作成します。
|
||||
3. XLSドラフトを仕様候補として公開します。
|
||||
|
||||
詳細については、[XLS貢献ガイド](https://github.com/XRPLF/XRPL-Standards/blob/master/CONTRIBUTING.md) をご覧ください。
|
||||
|
||||
|
||||
## Amendmentの実装
|
||||
|
||||
XLSドラフトを作成した後、その変更にAmendmentが必要かどうかを判断する必要があります。特に次のような**トランザクション処理**に影響する変更にはAmendment が必要です。
|
||||
|
||||
- レジャールールを変更し、異なる結果をもたらすもの。
|
||||
- トランザクションの追加または削除。
|
||||
- コンセンサスへの影響がある変更。
|
||||
|
||||
**注記:** 変更にAmendmentが必要ない場合、そのままコーディングとデプロイに進むことができます。
|
||||
|
||||
コードをAmendmentとして実装するには、次のファイルにAmendment情報を追加する必要があります。
|
||||
|
||||
- **Feature.cpp**:
|
||||
|
||||
開発が完了するまで、`Supported`パラメータは`no`に設定してください。
|
||||
|
||||
バグの修正の場合、`DefaultVote`パラメータを`yes`に設定する必要があります。
|
||||
|
||||
- **Feature.h**: `numFeatures` カウンタを増やし、`extern uint256 const` 変数を宣言します。
|
||||
|
||||
|
||||
## コーディングとデプロイ
|
||||
|
||||
一般的な開発プロセスは以下の通りです。
|
||||
|
||||
1. コードを開発するためにはまず、[`rippled` リポジトリ](https://github.com/XRPLF/rippled) をフォークまたはブランチを作成します。
|
||||
|
||||
**ヒント:** 何から始めたらいいかわからない場合は、_Dev Null Productions_ が詳細かつ充実した [`rippled` ソースコードガイド](https://xrpintel.com/source) を提供しています。
|
||||
|
||||
2. 単体テストと統合テストを実行します。独立した環境で作業をテストするにはスタンドアロンモードでサーバを実行するのが良いでしょう。
|
||||
|
||||
3. `XRPLF:develop`にプルリクエストを作成します。
|
||||
|
||||
**Amendment向けの注記:** **Feature.cpp**の`Supported`パラメータを`yes`に更新します。
|
||||
|
||||
4. プルリクエストがXRP Ledgerのメンテナによって承認されると、あなたのコードは`develop`にマージされ、Devnet上で追加のテストを行うことができます。
|
||||
|
||||
**Amendment向けの注記:**
|
||||
- `DefaultVote`パラメータはロックされます。
|
||||
- もしAmendmentに問題が見つかれば、Amendmentの修正と新しいPRの提出を再度行う必要があります。新しいPRでは`DefaultVote`を変更することができます。
|
||||
|
||||
年に4回、`develop`で承認されたPRからリリース候補がビルドされます。このパッケージはTestnetとMainnet上のいくつかのノードにデプロイされます。リリース候補に問題がなければ、コードは`master`にマージされ、メインネット上のノードはこのビルドにアップグレードできます。
|
||||
|
||||
6. 新しいAmendmentは合意形成プロセスを経て、バリデーターがそのAmendmentを有効にするかどうかを投票します。
|
||||
|
||||
|
||||
## コードのフローチャート
|
||||
|
||||

|
||||
|
||||
|
||||
## 関連項目
|
||||
|
||||
- **コンセプト:**
|
||||
- [Amendment](amendments.html)
|
||||
94
content/resources/contribute-code/contribute-code.md
Normal file
94
content/resources/contribute-code/contribute-code.md
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
html: contribute-code.html
|
||||
parent: resources.html
|
||||
blurb: Learn how features can be coded into the XRP Ledger protocol.
|
||||
labels:
|
||||
- Blockchain
|
||||
---
|
||||
# Contribute Code
|
||||
|
||||
The software that powers the XRP Ledger is open source. Anyone can download, modify, extend, or explore it. If you want to contribute code, it's important to work with the community to define the specifications of your changes and test the code before it becomes a part of the XRP Ledger protocol and blockchain.
|
||||
|
||||
## Core Server Source
|
||||
|
||||
The software that powers the XRP Ledger is open-source, so anyone can download, modify, extend, or explore it. Community involvement makes it better. Look for "[Source]" links in the [documentation](docs.html) to jump directly into the related source code, or browse the source code on GitHub:
|
||||
|
||||
| XRP Ledger Source Code | |
|
||||
|:-----------------------|:----------------------------------------------------|
|
||||
| Repository | <https://github.com/XRPLF/rippled> |
|
||||
| License | [Multiple; ISC (permissive)](https://github.com/ripple/rippled/blob/develop/LICENSE.md) |
|
||||
| Programming Language | C++ |
|
||||
|
||||
If you're not sure where to start, Dev Null Productions provides a detailed and thorough [**Source Code Guide**](https://xrpintel.com/source) that describes the structure and functions of the core XRP Ledger server (`rippled`) implementation.
|
||||
|
||||
|
||||
## XRP Ledger Standards
|
||||
|
||||
Changes to `rippled` are tracked by an XRP Ledger Standard (XLS), a document that identifies and details the specifications of a change. Before committing to development, you must start a discussion in the [XRPL-Standards repo](https://github.com/XRPLF/XRPL-Standards/discussions). This provides the community a chance to discuss and provide feedback about your change.
|
||||
|
||||
**Note:** Bug fixes don't require an XLS, but may require an amendment.
|
||||
|
||||
Creating an XLS has its own process, but can be summarized as:
|
||||
|
||||
1. Start a discussion and gather feedback.
|
||||
2. Create an XLS draft in the standards repo.
|
||||
3. Publishing the XLS draft as a Candidate Specification.
|
||||
|
||||
For details, see the [XLS contributing guide](https://github.com/XRPLF/XRPL-Standards/blob/master/CONTRIBUTING.md).
|
||||
|
||||
|
||||
## Amendment Implementation
|
||||
|
||||
After you've created an XLS draft, you now need to determine if your change requires an amendment. Changes that affect **transaction processing** require amendments, specifically changes that:
|
||||
|
||||
- Modify ledger rules, resulting in different outcomes.
|
||||
- Add or remove transactions.
|
||||
- Affect consensus.
|
||||
|
||||
**Note:** If your change doesn't need an amendment, you can go straight to coding and deployment.
|
||||
|
||||
Implementing code as an amendment requires you to add the amendment to these files:
|
||||
|
||||
- **Feature.cpp**:
|
||||
|
||||
`Supported` parameter should be set to `no` until development is complete.
|
||||
|
||||
`DefaultVote` parameter should be set to `yes` for bug fixes; everything else defaults to `no`.
|
||||
|
||||
- **Feature.h**: Increment the `numFeatures` counter and declare an `extern uint256 const` variable.
|
||||
|
||||
|
||||
## Coding and Deployment
|
||||
|
||||
The general development path breaks down as follows:
|
||||
|
||||
1. Create a fork or branch in the [`rippled` repository](https://github.com/XRPLF/rippled) to develop your code.
|
||||
|
||||
**Tip:** If you're not sure where to start, _Dev Null Productions_ provides a detailed and thorough [`rippled` Source Code Guide](https://xrpintel.com/source).
|
||||
|
||||
2. Run unit and integration tests. Running a server in _stand-alone mode_ is useful for testing your changes in an isolated environment, but you may want to stand up a private network for extensive changes.
|
||||
|
||||
3. Create a pull request on `XRPLF:develop`.
|
||||
|
||||
**Note for Amendments:** Update the `Supported` paramter to `yes` in **Feature.cpp**.
|
||||
|
||||
4. After the pull request is approved by XRP Ledger maintainers, your code is merged into `develop` and additional testing can be done on Devnet.
|
||||
|
||||
**Note for Amendments:**
|
||||
- The `DefaultVote` parameter is now locked.
|
||||
- If problems are found with the amendment, you must restart the process of making fixes and submitting a new PR. You can change the default vote in the new PR.
|
||||
|
||||
5. On a quarterly basis, a release candidate is built from approved PRs on `develop`. The package is deployed to Testnet and a few nodes on Mainnet. If no issues are found with the release candidate, the code is merged into `master` and nodes on Mainnet can upgrade to this build.
|
||||
|
||||
6. New amendments go through the consensus process and validators vote on whether to enable them.
|
||||
|
||||
|
||||
## Code Flowchart
|
||||
|
||||

|
||||
|
||||
|
||||
## See Also
|
||||
|
||||
- **Concepts:**
|
||||
- [Amendments](amendments.html)
|
||||
@@ -0,0 +1,389 @@
|
||||
---
|
||||
html: create-custom-transactors.html
|
||||
parent: contribute-code.html
|
||||
blurb: XRPレジャーとやり取りするためのカスタムトランザクタを作成します。
|
||||
labels:
|
||||
- 開発
|
||||
- ブロックチェーン
|
||||
---
|
||||
# カスタムトランザクタの作成
|
||||
|
||||
_トランザクタ_ はトランザクションを処理し、XRP Ledgerを変更するコードです。カスタムトランザクタを作成することで、`rippled`に新しい機能を追加することができます。このチュートリアルではトランザクタのコーディングについて説明しますが、それをXRPLに追加するにはAmendmentプロセスを経る必要があります。 [XRPレジャーのコードへの貢献](contribute-code-flow.html)をご覧ください。
|
||||
|
||||
トランザクタは 基本的な処理順序に則って処理されます。
|
||||
|
||||
1. シリアライズ型レジャーエントリ(SLE/serialized type ledger entry)の _view_ へアクセスします。
|
||||
2. _view_ 内の値を更新、削除、挿入します。
|
||||
3. 確定した変更を _view_ からレジャーに適用します。
|
||||
|
||||
**注記:** _view_ はレジャーのサンドボックスです。トランザクタは必要なエラーチェックと変更のすべてをサンドボックス内で行い、レジャーでは直接行いません。値が確定した後、変更はレジャーにアトミックに適用されます。
|
||||
|
||||
このチュートリアルでは、既存の`CreateCheck`トランザクションを例として使用します。ソースファイルはここで確認できます。
|
||||
|
||||
- [ヘッダファイル](https://github.com/XRPLF/rippled/blob/master/src/ripple/app/tx/impl/CreateCheck.h)
|
||||
- [CPPファイル](https://github.com/XRPLF/rippled/blob/master/src/ripple/app/tx/impl/CreateCheck.cpp)
|
||||
|
||||
|
||||
## ヘッダファイル
|
||||
|
||||
次の形式でヘッダーファイルを作成します。
|
||||
|
||||
```c++
|
||||
namespace ripple {
|
||||
|
||||
class CreateCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CreateCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
```
|
||||
|
||||
`ApplyContext`でトランザクタを初期化すると、トランザクタは以下にアクセスできます:
|
||||
|
||||
- トランザクタをトリガーしたトランザクション。
|
||||
- SLEのビュー。
|
||||
- エラーを記録するためのジャーナル。
|
||||
|
||||
|
||||
## CPPファイル
|
||||
|
||||
### 1. `preflight`関数の追加
|
||||
|
||||
`preflight`関数はレジャーにアクセスする前にトランザクション自体にエラーがないかチェックします。無効なトランザクションや正しく設定されていないトランザクションは拒否されなければなりません。
|
||||
|
||||
- `PreflightContext`はレジャーのビューを持っていません。
|
||||
- レジャーやトランザクションからフィールドを取得するには、次のようにブラケット記法を使用します。
|
||||
|
||||
auto const curExpiration = (*sle*)[~sfExpiration];
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + reqDelta;
|
||||
|
||||
**注記:** `~`記号は optional型を返します。
|
||||
|
||||
- レジャーとトランザクションのスキーマはこちらから確認できます。
|
||||
- [`LedgerFormats.cpp`](https://github.com/XRPLF/rippled/blob/master/src/ripple/protocol/impl/LedgerFormats.cpp)
|
||||
- [`TxFormats.cpp`](https://github.com/XRPLF/rippled/blob/master/src/ripple/protocol/impl/TxFormats.cpp)
|
||||
|
||||
-` rippled` はトランザクションの結果を結果コードで表します。[トランザクションの結果](transaction-results.html)をご覧ください。
|
||||
|
||||
```c++
|
||||
CreateCheck::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
// Check if this amendment functionality is enabled on the network.
|
||||
if (!ctx.rules.enabled(featureChecks))
|
||||
return temDISABLED;
|
||||
|
||||
NotTEC const ret{preflight1(ctx)};
|
||||
if (!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (ctx.tx.getFlags() & tfUniversalMask)
|
||||
{
|
||||
// There are no flags (other than universal) for CreateCheck yet.
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
|
||||
{
|
||||
// They wrote a check to themselves.
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Check to self.";
|
||||
return temREDUNDANT;
|
||||
}
|
||||
|
||||
{
|
||||
STAmount const sendMax{ctx.tx.getFieldAmount(sfSendMax)};
|
||||
if (!isLegalNet(sendMax) || sendMax.signum() <= 0)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: bad sendMax amount: "
|
||||
<< sendMax.getFullText();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
|
||||
if (badCurrency() == sendMax.getCurrency())
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Bad currency.";
|
||||
return temBAD_CURRENCY;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const optExpiry = ctx.tx[~sfExpiration])
|
||||
{
|
||||
if (*optExpiry == 0)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: bad expiration";
|
||||
return temBAD_EXPIRATION;
|
||||
}
|
||||
}
|
||||
|
||||
return preflight2(ctx);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 2. `preclaim`関数の追加
|
||||
|
||||
`preclaim`関数は、現在のレジャーの情報を見る必要があるエラーをチェックします。
|
||||
|
||||
- このステップが結果コード`tesSUCCESS`または`tec`を返した場合、トランザクションはキューに入れられ、ピアに送信されます。
|
||||
|
||||
```c++
|
||||
CreateCheck::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const dstId{ctx.tx[sfDestination]};
|
||||
|
||||
// Use the `keylet` function to get the key of the SLE. Views have either `read` or `peek` access.
|
||||
// `peek` access allows the developer to modify the SLE returned.
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
if (!sleDst)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Destination account does not exist.";
|
||||
return tecNO_DST;
|
||||
}
|
||||
|
||||
auto const flags = sleDst->getFlags();
|
||||
|
||||
// Check if the destination has disallowed incoming checks
|
||||
if (ctx.view.rules().enabled(featureDisallowIncoming) &&
|
||||
(flags & lsfDisallowIncomingCheck))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if ((flags & lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: DestinationTag required.";
|
||||
return tecDST_TAG_NEEDED;
|
||||
}
|
||||
|
||||
{
|
||||
STAmount const sendMax{ctx.tx[sfSendMax]};
|
||||
if (!sendMax.native())
|
||||
{
|
||||
// The currency may not be globally frozen
|
||||
AccountID const& issuerId{sendMax.getIssuer()};
|
||||
if (isGlobalFrozen(ctx.view, issuerId))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Creating a check for frozen asset";
|
||||
return tecFROZEN;
|
||||
}
|
||||
// If this account has a trustline for the currency, that
|
||||
// trustline may not be frozen.
|
||||
//
|
||||
// Note that we DO allow create check for a currency that the
|
||||
// account does not yet have a trustline to.
|
||||
AccountID const srcId{ctx.tx.getAccountID(sfAccount)};
|
||||
if (issuerId != srcId)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
auto const sleTrust = ctx.view.read(
|
||||
keylet::line(srcId, issuerId, sendMax.getCurrency()));
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag(
|
||||
(issuerId > srcId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Creating a check for frozen trustline.";
|
||||
return tecFROZEN;
|
||||
}
|
||||
}
|
||||
if (issuerId != dstId)
|
||||
{
|
||||
// Check if dst froze the line.
|
||||
auto const sleTrust = ctx.view.read(
|
||||
keylet::line(issuerId, dstId, sendMax.getCurrency()));
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag(
|
||||
(dstId > issuerId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Creating a check for destination frozen trustline.";
|
||||
return tecFROZEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasExpired(ctx.view, ctx.tx[~sfExpiration]))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Creating a check that has already expired.";
|
||||
return tecEXPIRED;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Add a `doApply()` function.
|
||||
|
||||
The `doApply()` function has read/write access, enabling you to modify the ledger.
|
||||
|
||||
```c++
|
||||
CreateCheck::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
return tefINTERNAL;
|
||||
|
||||
// A check counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (mPriorBalance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// Check sequence. For more explanation see comments in SeqProxy.h.
|
||||
std::uint32_t const seq = ctx_.tx.getSeqProxy().value();
|
||||
Keylet const checkKeylet = keylet::check(account_, seq);
|
||||
auto sleCheck = std::make_shared<SLE>(checkKeylet);
|
||||
|
||||
sleCheck->setAccountID(sfAccount, account_);
|
||||
AccountID const dstAccountId = ctx_.tx[sfDestination];
|
||||
sleCheck->setAccountID(sfDestination, dstAccountId);
|
||||
sleCheck->setFieldU32(sfSequence, seq);
|
||||
sleCheck->setFieldAmount(sfSendMax, ctx_.tx[sfSendMax]);
|
||||
if (auto const srcTag = ctx_.tx[~sfSourceTag])
|
||||
sleCheck->setFieldU32(sfSourceTag, *srcTag);
|
||||
if (auto const dstTag = ctx_.tx[~sfDestinationTag])
|
||||
sleCheck->setFieldU32(sfDestinationTag, *dstTag);
|
||||
if (auto const invoiceId = ctx_.tx[~sfInvoiceID])
|
||||
sleCheck->setFieldH256(sfInvoiceID, *invoiceId);
|
||||
if (auto const expiry = ctx_.tx[~sfExpiration])
|
||||
sleCheck->setFieldU32(sfExpiration, *expiry);
|
||||
|
||||
view().insert(sleCheck);
|
||||
|
||||
auto viewJ = ctx_.app.journal("View");
|
||||
// If it's not a self-send (and it shouldn't be), add Check to the
|
||||
// destination's owner directory.
|
||||
if (dstAccountId != account_)
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(dstAccountId),
|
||||
checkKeylet,
|
||||
describeOwnerDir(dstAccountId));
|
||||
|
||||
JLOG(j_.trace()) << "Adding Check to destination directory "
|
||||
<< to_string(checkKeylet.key) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
|
||||
sleCheck->setFieldU64(sfDestinationNode, *page);
|
||||
}
|
||||
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(account_),
|
||||
checkKeylet,
|
||||
describeOwnerDir(account_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding Check to owner directory "
|
||||
<< to_string(checkKeylet.key) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
|
||||
sleCheck->setFieldU64(sfOwnerNode, *page);
|
||||
}
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
adjustOwnerCount(view(), sle, 1, viewJ);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 追加の関数
|
||||
|
||||
必要に応じて、カスタムトランザクタにヘルパー関数を追加することができます。特殊な場合に役立つ特別な関数がいくつかあります。
|
||||
|
||||
|
||||
### `calculateBaseFee`
|
||||
|
||||
ほとんどのトランザクションはデフォルトの[Referenceトランザクションコスト](transaction-cost.html)をそのまま引き継ぎます。しかし、トランザクションで通常以外のトランザクションコストを定義する必要がある場合、トランザクションの`calculateBaseFee`メソッドをカスタムメソッドに置き換えることができます。
|
||||
|
||||
次の例では、`EscrowFinish`ランザクションが条件付きエスクローに対して、フルフィルメントの大きさに応じて追加コストを請求する方法を示しています。
|
||||
|
||||
```c++
|
||||
XRPAmount
|
||||
EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
XRPAmount extraFee{0};
|
||||
|
||||
if (auto const fb = tx[~sfFulfillment])
|
||||
{
|
||||
extraFee += view.fees().base * (32 + (fb->size() / 16));
|
||||
}
|
||||
|
||||
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### `makeTxConsequences`
|
||||
|
||||
`rippled`は[`TxConsequences`](https://github.com/XRPLF/rippled/blob/master/src/ripple/app/tx/applySteps.h#L41-L44)クラスを使用して、トランザクション適用時のアカウントへの結果を記述します。このクラスは手数料、使用可能な最大XRP、トランザクションによって消費されたシーケンス番号の数を追跡します。結果には次の3つのタイプがあります。
|
||||
|
||||
- **ノーマル:**トランザクションは署名に影響を与えず、XRP手数料を消費するのみです。手数料を超えてXRPを消費するトランザクションは正常とはみなされません。
|
||||
- **ブロッカー:**トランザクションの署名に影響を与え、有効なトランザクションがその後ろにキューイングされるのを防ぎます。
|
||||
- **カスタム:**トランザクタは結果を決定するために追加の作業を行う必要があります。
|
||||
|
||||
`makeTxConsequences`関数を使うと、以下のような状況に対してカスタム結果を作成することができます:
|
||||
|
||||
- XRPを送信する支払い。
|
||||
- 複数のシーケンス番号を消費するチケット。
|
||||
- 設定されたフラグやフィールドによって、正常またはブロッカーとなるトランザクション。
|
||||
|
||||
**注記:** `TxConsequences`は[トランザクションキュー](transaction-queue.html)にのみ影響します。トランザクションがレジャーに適用されたときに手数料を請求する可能性が高い場合、それはピアに送信されます。手数料を請求する可能性がない場合、またはそれが判断できない場合は、送信されません。
|
||||
|
||||
|
||||
```c++
|
||||
SetAccount::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
// The SetAccount may be a blocker, but only if it sets or clears
|
||||
// specific account flags.
|
||||
auto getTxConsequencesCategory = [](STTx const& tx) {
|
||||
if (std::uint32_t const uTxFlags = tx.getFlags();
|
||||
uTxFlags & (tfRequireAuth | tfOptionalAuth))
|
||||
return TxConsequences::blocker;
|
||||
|
||||
if (auto const uSetFlag = tx[~sfSetFlag]; uSetFlag &&
|
||||
(*uSetFlag == asfRequireAuth || *uSetFlag == asfDisableMaster ||
|
||||
*uSetFlag == asfAccountTxnID))
|
||||
return TxConsequences::blocker;
|
||||
|
||||
if (auto const uClearFlag = tx[~sfClearFlag]; uClearFlag &&
|
||||
(*uClearFlag == asfRequireAuth || *uClearFlag == asfDisableMaster ||
|
||||
*uClearFlag == asfAccountTxnID))
|
||||
return TxConsequences::blocker;
|
||||
|
||||
return TxConsequences::normal;
|
||||
};
|
||||
|
||||
return TxConsequences{ctx.tx, getTxConsequencesCategory(ctx.tx)};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 次のステップ
|
||||
|
||||
新しいトランザクタでサーバを再コンパイルし、[スタンドアロンモード](use-stand-alone-mode.html)でテストしてください。もしAmendmentの後ろにトランザクタをコーディングした場合、設定ファイルを使ってその機能を[強制的に有効にする](test-amendments.html)ことができます。
|
||||
389
content/resources/contribute-code/create-custom-transactors.md
Normal file
389
content/resources/contribute-code/create-custom-transactors.md
Normal file
@@ -0,0 +1,389 @@
|
||||
---
|
||||
html: create-custom-transactors.html
|
||||
parent: contribute-code.html
|
||||
blurb: Create custom transactors to interact with the XRP Ledger.
|
||||
labels:
|
||||
- Development
|
||||
- Blockchain
|
||||
---
|
||||
# Create Custom Transactors
|
||||
|
||||
A _transactor_ is code that processes a transaction and modifies the XRP Ledger. Creating custom transactors enables you to add new functionality to `rippled`. This tutorial walks through coding transactors, but you'll have to go through the amendment process to add it to XRPL. See: [Contribute Code to the XRP Ledger](contribute-code.html).
|
||||
|
||||
Transactors follow a basic order of operations:
|
||||
|
||||
1. Access a _view_ into a serialized type ledger entry (SLE).
|
||||
2. Update, erase, or insert values in the _view_.
|
||||
3. Apply the finalized changes from the _view_ to the ledger.
|
||||
|
||||
**Note:** _Views_ are sandboxes into ledgers. Transactors make all necessary error checks and changes in sandboxes, not directly on the ledger. After values are finalized, changes are applied atomically to the ledger.
|
||||
|
||||
This tutorial uses the existing `CreateCheck` transactor as an example. You can view the source files here:
|
||||
|
||||
- [Header File](https://github.com/XRPLF/rippled/blob/master/src/ripple/app/tx/impl/CreateCheck.h)
|
||||
- [CPP File](https://github.com/XRPLF/rippled/blob/master/src/ripple/app/tx/impl/CreateCheck.cpp)
|
||||
|
||||
|
||||
## Header File
|
||||
|
||||
Create a header file in this format:
|
||||
|
||||
```c++
|
||||
namespace ripple {
|
||||
|
||||
class CreateCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CreateCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
```
|
||||
|
||||
Initializing the transactor with `ApplyContext` gives it access to:
|
||||
|
||||
- The transaction that triggered the transactor.
|
||||
- A view of the SLE.
|
||||
- A journal to log errors.
|
||||
|
||||
|
||||
## CPP File
|
||||
|
||||
### 1. Add a `preflight` function.
|
||||
|
||||
The `preflight` function checks for errors in the transaction itself before accessing the ledger. It should reject invalid and incorrectly formed transactions.
|
||||
|
||||
- `PreflightContext` doesn't have a view of the ledger.
|
||||
- Use bracket notation to retrieve fields from ledgers and transactions:
|
||||
|
||||
auto const curExpiration = (*sle*)[~sfExpiration];
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + reqDelta;
|
||||
|
||||
**Note:** The `~` symbol returns an optional type.
|
||||
|
||||
- You can view ledger and transaction schemas here:
|
||||
- [`LedgerFormats.cpp`](https://github.com/XRPLF/rippled/blob/master/src/ripple/protocol/impl/LedgerFormats.cpp)
|
||||
- [`TxFormats.cpp`](https://github.com/XRPLF/rippled/blob/master/src/ripple/protocol/impl/TxFormats.cpp)
|
||||
|
||||
- `rippled` summarizes transaction results with result codes. See: [Transaction Results](transaction-results.html)
|
||||
|
||||
```c++
|
||||
CreateCheck::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
// Check if this amendment functionality is enabled on the network.
|
||||
if (!ctx.rules.enabled(featureChecks))
|
||||
return temDISABLED;
|
||||
|
||||
NotTEC const ret{preflight1(ctx)};
|
||||
if (!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (ctx.tx.getFlags() & tfUniversalMask)
|
||||
{
|
||||
// There are no flags (other than universal) for CreateCheck yet.
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
|
||||
{
|
||||
// They wrote a check to themselves.
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Check to self.";
|
||||
return temREDUNDANT;
|
||||
}
|
||||
|
||||
{
|
||||
STAmount const sendMax{ctx.tx.getFieldAmount(sfSendMax)};
|
||||
if (!isLegalNet(sendMax) || sendMax.signum() <= 0)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: bad sendMax amount: "
|
||||
<< sendMax.getFullText();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
|
||||
if (badCurrency() == sendMax.getCurrency())
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: Bad currency.";
|
||||
return temBAD_CURRENCY;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const optExpiry = ctx.tx[~sfExpiration])
|
||||
{
|
||||
if (*optExpiry == 0)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: bad expiration";
|
||||
return temBAD_EXPIRATION;
|
||||
}
|
||||
}
|
||||
|
||||
return preflight2(ctx);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 2. Add a `preclaim` function.
|
||||
|
||||
The `preclaim` function checks for errors that require viewing information on the current ledger.
|
||||
|
||||
- If this step returns a result code of `tesSUCCESS` or any `tec` result, the transaction will be queued and broadcast to peers.
|
||||
|
||||
```c++
|
||||
CreateCheck::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const dstId{ctx.tx[sfDestination]};
|
||||
|
||||
// Use the `keylet` function to get the key of the SLE. Views have either `read` or `peek` access.
|
||||
// `peek` access allows the developer to modify the SLE returned.
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
if (!sleDst)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Destination account does not exist.";
|
||||
return tecNO_DST;
|
||||
}
|
||||
|
||||
auto const flags = sleDst->getFlags();
|
||||
|
||||
// Check if the destination has disallowed incoming checks
|
||||
if (ctx.view.rules().enabled(featureDisallowIncoming) &&
|
||||
(flags & lsfDisallowIncomingCheck))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if ((flags & lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag))
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
JLOG(ctx.j.warn()) << "Malformed transaction: DestinationTag required.";
|
||||
return tecDST_TAG_NEEDED;
|
||||
}
|
||||
|
||||
{
|
||||
STAmount const sendMax{ctx.tx[sfSendMax]};
|
||||
if (!sendMax.native())
|
||||
{
|
||||
// The currency may not be globally frozen
|
||||
AccountID const& issuerId{sendMax.getIssuer()};
|
||||
if (isGlobalFrozen(ctx.view, issuerId))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Creating a check for frozen asset";
|
||||
return tecFROZEN;
|
||||
}
|
||||
// If this account has a trustline for the currency, that
|
||||
// trustline may not be frozen.
|
||||
//
|
||||
// Note that we DO allow create check for a currency that the
|
||||
// account does not yet have a trustline to.
|
||||
AccountID const srcId{ctx.tx.getAccountID(sfAccount)};
|
||||
if (issuerId != srcId)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
auto const sleTrust = ctx.view.read(
|
||||
keylet::line(srcId, issuerId, sendMax.getCurrency()));
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag(
|
||||
(issuerId > srcId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Creating a check for frozen trustline.";
|
||||
return tecFROZEN;
|
||||
}
|
||||
}
|
||||
if (issuerId != dstId)
|
||||
{
|
||||
// Check if dst froze the line.
|
||||
auto const sleTrust = ctx.view.read(
|
||||
keylet::line(issuerId, dstId, sendMax.getCurrency()));
|
||||
if (sleTrust &&
|
||||
sleTrust->isFlag(
|
||||
(dstId > issuerId) ? lsfHighFreeze : lsfLowFreeze))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Creating a check for destination frozen trustline.";
|
||||
return tecFROZEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasExpired(ctx.view, ctx.tx[~sfExpiration]))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Creating a check that has already expired.";
|
||||
return tecEXPIRED;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Add a `doApply()` function.
|
||||
|
||||
The `doApply()` function has read/write access, enabling you to modify the ledger.
|
||||
|
||||
```c++
|
||||
CreateCheck::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
return tefINTERNAL;
|
||||
|
||||
// A check counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
{
|
||||
STAmount const reserve{
|
||||
view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)};
|
||||
|
||||
if (mPriorBalance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// Check sequence. For more explanation see comments in SeqProxy.h.
|
||||
std::uint32_t const seq = ctx_.tx.getSeqProxy().value();
|
||||
Keylet const checkKeylet = keylet::check(account_, seq);
|
||||
auto sleCheck = std::make_shared<SLE>(checkKeylet);
|
||||
|
||||
sleCheck->setAccountID(sfAccount, account_);
|
||||
AccountID const dstAccountId = ctx_.tx[sfDestination];
|
||||
sleCheck->setAccountID(sfDestination, dstAccountId);
|
||||
sleCheck->setFieldU32(sfSequence, seq);
|
||||
sleCheck->setFieldAmount(sfSendMax, ctx_.tx[sfSendMax]);
|
||||
if (auto const srcTag = ctx_.tx[~sfSourceTag])
|
||||
sleCheck->setFieldU32(sfSourceTag, *srcTag);
|
||||
if (auto const dstTag = ctx_.tx[~sfDestinationTag])
|
||||
sleCheck->setFieldU32(sfDestinationTag, *dstTag);
|
||||
if (auto const invoiceId = ctx_.tx[~sfInvoiceID])
|
||||
sleCheck->setFieldH256(sfInvoiceID, *invoiceId);
|
||||
if (auto const expiry = ctx_.tx[~sfExpiration])
|
||||
sleCheck->setFieldU32(sfExpiration, *expiry);
|
||||
|
||||
view().insert(sleCheck);
|
||||
|
||||
auto viewJ = ctx_.app.journal("View");
|
||||
// If it's not a self-send (and it shouldn't be), add Check to the
|
||||
// destination's owner directory.
|
||||
if (dstAccountId != account_)
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(dstAccountId),
|
||||
checkKeylet,
|
||||
describeOwnerDir(dstAccountId));
|
||||
|
||||
JLOG(j_.trace()) << "Adding Check to destination directory "
|
||||
<< to_string(checkKeylet.key) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
|
||||
sleCheck->setFieldU64(sfDestinationNode, *page);
|
||||
}
|
||||
|
||||
{
|
||||
auto const page = view().dirInsert(
|
||||
keylet::ownerDir(account_),
|
||||
checkKeylet,
|
||||
describeOwnerDir(account_));
|
||||
|
||||
JLOG(j_.trace()) << "Adding Check to owner directory "
|
||||
<< to_string(checkKeylet.key) << ": "
|
||||
<< (page ? "success" : "failure");
|
||||
|
||||
if (!page)
|
||||
return tecDIR_FULL;
|
||||
|
||||
sleCheck->setFieldU64(sfOwnerNode, *page);
|
||||
}
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
adjustOwnerCount(view(), sle, 1, viewJ);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Additional Functions
|
||||
|
||||
You can add more helper functions to your custom transactor as necessary. There are a few special functions that are relevant in special cases.
|
||||
|
||||
|
||||
### `calculateBaseFee`
|
||||
|
||||
Most transactions inherit the default [reference transaction cost](transaction-cost.html). However, if your transactor needs to define a non-standard transaction cost, you can replace the transactor's `calculateBaseFee` method with a custom one.
|
||||
|
||||
The following example shows how `EscrowFinish` transactions charge an additional cost on conditional escrows based on the size of the fulfillment:
|
||||
|
||||
```c++
|
||||
XRPAmount
|
||||
EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
XRPAmount extraFee{0};
|
||||
|
||||
if (auto const fb = tx[~sfFulfillment])
|
||||
{
|
||||
extraFee += view.fees().base * (32 + (fb->size() / 16));
|
||||
}
|
||||
|
||||
return Transactor::calculateBaseFee(view, tx) + extraFee;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### `makeTxConsequences`
|
||||
|
||||
`rippled` uses a [`TxConsequences`](https://github.com/XRPLF/rippled/blob/master/src/ripple/app/tx/applySteps.h#L41-L44) class to describe the outcome to an account when applying a transaction. It tracks the fee, maximum possible XRP spent, and how many sequence numbers are consumed by the transaction. There are three types of consequences:
|
||||
|
||||
- **Normal:** The transactor doesn't affect transaction signing and _only_ consumes an XRP fee. Transactions that spend XRP beyond the fee aren't considered normal.
|
||||
- **Blocker:** The transactor affects transaction signing, preventing valid transactions from queueing behind it.
|
||||
- **Custom:** The transactor needs to do additional work to determination consequences.
|
||||
|
||||
The `makeTxConsequences` function enables you to create custom consequences for situations such as:
|
||||
|
||||
- Payments sending XRP.
|
||||
- Tickets consuming more than one sequence number.
|
||||
- Transactions that are normal or blockers, depending on flags or fields set.
|
||||
|
||||
**Note:** `TxConsequences` only affects the [transaction queue](transaction-queue.html). If a transaction is likely to claim a fee when applied to the ledger, it will be broadcast to peers. If it's not likely to claim a fee, or that can't be determined, it won't be broadcast.
|
||||
|
||||
|
||||
```c++
|
||||
SetAccount::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
// The SetAccount may be a blocker, but only if it sets or clears
|
||||
// specific account flags.
|
||||
auto getTxConsequencesCategory = [](STTx const& tx) {
|
||||
if (std::uint32_t const uTxFlags = tx.getFlags();
|
||||
uTxFlags & (tfRequireAuth | tfOptionalAuth))
|
||||
return TxConsequences::blocker;
|
||||
|
||||
if (auto const uSetFlag = tx[~sfSetFlag]; uSetFlag &&
|
||||
(*uSetFlag == asfRequireAuth || *uSetFlag == asfDisableMaster ||
|
||||
*uSetFlag == asfAccountTxnID))
|
||||
return TxConsequences::blocker;
|
||||
|
||||
if (auto const uClearFlag = tx[~sfClearFlag]; uClearFlag &&
|
||||
(*uClearFlag == asfRequireAuth || *uClearFlag == asfDisableMaster ||
|
||||
*uClearFlag == asfAccountTxnID))
|
||||
return TxConsequences::blocker;
|
||||
|
||||
return TxConsequences::normal;
|
||||
};
|
||||
|
||||
return TxConsequences{ctx.tx, getTxConsequencesCategory(ctx.tx)};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
Re-compile the server with your new transactor and test it in [stand-alone mode](use-stand-alone-mode.html). If you coded the transactor behind an amendment, you can [force-enable](test-amendments.html) the feature using the config file.
|
||||
Reference in New Issue
Block a user