layout: true
--- class: center, middle # Introduction to Ripple APIs by d10 --- hook: confirmFaucetResponse # Ripple APIs Overview ## Connect to `rippled` ## Query Ledger Data ## Write Ledger Data (Transactions) ``` ``` Complete documentation: [ripple.com/build](https://www.ripple.com) --- hook: showFaucetResponse # Before We Begin... We will need XRP for API examples that follow. We will use the Ripple *Test Net*, where we can allocate ourselves some test XRP. Unlike XRP on the live network, Test Net funds have no real value. The Test Net ledger and balances are reset frequently. --- hook: showFaucetResponse # Test Net XRP A Test Net account can be generated manually on https://ripple.com/build/ripple-test-net/, or programatically via an HTTP request: ```bash curl -s -X POST https://faucet.altnet.rippletest.net/accounts ``` Example response: .faucetResponse.preformatted[ ... ] --- hook: showFaucetResponse # Test Net XRP .faucetResponse.preformatted[ ... ] The response gives a unique *address* for the account: .faucetResponse-address.preformatted[address] And a *secret* that will allow us to transact with this account. The address is derived from a *public key*. The secret is derived from the corresponding *private key*. The *balance* indicates that the account was initially funded with 10,000 XRP. Remember that on the **Test Net XRP have no real value**. --- hook: showFaucetResponse # Test Net XRP .faucetResponse.preformatted[ ... ] **Never show, or share, your secret key!** We are showing one here for a *Test Net* account, which holds *no real value*. --- # Ripple APIs Overview ## **Connect to `rippled`** ## Query Ledger Data ## Write Ledger Data (Transactions) --- # Connect to `rippled` `server_info` is a `rippled` *command*. Response confirms that an application can reach `rippled`. Also indicates whether the `rippled` server is in sync with the rest of the network. `rippled` offers three ways to invoke commands such as `server_info`: - Command line - JSON-RPC - WebSocket JavaScript applications can use [RippleAPI](https://ripple.com/build/rippleapi), a library which wraps `rippled`'s websocket interface. --- # Connect to `rippled` ## Command line `rippled` features are available through a *command line* interface, when you are running an instance of `rippled` on your local machine. To invoke the `server_info` command, for example: ```bash rippled server_info ``` --- hook: showRippledURL # Connect to `rippled` ## JSON-RPC Applications may also POST *Remote Procedure Calls* (RPC) to `rippled`. ```bash curl -s -X POST "$RIPPLED_RPC" -d @- <<-END { "method": "server_info", "params": [ {} ] } END ``` --- # Connect to `rippled` ## Websocket *Websockets* allow applications to submit multiple requests over a long-lived connection. Requests may receive multiple responses, or subscribe to events. To invoke `server_info`, POST: ```json { "id": 1, "command": "server_info" } ``` The response will include the same `id` that was passed in. Your application should increase that counter with each subsequent request. --- # Connect to `rippled` ## RippleAPI This [*boilerplate*](https://ripple.com/build/rippleapi/#boilerplate) shows the [`connect()`](https://ripple.com/build/rippleapi/#connect) method. ```javascript const {RippleAPI} = require('ripple-lib'); const api = new RippleAPI({ server: 'wss://s.altnet.rippletest.net:51233' }); api.connect().then(() => { /* insert code here */ }).then(() => { return api.disconnect(); }).catch(console.error); ``` After `connect()`, other methods may be invoked. --- # Connect to `rippled` ## RippleAPI [getServerInfo()](https://ripple.com/build/rippleapi/#getserverinfo) in `ripple-lib` corresponds to `rippled`'s [`server_info`](https://ripple.com/build/rippled-apis/#server-info). ```javascript api.connect().then(() => { // Request server_info, log response... api.getServerInfo().then(info => { console.log(info); }); }); ``` --- hook: showServerInfo # Connect to `rippled` ## `getServerInfo()` response .serverInfo.preformatted[{ server_info }] JavaScript response shown, `server_info` command returns similar information. *(portions of response omitted for brevity)* --- hook: showServerInfo # Connect to `rippled` ## `getServerInfo()` response .serverInfo.preformatted[{ server_info }] Fields including `completeLedgers`, `peers`, and `serverState` make `server_info` useful when troubleshooting `rippled` issues. --- # Ripple APIs Overview ## Connect to `rippled` ## **Query Ledger Data** ## Write Ledger Data (Transactions) --- # Query Ledger Data The [`account_info`](https://ripple.com/build/rippled-apis/#account-info) command retrieves information about an account, its activity, and its XRP balance. Most commands, including `account_info`, can be invoked via `rippled` webSockets or JSON-RPC, or via JavaScript. --- hook: showHotAccountInfo showRippledURL # Query Ledger Data ## [`account_info`](https://ripple.com/build/rippled-apis/#account-info) by JSON-RPC ```bash curl -s -X POST "$RIPPLED_RPC" -d @- <<-END { "method": "account_info", "params": [{ "account": "$ADDRESS" }] } END ``` --- hook: showHotAccountInfo # Query Ledger Data ## [`getAccountInfo()`](https://ripple.com/build/rippleapi/#getaccountinfo) by JavaScript ```javascript api.connect().then(() => { // Request account_info, log response... api.getAccountInfo("$ADDRESS").then(info => { console.log(info); }); }); ``` --- hook: showHotAccountInfo # Query Ledger Data ## [`getAccountInfo()`](https://ripple.com/build/rippleapi/#getaccountinfo) by JavaScript .hotAccountInfo.preformatted[{account_info}] *sequence* orders transactions submitted by this account. *xrpBalance* shows XRP owned by this account. **Note:** JavaScript returns `"xrpBalance"` in *XRP*. Other APIs return `"Balance"` in *drops*. ``` 1 XRP == 1,000,000 drops ``` --- # Query Ledger Data We've seen that `server_info` and `account_info` are *commands* which return information about the Ripple network. There are many more commands, which are invoked in similar ways. In-depth documentation is found on Ripple's *Developer Portal*: https://ripple.com/build/ For JSON-RPC or WebSockets: https://ripple.com/build/rippled-apis/ For JavaScript: https://ripple.com/build/rippleapi/ --- # Ripple APIs Overview ## Connect to `rippled` ## Query Ledger Data ## **Write Ledger Data (Transactions)** --- # Write Ledger Data A [transaction](https://ripple.com/build/transactions/) is the only way to modify the Ripple Ledger. Transactions are only valid if signed, submitted, and accepted into a validated ledger version following the [consensus process](https://ripple.com/build/ripple-ledger-consensus-process/). There are several [transaction *types*](https://ripple.com/build/transactions/#transaction-types), which have a variety of effects on the ledger. `Payment` is the transaction type we'll use next... --- # Write Ledger Data ## [Payment](https://ripple.com/build/transactions/#payment) Transaction A `Payment` transaction represents a transfer of value from one account to another. Payments are also the only way to [create accounts](https://ripple.com/build/transactions/#creating-accounts). The slides that follow we show how this is done. We begin by creating a new address to receive the payment... --- hook: showColdWallet # Write Ledger Data ## [Payment](https://ripple.com/build/transactions/#payment) Transaction `rippled` provides [`wallet_propose`](https://ripple.com/build/rippled-apis/#wallet-propose) to create a new address. RippleAPI (javascript) provides [`generateAddress()`](https://ripple.com/build/rippleapi/#generateaddress). Note that `api.generateAddress()` can be called without `api.connect()`. ```javascript // Generate address, log address (not secret) account = api.generateAddress(); console.log("Generated " + account.address); ``` Result: .coldWallet.preformatted[{ cold wallet }] Remember, normally, don't share secrets. --- hook: showColdWallet showHotWallet showPaymentHotToCold # Write Ledger Data ## [Payment](https://ripple.com/build/transactions/#payment) Transaction Here is an example of a prepared Payment transaction: .paymentHotToCold.preformatted[{ txJSON }] Fields `"TransactionType", "Account", "Flags", "LastLedgerSequence", "Fee"` and `"Sequence"` appear in **all** [transaction types](https://ripple.com/build/transactions/). `"Destination"` and `"Amount"` are specific to the **Payment** transaction type. --- hook: showColdWallet showHotWallet showPaymentHotToCold # Write Ledger Data ## [Payment](https://ripple.com/build/transactions/#payment) Transaction Here is an example of a prepared Payment transaction: .paymentHotToCold.preformatted[{ txJSON }] The example Payment shown above will send *100 XRP* from the source account we funded earlier. The destination is our new, *not yet funded*, address. ``` (100 XRP == 100,000,000 drops) ``` --- # Write Ledger Data ## Submit Payment Transaction The `rippled` [submit](https://ripple.com/build/rippled-apis/#submit) method applies a transaction and sends it to the network to be confirmed and included in future ledgers. Cryptographically signing the transaction and submitting it to the network can be performed in two independent steps. In JavaScript API, the [sign()](https://ripple.com/build/rippleapi/#sign) method is always be invoked before [submit()](https://ripple.com/build/rippleapi/#submit). --- hook: showProvisionalPaymentHotToCold # Write Ledger Data ## Submit Payment Transaction The immediate [submit](https://ripple.com/build/rippled-apis/#submit) response indicates a **provisional** result. .provisionalPaymentHotToCold.preformatted[{ provisional result }] **The final outcome of a transaction *is not known until it appears in a validated ledger*.** --- hook: showFinalPaymentHotToCold # Write Ledger Data ## Submit Payment Transaction After waiting several seconds, for the consensus process to complete, query for the final result of the transaction. Use `rippled`'s [tx](https://ripple.com/build/rippled-apis/#tx) command, or `RippleAPI`'s [getTransaction()](https://ripple.com/build/rippleapi/#gettransaction). It bears repeating: *The final outcome of a transaction is not known until it appears in a validated ledger.* A transaction which provisionally returned an error may eventually succeed. A transaction which provisionally returned success, may eventually fail. When a transaction appears in a validated ledger, its outcome is immutable. It will never change. --- hook: showFinalPaymentHotToCold # Write Ledger Data ## Submit Payment Transaction .finalPaymentHotToCold.preformatted.small[{ final result }] The response to [getTransaction()](https://ripple.com/build/rippleapi/#gettransaction) shows the final, immutable result in `"outcome"`. Note, when using `rippled`'s [tx](https://ripple.com/build/rippled-apis/#tx), look for **`"validated": true`**. --- hook: showFinalPaymentHotToCold # Write Ledger Data ## Submit Payment Transaction .finalPaymentHotToCold.preformatted.small[{ final result }] The *`id`* is the transaction's permanent unique identifier. This is also known as the transaction's *`hash`*. The combination of *`address`* and *`sequence`* together also uniquely identifies this transaction. --- hook: showFinalPaymentHotToCold # Write Ledger Data ## Submit Payment Transaction .finalPaymentHotToCold.preformatted.small[{ final result }] Validated payment transactions have a *`deliveredAmount`*. In `rippled` APIs, this is called *`delivered_amount`*. Inspect the `delivered_amount`, **not the `Amount`**, when handling payments. --- # Ripple APIs ## Review * APIs are available in several formats * `rippled` command line * JSON-RPC * webSockets * JavaScript * A variety of API calls query ledger data * Only the `submit` API writes to the ledger * See [ripple.com/build](https://ripple.com/build) for complete documentation and tools --- # Ripple APIs ## **Note!** * Be aware of Test Net vs Live Net * Protect secret keys * Some API values are *XRP*, while others are *drops* * `1,000,000 drops == one XRP` * The result of `submit` API is *provisional* * Results are *immutable* only when they appear in a *validated* ledger * When processing Payments, observe the `delivered_amount` field * `delivered_amount` may differ from `Amount` * An address must be *funded* to be part of the ledger * Specify a maximum transaction fee * `maxFee` in JavaScript * `fee_multi_max`, `fee_div_max` in `rippled` --- # Ripple APIs Advanced ## `AccountSet` Transaction Type ## Offline Transaction Signing ## Hot and Cold Wallets --- # Ripple APIs Advanced ## AccountSet Transaction Each Ripple account has a balance of XRP. An account also has settings and properties. Transactions of type [`AccountSet`](https://ripple.com/build/transactions/#accountset) modify properties of an account. The examples that follow use `AccountSet` to change two [*account flags*](https://ripple.com/build/transactions/#accountset-flags): * `asfRequireDest` - payments to this address must have *destination tag* * `asfRequireAuth` - all *trust lines* must be approved --- hook: showAccountSetRequireDest # Ripple APIs Advanced ## AccountSet Transaction .preparedAccountSetRequireDest.preformatted[{ tx JSON }] `"SetFlag"` is an *account flag* (`asfRequireDest == 1`), a field specific to the `AccountSet` transaction type. `"Flags"` are *transaction flags*, a field on all transaction types. --- hook: showAccountSetRequireDest # Ripple APIs Advanced ## AccountSet Transaction .preparedAccountSetRequireDest.preformatted[{ tx JSON }] Unlike the `Payment` transaction we submitted earlier, this transaction: * Omits the `LastLedgerSequence` field * Specifies a higher fee These changes make it easier to separate the *sign* and *submit* steps. --- hook: showAccountSetRequireAuth # Ripple APIs Advanced ## AccountSet Transaction .preparedAccountSetRequireAuth.preformatted[{ tx JSON }] This transaction is almost identical to the earlier `AccountSet`. * `"SetFlag"` is now 2, for `asfRequireAuth` * `"Sequence"` is incremented by `1` An account cannot submit two transactions with the same *account sequence* number, it must increase by one with each new transaction. --- # Ripple APIs Advanced ## Offline Transaction Signing We've prepared two `AccountSet` transactions. Next we will *sign* both. Signing, unlike submitting, can be done *offline* for full protection of secret keys. --- hook: showRippledURL showAccountSetRequireDest showColdWallet # Ripple APIs Advanced ## Offline Transaction Signing `rippled` JSON-RPC [sign](https://ripple.com/build/rippled-apis/#sign) example: ```bash curl -s -X POST "$RIPPLED_RPC" -d @- <<-END { "method": "sign", "params": [ { "offline": true, "secret": "$SECRET", "tx_json": $TX_JSON } ] } END ``` `"offline"` tells `rippled` to sign only, not submit. `"tx_json"` is the `AccountSet` transaction as shown previously. *Don't post a `"secret"`, except to a local `rippled` that you host and keep secure*. --- hook: showRippledURL showAccountSetRequireDest showColdWallet # Ripple APIs Advanced ## Offline Transaction Signing JavaScript [sign](https://ripple.com/build/rippleapi/#sign) example: ```javascript // Sign transaction, log response... txJSON = '$TX_JSON'; secret = '$SECRET'; result = api.sign(txJSON, secret); console.log(result) ``` Like `api.generateAddress()` shown earlier, `api.sign()` can be called without `api.connect()`. --- hook: showSignedAccountSetRequireAuth showSignedAccountSetRequireDest # Ripple APIs Advanced ## Offline Transaction Signing Signed `AccountSet (asfRequireDest)` example: .signedAccountSetRequireDest.preformatted.small[] Signed `AccountSet (asfRequireAuth)` example: .signedAccountSetRequireAuth.preformatted.small[] Note the `"id"` (a.k.a. `"hash"`) is the permanent identifier for a transaction. The `"signedTransaction"` may be copied to an online machine, to be submitted to the network. --- hook: showRippledURL showSignedAccountSetRequireDest # Ripple APIs Advanced ## Offline Transaction Signing `rippled` JSON-RPC [submit](https://ripple.com/build/rippled-apis/#submit) example: ```bash curl -s -X POST "$RIPPLED_RPC" -d @- <<-END { "method": "submit", "params": [ { "tx_blob": $TX_BLOB } ] } END ``` `"tx_blob"` is the signed transaction returned from `sign`. --- hook: showRippledURL showSignedAccountSetRequireDest # Ripple APIs Advanced ## Offline Transaction Signing JavaScript [submit](https://ripple.com/build/rippleapi/#submit) example: ```javascript signedTransaction = $TX_BLOB api.connect().then(() => { // Submit transaction, log response... api.submit(signedTransaction).then(provisionalResult => { console.log(provisionalResult); }); }); ``` `signedTransaction` is the signed transaction returned from `api.sign()`. --- hook: showSubmitAccountSetRequireDest showSubmitAccountSetRequireAuth # Ripple APIs Advanced ## Offline Transaction Signing Provisional result of submitted `AccountSet` (`asfRequireDest` flag): .submitAccountSetRequireDest.preformatted.small[{ pending... }] Provisional result of submitted `AccountSet` (`asfRequireAuth` flag): .submitAccountSetRequireAuth.preformatted.small[{ pending... }] The result of submitting a transaction is *provisional*. Final result is available after network consensus. --- hook: showFinalAccountSetRequireDest # Ripple APIs Advanced ## Offline Transaction Signing Final result of first `AccountSet` transaction (via javascript [getTransaction()](https://ripple.com/build/rippleapi/#gettransaction)): .finalAccountSetRequireDest.preformatted.small[{ pending... }] --- hook: showFinalAccountSetRequireAuth # Ripple APIs Advanced ## Offline Transaction Signing Final result of second `AccountSet` transaction (via javascript [getTransaction()](https://ripple.com/build/rippleapi/#gettransaction)): .finalAccountSetRequireAuth.preformatted.small[{ pending... }] --- hook: showColdAccountInfo # Ripple APIs Advanced ## Offline Transaction Signing State of the account, `$ADDRESS`, after two `AccountSet` transactions: .coldAccountInfo.preformatted[] --- #Ripple APIs Advanced ## Review * We started with a *Test Net* account (with 10,000 XRP) * We used `wallet_propose` or `generateAddress()` to make a new address * An address is derived from public/private key pair * We used a `Payment` transaction to create an account with that address * An address is not an account on ledger until it is funded with a *reserve* --- #Ripple APIs Advanced ## Review * We signed two `AccountSet` transactions, setting account flags * `asfRequireDest` and `asfRequireAuth` are recommended for exchanges * We signed transactions "offline" * Offline signing better protects the private key * We submit the two signed transactions to the network * Results of submit are provisional, until transaction is validated --- #Ripple APIs Advanced ## Hot and Cold Wallets Our new account has recommended flags enabled. We protected the account's secret key by signing transactions "offline". This level of security is typical for *cold wallets*. If online systems are hacked, an offline cold wallet key remains safe. A *hot wallet* key may be less safe, so hold lower balances in hot wallets. --- # Ripple APIs Advanced ## Hot and Cold Wallets * Cold Wallet * Keep secret key offline * Set recommended account flags * Published address, i.e. receives incoming deposits * Holds majority of XRP * Hot Wallet(s) * Secret may be online, for automated processes * Holds just enough funds * Enables automated transactions, i.e. sends outgoing withdrawals Use cold wallet secret infrequently. For example, to rebalance funds when hot wallet(s) run low. --- hook: showHotWallet showHotAccountInfo # Ripple APIs Advanced ## Review We created a Test Net **hot** wallet: .hotWalletAddress.preformatted[] A current snapshot of this wallet, via `getAccountInfo()`: .hotAccountInfo.preformatted[] --- hook: showColdWallet showColdAccountInfo # Ripple APIs Advanced ## Review We created a Test Net **cold** wallet: .coldWalletAddress.preformatted[] A current snapshot of this wallet, via `getAccountInfo()`: .coldAccountInfo.preformatted[] --- hook: showHotWallet showHotAccountInfo # Ripple APIs Advanced ## Review The accounts below were created by javascript running in your web browser. Copy and save these key pairs: .hotWallet.preformatted[] .coldWallet.preformatted[] These are active *Test Net* accounts which you can use to start your Ripple integration today. --- # Ripple APIs Advanced ## Question & Answer --- # Responding to Events ## (TODO...) ```javascript api.on('error', (errorCode, errorMessage) => { console.log(errorCode + ': ' + errorMessage); }); api.on('connected', () => { console.log('connected'); }); api.on('disconnected', (code) => { // `code` - will be 1000 if this was normal closure. // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent console.log('disconnected, code:', code); }); ```