Low level Use Cases

From Open Transactions
Jump to navigation Jump to search

Experts only!

Since the high-level API was introduced, the low-level API is now wrapped by the high-level API, instead of being touched directly by the programmatic user.

Many details which were forced on everyday programmers have since been hidden behind that veil. These details are still important: to programmers tweaking the APIs themselves. But otherwise they needed to be moved away from the higher-level API pages, because they introduce unnecessary confusion to everyday programmers to whom they are entirely irrelevant.



SERVER MESSAGES

The Request Number When you message the server, your request number needs to be in sync. Normally it will stay in sync and everything works perfectly. But if the number goes out of sync, your messages will all fail! Just call OT_API_getRequest() to put it back into sync again. You will probably do this once when you first connect to the server, and then never need to do it again. Since the request number must increment for each message, it is impossible for an attacker to intercept and replay an old message, (since the request number on the old message is no longer any good.)



Create a new Asset Account Clear the reply buffer: Call OT_API_FlushMessageBuffer. Wallet sends message to server: OT_API_createAssetAccount() Server replies with FAILURE/SUCCESS, and the new account is automatically stored in your wallet (if applicable.) Use OT_API_PopMessageBuffer() to peer inside the server reply. Call OT_API_Message_GetCommand to find out the command (there are many commands possible in an OT message.) Print the command out onto the screen, so you can see what it looks like. That! is the string you want to compare for in your project's code, to make sure you are looking at the correct reply. There may be multiple replies in the buffer, but probably not, since we FLUSHED it before sending the message... Call OT_API_Message_GetSuccess to find out if the reply was SUCCESS or FAILURE. If it was a SUCCESS, then refresh the list of accounts in your display (from the OT API) since your new asset account should now be there.



the Transaction Number
Make sure your wallet always has a supply of transaction numbers. Call OT_API_GetNym_TransactionNumCount() to see how many transaction numbers are currently in your wallet. Call OT_API_getTransactionNumber() to get more numbers when you are running low. (All transactions require a transaction number that was specifically issued to you by an OT Server.) (Your transaction numbers remain outstanding to you until you close out their actual transactions.) You don't have to juggle these transaction numbers--OT does it automatically in your wallet--but you do need to make sure you have some available, and replenish when they're getting low. I recommend just requesting 10 more of them whenever you have less than 5 available. (OT auto-receives all new #s in reply from the server, and stores them in your wallet.)

Make sure you have the latest intermediary files before doing a transaction:
All transactions, in addition to requiring a transaction number, also require a balance agreement. (The wallet performs this behind the scenes when you request any transaction.)

IMPORTANT: Balance agreement, combined with transaction numbers, is what makes it possible for the server and all other parties to eliminate account history, instead storing only the last signed receipt. (Of course they have the choice to keep their own records--but they are no longer forced to do so.)

In order to do any transaction, the wallet's balance agreement process requires that you have the latest copy of your account, inbox, and outbox files. Really, only the account and inbox will change "behind your back" (that is, on the server side), but you still need to grab the outbox at least once: when you first create an account, before the first transaction. Your balance agreements will succeed as long as you have the latest copies of these files. To retrieve them from the server, call these functions:
OT_API_getAccount()
OT_API_getOutbox()
OT_API_getInbox()

You might ask: "But what if I download my account, or my outbox, and the server has maliciously inserted data into them?" No problem -- Open Transactions stores the last signed receipt from the server, and is able to verify the new intermediary files against that last receipt. You can see this happening when you run the command-line test client, and receipts can be verified programmatically using OT_API_VerifyAccountReceipt().

The Triple-Signed Receipts on Open Transactions ensure that this sort of protection is available to all parties, while simultaneously not having to store any transaction history!



Deposit cash Alice calls OT_API_FlushMessageBuffer.
Alice sends message to server requesting deposit: OT_API_notarizeDeposit()
Server replies success or failure.
Call OT_API_PopMessageBuffer() to get the server reply out of the buffer. Since you just flushed the buffer (above), we already know what kind of message we're expecting to find (it's a "notarizeTransactions" message containing an "atDeposit" transaction.)
Call
OT_API_Message_GetCommand if you want to see the exact command you're holding, just to make sure you're looking at the right message (See OTMessage.cpp for all the different commands, if you're curious to see them all + their data!)
Call
OT_API_Message_GetSuccess@ (and pass it the server reply) to find out if the message was a SUCCESS or FAILURE.
WARNING: Just because a MESSAGE was successful, does NOT mean that the TRANSACTION INSIDE THAT MESSAGE was also successful! The message may have processed normally, yet the server may have still refused the transaction inside that message for whatever reason (expired, etc.) You might have a perfectly valid message informing you that your cheque deposit failed due to a bounced cheque.

FYI, not all messages contain transactions, but deposit cash DOES. You will find this useful:

If any Message FAILS, it's possible that your Request Number got out of sync. Just call OT_API_getRequest() and then try the message again.

FOR TRANSACTIONS: If the Message SUCCEEDS, next check to see if the BALANCE AGREEMENT was also successful. (All Transactions must include a balance agreement, which the server will verify before signing, and which will form part of your receipt.)

To verify the balance agreement, OT_API_Message_GetBalanceAgreementSuccess() will prove useful to you. (Even a perfectly valid transaction will be automatically rejected by the server if the balance agreement is wrong. So you need to check the server's reply and see if your balance agreement was a success.)

If your balance agreement is failing, it could be because a cron item has expired (payment plan, market offer, etc) and the transaction number has come off of your list (you just don't know it yet.) To fix this, call OT_API_getNymbox() to download any notices of this nature, wait a few MS, and then try again.

If your balance agreement is a SUCCESS, then verify the transaction itself to see if it succeeded. To verify a transaction, use OT_API_Message_GetTransactionSuccess() or OT_API_Transaction_GetSuccess(). When this verifies, it means your withdrawal, or deposit, or whatever, actually went through, and you now have a receipt.

The receipt will be saved automatically into your receipts folder, whether success or fail. The next time you download your inbox, you'll want to call OT_API_VerifyAccountReceipt(), which uses that last receipt to make sure the server isn't screwing you.

AGAIN: Whenever you download the latest copy of your account balance, or your inbox, or any other such intermediary files, how do you know the server isn't screwing you over by getting you to sign a new balance agreement based on some WRONG balance? Because, you are ALWAYS able to VERIFY those intermediary files against your LAST SIGNED RECEIPT, by calling OT_API_VerifyAccountReceipt(). Do you understand how important that is? It should be performed anytime those intermediary files have been downloaded from the server. This receipt is what protects the client from getting SCREWED. (Do you know what it feels like to get screwed?)



BOX RECEIPTS: Any of the ledger boxes (Nymbox, Inbox, Outbox) now only contain abbreviated versions of their contents, instead of the full receipts. (At some point the boxes had gotten too large to download as a single file, so the contents were separated out into individual files.)

Thus, once you download the ledger itself, iterate through it and use OT_API_DoesBoxReceiptExist for each receipt in it. If it doesn't then use OT_API_getBoxReceipt to download it from the server. (Using the normal flush/call/pop pattern to check for success.)



NOTE: The new "Ultra-High-Level API" manages all of this complexity for you! See the scripts/samples folder, as well as the Java code for the Moneychanger test GUI, to see examples of it in action. All of the above gets reduced down to a couple of lines of code!



Account-to-Account Transfer
Call OT_API_notarizeTransfer() to send a transfer from one asset account to another. (A pending transfer will go into your outbox, and the recipient's inbox, pending acceptance by the recipient.) (FYI, every asset account has its own inbox and outbox for handling pending transactions, as well as receipts.) (FYI, every Nym similarly has its own Nymbox, used for receiving messages from other users, and for receiving new transaction numbers from the server.)



Withdraw Voucher A voucher is like a "money order" or "cashier's cheque". It's a cheque, but drawn on a server account, instead of your own account. Withdrawing in voucher form is similar to withdrawing in cash: the server debits your asset account, and then gives you an official cheque.

But with cash, it's automatically saved to your purse, whereas with a voucher, you have to read the voucher out of the server reply, and display it on the screen!

Call OT_API_FlushMessageBuffer Call OT_API_withdrawVoucher() to send the request to the server. Then, call OT_API_PopMessageBuffer() to retrieve any server reply.

If there is a message from the server in reply, then call OT_API_Message_GetCommand() to verify that it's a reply to the message that you sent, and then call OT_API_Message_GetSuccess() to verify whether the message successful.

If the message was successful, then use OT_API_Message_GetBalanceAgreementSuccess() and OT_API_Message_GetTransactionSuccess() as described above in the deposit cash instructions.

If it was all a success, next call OT_API_Message_GetLedger() to retrieve the actual "reply ledger" from the server.

Penultimately, call OT_API_Ledger_GetTransactionByID() and then, finally, call OT_API_Transaction_GetVoucher() in order to retrieve the voucher cheque itself from the transaction.



BASKET CURRENCIES

Create a new Basket Currency
First, invent the basket in your head. Give it name: "Clams." Next, give it a ratio in terms of other existing currencies: "10 Clams equals a basket of 5 dollars, 3 Euro, and 20 Bitcoin" Call OT_API_GenerateBasketCreation() to begin creating the basket. The "10 Clams" from above is set here. Next, call OT_API_AddBasketCreationItem() for EACH currency in the basket. In the above example, I would call this function 3 times, in order to set the "5 dollars" the "3 Euro" and the "20 Bitcoin" from above. Now that the basket has been defined, call OT_API_issueBasket() to actually create the basket currency on the server. (Now you have created a new asset type--a basket--which the server will control. Any user may now create accounts using this type! And from there, they can write cheques, withdraw cash, trade on markets, etc. the same as with any other asset type.)

Exchange Digital Assets in/out of a Basket Currency (from your Asset Accounts)
First, call OT_API_GenerateBasketExchange() to setup the exchange. You'll need to know the basket's asset type, and you'll need to have an existing asset account of that type. You will also set the "transfer multiple" in that call. What's the transfer multiple? Remember the ratio above: "10 Clams equals a basket of 5 dollars, 3 Euro, and 20 Bitcoin"

Here are examples of the transfer multiple:
"10 Clams == 5 USD, 3 EUR, 20 BIT" (Transfer multiple: 1)
"20 Clams == 10 USD, 6 EUR, 40 BIT" (Transfer multiple: 2)
"30 Clams == 15 USD, 9 EUR, 60 BIT" (Transfer multiple: 3)
"40 Clams == 20 USD, 12 EUR, 80 BIT" (Transfer multiple: 4)
"50 Clams == 25 USD, 15 EUR, 100 BIT" (Transfer multiple: 5)

Next, call OT_API_AddBasketExchangeItem() for each currency type in the basket. You will need to to pass the asset account ID for an existing asset account, for each currency type (since you are converting in or out, from your basket account, to your asset accounts.) Therefore you will call this function once for USD, once for EUR, and once for BIT, in that example, passing in your USD asset acct ID, your EUR asset acct ID, and your Bitcoin asset account ID.

Now that it's all set up, call OT_API_exchangeBasket() to actually message the server and perform the exchange.

If the message was successful, then use OT_API_Message_GetBalanceAgreementSuccess() and OT_API_Message_GetTransactionSuccess() as described above in the deposit cash instructions.

Interesting: basket currencies themselves require no more additional system resources than normal currencies! The magic happens during the exchange process. The server simply stores internal asset accounts for the dollars, euros, and bitcoins (say), and it manages the issuer account for the basket, and it also manages the exchanging in and out, by moving digital assets in or out of the internal backing accounts. After that point, a basket is just another Asset Type ID, and requires no additional resources. You can even have baskets made up of other baskets, nested 10 times, and it won't affect the speed of operation of the server!



PROCESSING YOUR INBOX

Process the Receipts and Pending Transfers in your Inbox
Call OT_API_FlushMessageBuffer
Call OT_API_getInbox() to grab the latest inbox from the server.
Then, call OT_API_PopMessageBuffer() to retrieve any server reply.

You will also probably want to call OT_API_getAccount as well as OT_API_getOutbox, using the above method, since you need to have the latest versions of those files, or your balance agreement will be calculated wrong, causing your transaction to fail. NOTE: You only have to getOutbox for a brand new account. After that, OT will keep it in sync automatically. But the Account and the Inbox need to be downloaded all the time, since they change on server side occasionally.

Then call OT_API_LoadInbox() to load the inbox ledger from local storage.

During this time, your user has the opportunity to peruse the inbox, and to decide which transactions therein he wishes to accept or reject. Usually the inbox is display on the screen, then the user selects various items to accept or reject, and then the user clicks "Process Inbox" and then you do this:

Then call OT_API_Ledger_CreateResponse() in order to create a 'response' ledger for that inbox, which will be sent to the server to signal your responses to the various inbox transactions.
Then call OT_API_Ledger_GetCount() (pass it the inbox) to find out how many transactions are inside of it.
Use that count to LOOP through them... Use OT_API_Ledger_GetTransactionByIndex() to grab each transaction as you iterate through the inbox. (There are various introspection functions you can use in the API here if you wish to display the inbox items on the screen for the user...)
Next call OT_API_Transaction_CreateResponse() for each transaction in the inbox, to create a response to it, accepting or rejecting it. This function creates the response and adds it to the response ledger.
Next, call OT_API_Ledger_FinalizeResponse() which will create a Balance Agreement for the ledger.
Finally, call OT_API_processInbox() to send your message to the server and process the various items.

If the message was successful, then use OT_API_Message_GetBalanceAgreementSuccess() and OT_API_Message_GetTransactionSuccess() as described above in the deposit cash instructions.



MARKETS and PAYMENT PLANS

Setup a Payment Plan
The Merchant draws up the Payment Plan using OT_API_ProposePaymentPlan
Then he sends it to the Customer, who calls OT_API_ConfirmPaymentPlan
The Customer's wallet activates the payment plan by sending it to the server using this function: OT_API_depositPaymentPlan() (Receipts will process into the respective parties' inboxes.)
Use OT_API_cancelCronItem() to cancel any payment plan (or market offer.)

If the message was successful, then use OT_API_Message_GetBalanceAgreementSuccess() and OT_API_Message_GetTransactionSuccess() as described above in the deposit cash instructions.

Issue a Market Offer

Clear the reply buffer:
Call OT_API_FlushMessageBuffer.
Call this function: OT_API_issueMarketOffer()
Server replies with FAILURE/SUCCESS...
Use OT_API_PopMessageBuffer() to grab a copy of the server reply.
Call OT_API_Message_GetCommand to verify it's the correct one. (There may be multiple replies in the buffer, but probably not, since we FLUSHED it before sending the message...)
Call OT_API_Message_GetSuccess to find out if the message was a SUCCESS or FAILURE. If the message was successful, then use OT_API_Message_GetBalanceAgreementSuccess() and OT_API_Message_GetTransactionSuccess() as described above in the deposit cash instructions.

Once an offer goes onto the market, multiple trades may occur between it and the other offers on the market. (According to the rules in the offers.) Receipts will process into the respective parties' inboxes after each trade.

Cancel a Market Offer
Use OT_API_cancelNymMarketOffer to remove an offer from a market. Use Flush / Pop / GetSuccess as above, to see if your call was a success.
If the message was successful, then use OT_API_Message_GetBalanceAgreementSuccess() and OT_API_Message_GetTransactionSuccess() as described above in the deposit cash instructions.

Retrieving Market Data from OT Server:
OT_API_getMarketList retrieves a list of all the markets available on a specific server, and details for each market. If the server call is a success, the data will be retrieved from the OT server, and written to local storage using the OT Storage API, at this path or key (inside your data_folder location): markets/SERVER_ID/market_data.bin
If you want to verify whether the data is now available in local storage (after a successful call to getMarketList), your code would look like this: if (otapi.Exists("markets", serverID, "market_data.bin")) bFoundFile = true;

Here is some Java code that reads the market list object from local storage, assuming bFoundFile is true:

    MarketList marketList = null;
    Storable storable = null;

    if (otapi.Exists("markets", serverID, "market_data.bin")) 
    {
        storable =
            otapi.QueryObject(StoredObjectType.STORED_OBJ_MARKET_LIST,
                "markets", serverID, "market_data.bin");
        if (storable==null)
            return null;
            marketList = MarketList.ot_dynamic_cast(storable);
        }

Once you have the market list, here is some sample Java code to read the details of an individual market from that list:

public static Object getMarketDetails(String marketID)
{
    MarketList marketList = Utility.getMarketList();

    if (marketList == null) 
    {
        return null;
    }

    for (int i=0; i < marketList.GetMarketDataCount(); i++)
    {
        MarketData marketData = marketList.GetMarketData(i);

        if (marketData == null)
            continue;

        String [] row = new String[2];

        if (marketID.equals(marketData.getMarket_id())) 
        {
            String[] data = new String[2];
            marketData.getLast_sale_price();
            marketData.getCurrent_ask();
            
            // Etc!! This is where you get the market details.
        }
    }

    // return custom object here (row/data or whatever), or null if failure..
    return null;
}

OT_API_getMarketOffers retrieves publicly-available info for all offers on a specific market, up until maximum depth, and saves the data here: markets/SERVER_ID/offers/MARKET_ID.bin

(See testwallet/OTAPI.i for ALL DATA OBJECTS that are used in local storage, including market offers, recent trades, etc. See the code above for a sample on how those data objects are used.)

OT_API_getMarketRecentTrades: Retrieves all recent trades on a market (up until maximum depth), and stores them here: markets/SERVER_ID/recent/MARKET_ID.bin

To retrieve the offers that a specific Nym has out on the market, use: OT_API_getNym_MarketOffers This getNymMarketOffers_ data is private and thus a lot more detailed than what's retrieved via OT_API_Market_GetOffers, which is more meant for public use. After a successful retrieval, you can load the data from this location: nyms/SERVER_ID/offers/NYM_ID.bin

If you want to see the Nym's private list of his completed trades, the path to load it from is here: nyms/trades/SERVER_ID/NYM_ID
There is no need to send a server message in this last case--just read the data from local storage directly. (When market trading receipts are accepted and cleared out of your inbox, this is where they will go. So there's no need to download them, since they were already downloaded through your inbox previously.)
As before, the appropriate data object can be found in testwallet/OTAPI.i along with all the others.



I will be very responsive to developer needs and questions regarding the OT API, so I encourage you to play with the software and contact me anytime.

See also:
The OT API
Installation
Test Wallet commands


The OT C++ class library

Diagrams: Overview, [1]' (cash only), and [2] (using accounts).