{
  "comments": [
    {
      "abs_rshares": 0,
      "active": "2016-07-21T17:34:03",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 9197,
      "beneficiaries": [],
      "body": "This article desribes the API of the STEEM full node (**not** of the wallet API).\n\n## Prerequisits\n\nThis article assumes that you have a full node running and listening to port ``8092``, locally. You can achieve this by\n\n```\n./programs/steemd/steemd --rpc-endpoint=127.0.0.1:8092\n```\n\nWe open up the RPC endpoint so that we can interface with the node using RPC-JSON calls.\n\n## Call Format\n\nIn Graphene, RPC calls are state-less and accessible via regular JSON formated RPC-HTTP-calls. The correct structure of the JSON call is\n\n```json\n{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1\n    \"method\": \"get_account\",\n    \"params\": [[\"xeroc\", \"steemit\"]],\n}\n```\n\nThe `get_accounts` call is available in the full node's API and takes only one argument which is an array of account ids (here: `[\"xeroc\", \"steemit\"]`).\n\n### Example Call with `curl`\n\nSuch as call can be submitted via ``curl``:\n\n```sh\ncurl --data '{\"jsonrpc\": \"2.0\", \"method\": \"get_accounts\", \"params\": [[\"xeroc\",\"steemit\"]], \"id\": 1}' http://127.0.0.1:8090/rpc\n```\n\n## Successful Calls\n\n\nThe API will return a properly JSON formated response carrying the same ``id``\nas the request to distinguish subsequent calls.\n\n```json\n{ \"id\":1, \"result\": \"data\" }\n```\n\n## Errors\n\nIn case of an error, the resulting answer will carry an ``error`` attribute and\na detailed description:\n\n```json\n{\n  \"id\": 0\n  \"error\": {\n    \"data\": {\n      \"code\": error-code,\n      \"name\": \" .. name of exception ..\"\n      \"message\": \" .. message of exception ..\",\n      \"stack\": [ .. stack trace .. ],\n    },\n    \"code\": 1,\n  },\n}\n```\n\n## Available Calls\n\nEven though, the `help` call does not exist, it gives us an error message that contains all available API calls in the stack trace:\n\n```\ncurl --data '{\"jsonrpc\": \"2.0\", \"method\": \"help\", \"params\": [], \"id\": 1}' http://127.0.0.1:8090/rpc\n```\n```json\n{\n  \"id\": 1,\n  \"error\": {\n    \"message\": <...>,\n    \"data\": {\n      \"message\": \"Assert Exception\",\n      \"name\": \"assert_exception\",\n      \"stack\": [\n        {\n          \"data\": {\n            \"name\": \"help\",\n            \"api\": {\n              \"set_subscribe_callback\": 0,\n              \"get_dynamic_global_properties\": 12,\n              \"get_accounts\": 17,\n              \"get_active_categories\": 9,\n              \"get_account_references\": 18,\n              \"get_trending_categories\": 7,\n              \"get_content\": 36,\n              \"get_state\": 6,\n              \"get_discussions_by_total_pending_payout\": 38,\n              \"cancel_all_subscriptions\": 3,\n              \"get_block_header\": 4,\n              \"get_active_votes\": 35,\n              \"get_current_median_history_price\": 15,\n              \"lookup_witness_accounts\": 26,\n              \"verify_account_authority\": 34,\n              \"get_key_references\": 16,\n              \"set_pending_transaction_callback\": 1,\n              \"get_required_signatures\": 31,\n              \"get_recent_categories\": 10,\n              \"get_order_book\": 28,\n              \"lookup_accounts\": 20,\n              \"get_account_history\": 23,\n              \"get_chain_properties\": 13,\n              \"get_feed_history\": 14,\n              \"verify_authority\": 33,\n              \"get_discussions_by_last_update\": 40,\n              \"get_conversion_requests\": 22,\n              \"get_discussions_in_category_by_last_update\": 41,\n              \"get_block\": 5,\n              \"get_witness_count\": 27,\n              \"get_best_categories\": 8,\n              \"get_potential_signatures\": 32,\n              \"lookup_account_names\": 19,\n              \"get_transaction\": 30,\n              \"get_witnesses\": 24,\n              \"get_witness_by_account\": 25,\n              \"get_account_count\": 21,\n              \"get_transaction_hex\": 29,\n              \"get_content_replies\": 37,\n              \"get_discussions_in_category_by_total_pending_payout\": 39,\n              \"get_miner_queue\": 43,\n              \"get_active_witnesses\": 42,\n              \"set_block_applied_callback\": 2,\n              \"get_config\": 11\n            }\n          },\n          \"context\": {\n            \"line\": 109,\n            \"hostname\": \"\",\n            \"timestamp\": \"2016-04-13T16:15:17\",\n            \"method\": \"call\",\n            \"thread_name\": \"th_a\",\n            \"level\": \"error\",\n            \"file\": \"api_connection.hpp\"\n          },\n          \"format\": \"itr != _by_name.end(): no method with name '${name}'\"\n        }\n      ],\n      \"code\": 10\n    },\n    \"code\": 1\n  }\n}\n```\n\nFurther documentation about the calls can be found in the sources in [libraries/app/include/steemit/app/database_api.hpp](https://github.com/steemit/steem/blob/master/libraries/app/include/steemit/app/database_api.hpp).\n",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "",
      "children": 3,
      "children_abs_rshares": 0,
      "created": "2016-04-13T16:25:15",
      "curator_payout_value": {
        "amount": "493",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 111,
      "json_metadata": "{}",
      "last_payout": "2016-08-24T05:37:24",
      "last_update": "2016-04-13T16:25:15",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": 0,
      "net_votes": 29,
      "parent_author": "",
      "parent_permlink": "",
      "percent_steem_dollars": 10000,
      "permlink": "steem-api",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "steem-api",
      "title": "Steem API",
      "total_payout_value": {
        "amount": "2165",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-04-13T16:27:03",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 96839,
      "beneficiaries": [],
      "body": "The underlying technology if STEEM is very similar to the Graphene technology used in other blockchains. It consists of hash-linked blocks that may contain several transactions. In contrast to Bitcoin, each transaction can itself contain several so called operations that perform certain tasks (e.g. transfers, vote and comment operations, vesting operations, and many more).\n\nOperations can easily be identified by their *operation type* as well as a different structure in the *operation data*.\n\nFor the sake of simplicity, this article will show how to read and interpret **transfer** operations on order to process customer deposits. In order to distinguish customers, we will make use of *memos* that can be attached to each transfer. Note, that these memos are stored on the blockchain in plain text.\n\n## Transfer Operation\n\nA transfer operations takes the following form:\n\n```json\n{\n  \"from\": \"hello\",\n  \"to\": \"world\",\n  \"amount\": \"10.000 STEEM\",\n  \"memo\": \"mymemo\"\n}\n```\nwhere `from` and `to` identify the sender and recipient. The amount is a space-separated string that contains a floating point number and the symbol name `STEEM`. As mentioned above, the sender can attach a memo which is stored on the blockchain in plain text.\n\n## Operations\n\nEach operation is identified by an operation identifier (e.g. `transfer`) together with the operation-specific data and are bundled into an array of *operations*:\n\n```json\n[\n  [operationType, {operation_data}],\n  [operationType, {operation_data}],\n  [operationType, {operation_data}],\n]\n```\n\nSeveral operations can be grouped together but they all take the form `[operationType, {data}]`:\n```json\n[\n  [\"transfer\", {\n      \"from\": \"hello\",\n      \"to\": \"world\",\n      \"amount\": \"10.000 STEEM\",\n      \"memo\": \"mymemo\"\n      }\n   ],\n   [\"transfer\", {\n      \"from\": \"world\",\n      \"to\": \"trade\",\n      \"amount\": \"15.000 STEEM\",\n      \"memo\": \"Gift!\"\n      }\n   ]\n]\n```\nThe set of operations is executed in the given order. Given that STEEM has a single threaded business logic, all operations in a transaction are guaranteed to be executed atomically.\n\n## Transactions\n\nThe set of operations is stored in a transaction that now carries the required signatures of the accounts involved, an expiration date as well as some parameters required for the TaPOS/DPOS consensus scheme.\n\n```json\n[\n  {\"ref_block_num\": 29906,\n   \"ref_block_prefix\": 1137201336,\n   \"expiration\": \"2016-03-30T07:15:00\",\n   \"operations\": [[\n       \"transfer\",{\n         \"from\": \"hello\",\n         \"to\": \"world\",\n         \"amount\": \"10.000 STEEM\",\n         \"memo\": \"mymemo\"\n       }\n     ]\n   ],\n   \"extensions\": [],\n   \"signatures\": [\"20326......\"]\n   }\n]\n```\n\n## Block\n\nSeveral transactions from different entities are then grouped into a block by the block producers (e.g. witnesses and POW miners). The block carries the usual blockchain parameters, such as the transaction merkle root, hash of the previous block as well as the transactions.\n\n```json\n{\n  \"previous\": \"000274d2b850c8433f4c908a12cc3d33e69a9191\",\n  \"timestamp\": \"2016-03-30T07:14:33\",\n  \"witness\": \"batel\",\n  \"transaction_merkle_root\": \"f55d5d65e27b80306c8e33791eb2b24f58a94839\",\n  \"extensions\": [],\n  \"witness_signature\": \"203b5ae231c4cf339367240551964cd8a00b85554dfa1362e270a78fa322737371416b00d1d7da434f86ad77a82b6cc1dd2255ca6325b731185fe2c59514e37b29\",\n  \"transactions\": [{\n      \"ref_block_num\": 29906,\n      \"ref_block_prefix\": 1137201336,\n      \"expiration\": \"2016-03-30T07:15:00\",\n      \"operations\": [[\n          \"transfer\",{\n            \"from\": \"hello\",\n            \"to\": \"world\",\n            \"amount\": \"10.000 STEEM\",\n            \"memo\": \"mymemo\"\n          }\n        ]\n      ],\n      \"extensions\": [],\n      \"signatures\": [\n        \"20326d2fe6e6ba5169a3aa2f1e07ff1636e84310e95a40af12483af21a3d3c5e9564565ede62659c2c78a0d9a65439ad4171a9373687b86a550aa0df9d23ade425\"\n      ]\n    }\n  ],\n  \"block_id\": \"000274d3399c50585c47036a7d62fd6d8c5b30ad\",\n  \"signing_key\": \"STM767UyP27Tuak3MwJxfNcF8JH1CM2YMxtCAZoz8A5S8VZKQfZ8p\",\n  \"transaction_ids\": [\n    \"64d45b5497252395e38ed23344575b5253b257c3\"\n  ]\n}\n```\n\nFurthermore, the call `get_block <blocknumber>` returns the transaction ids (i.e. the hashes of the signed transaction produced by the sender) that uniquely identify a transaction and thus the containing operations.\n",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "",
      "children": 0,
      "children_abs_rshares": 0,
      "created": "2016-04-13T16:27:03",
      "curator_payout_value": {
        "amount": "21301",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 112,
      "json_metadata": "{}",
      "last_payout": "2016-08-03T00:00:06",
      "last_update": "2016-04-13T16:27:03",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": 0,
      "net_votes": 29,
      "parent_author": "",
      "parent_permlink": "",
      "percent_steem_dollars": 10000,
      "permlink": "steem-blockchain-data-structure",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "steem-blockchain-data-structure",
      "title": "The Steem API",
      "total_payout_value": {
        "amount": "21304",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-05-10T16:00:12",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 625243,
      "beneficiaries": [],
      "body": "This guide gives a quick introduction on how to trade STEEM:BTC in\nBitShares.\n\n## Choose your wallet\n\nTo interact with the BitShares ecosystem, you can either\n\n* [download the official Light Wallet](https://bitshares.org/download)\n* or access the network in the browsers via one of our partners:\n  * https://openledger.info\n  * https://secure.freedomledger.com\n  * https://bitshares.org/wallet\n\n## Create an account\n\nIn order to use BitShares, you will need to register an account.  All\nyou need\nto provide is\n\n* an account name\n* a password\n\nThe identicon at the top cn be used to verify your account name to third\nparties. It is derived from your acocunt name and gives a second\nverification\nfactor. And this is how you register your account:\n\n![Create a new account](http://docs.bitshares.org/_images/create-account.png)\n\nNote that, in contrast to any other platform you have ever used:\n\n    Creating an account at one of our partners will make your account\n    available at all the other partners as well.\n\nHence, your account name can be seen similar to a mail address in such\nthat it\nis **unique** and every participant in the BitShares network can\ninteract with\nyou independent of the actual partner providing the wallet.\n\n## Backup your account\n\nSince you are the only individual that has access to your account and\nfunds, it is **your responsibility** to make a secure backup of your\nregistered account.\n\nAfter creating your account, follow these steps:\n\n1. Click the *Backup required* link in the footer\n![Backup required footer](http://i.imgur.com/WMI04fc.png)\n2. Click \"Create Backup\"\n3. Click \"Download\" and store the file safely. Make sure to **remember\n   the passphrase** you provided when you created your wallet (above) as\n   the downloaded file is encrypted with it.\n4. (optionally but recommended) Note the `xxxxxx * SHA1` checksum to\n   verify the backup\n\n## Depositing Bitcoin\n\n1. Click on **Account** in the top navigation bar to open up your\n   account overview\n2. Make sure it states `(Your Account)` below your account name\n3. Click **Deposit/Withdraw** to open the corresponding page\n4. Enable the *gateway* **CCEDK** which provide the most liquid\n   `OPEN.BTC` IOU\n\n   Note: OPEN.BTC is an *I owe you* (IOU) provided by CCEDK. This means\n   that they back every `OPEN.BTC` token with 100% reserves!\n\n5. Identify the `OPEN.BTC` row and use the provided Bitcoin address to\n   fund your account with BTC\n6. After reception and confirmation of your transfer, you will be\n   automatically credited with `OPEN.BTC`\n\n![Depositing BTC](http://i.imgur.com/tuO0LFg.png)\n\n## Trading OPEN.BTC for STEEM\n\n1. Clicking the **Trade** link in the top navigation bar will open the\n   decentralized exchange of BitShares\n2. Since any two assets can be traded, we first need to search for our\n   `OPEN.STEEM:OPEN.BTC` pair by clicking **Find Markets**\n3. Search for `OPEN.STEEM:OPEN.BTC` to trade `OPEN.STEEM` for `OPEN.BTC`\n   (the tokens you have received for depositing `BTC`)\n4. Click on the search result below to open the market page\n\n![Identify the OPEN.STEEM:OPEN.BTC market](http://i.imgur.com/DE4USPk.png)\n\n5. Similar to other exchanges, you can now place buy and sell orders at\n   your price.\n\n   **Note:** In BitShares, placing an order costs a very small fee\n   (sub-$cent) which get a 90% refund on cancellation. This fee can be\n   payed in almost any asset including `OPEN.BTC`, `BTS` or `OPEN.STEEM`\n   and it is up to you to decide which asset to use to pay for the fee.\n   Further percentage fees on filled orders may apply.\n\n![Playing Buy and Sell Orders](http://i.imgur.com/lycrX2e.png)\n\n6. Once your order is matched, the corresponding asset will\n   automatically be credited in your account.\n\n## Withdrawing OPEN.STEEM into your STEEM account\n\n1. Open your account's overview page\n2. Click on **Deposit/Withdraw**\n3. Pick CCEDK\n4. Identify `OPEN.STEEM` and click *withdraw*\n5. Provide amount and your Steem account name\n6. Click *withdraw*\n\nWait a few seconds and you should receive your tokens in your STEEM\naccount.",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "steem",
      "children": 1,
      "children_abs_rshares": 0,
      "created": "2016-05-10T15:58:54",
      "curator_payout_value": {
        "amount": "137495",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 3221,
      "json_metadata": "",
      "last_payout": "2016-08-12T16:56:21",
      "last_update": "2016-05-10T15:58:54",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": 0,
      "net_votes": 29,
      "parent_author": "",
      "parent_permlink": "steem",
      "percent_steem_dollars": 10000,
      "permlink": "steem-howto-buy-open-steem-with-the-bts-dex",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "steem-howto-buy-open-steem-with-the-bts-dex",
      "title": "Howto Buy STEEM in BitShares",
      "total_payout_value": {
        "amount": "137644",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-05-22T17:06:24",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 18220,
      "beneficiaries": [],
      "body": "In Steem, we can organize posts in *categories*. Even though they are\ntechnically the same as a permlink, I think we should give it a more\nfancy or modern name.\n\nFor instance:\n\n* reddit has sub-reddits `/r/`\n* voat has sub-voats `/v/`\n* facebook has pages `/pages/`\n* 4chan has boards `boards.4chan.org/`\n\nCan the *alpha-steemians* comeup with a better name?",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "steem",
      "children": 39,
      "children_abs_rshares": 0,
      "created": "2016-05-03T07:12:18",
      "curator_payout_value": {
        "amount": "4006",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 1450,
      "json_metadata": "",
      "last_payout": "2016-08-07T14:36:06",
      "last_update": "2016-05-03T13:18:42",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": 0,
      "net_votes": 13,
      "parent_author": "",
      "parent_permlink": "steem",
      "percent_steem_dollars": 10000,
      "permlink": "steem-sub-steem-permlink-proposals",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "steem-sub-steem-permlink-proposals",
      "title": "Can we find a better name for 'category'?",
      "total_payout_value": {
        "amount": "4008",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-09-07T22:52:27",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 501641,
      "beneficiaries": [],
      "body": "This article is for developers that are trying to implement *transaction signing* for the Steem (or BitShares) blockchain in their favorite language. It gives a brief introduction of how transactions look like, how they are constructed and most importantly, how they are signed so that they are included in a block.\n\nI am writing this article because it took me almost 2 years to have it fully implemented in python-steem and python-graphene. If it wasn't for those 2 years, we wouldn't be able to use piston today.\nFurther, I hope that this tutorial allows people to quickly get a picture of what is going on so that they can dig deeper.\n\n# What's transaction signing?\n\nTransaction signing, in general, starts with an intention of the user to do *something*. This intention affects his account and thus needs the authorization of that account by means of a valid (in our case cryptographic) signature. In contrast to physical paper signatures, this digital signature needs to be tied to the actual intention (the transaction), otherwise it could be copied over to other transactions and used multiple times. For that reasons, every transaction is signed independently, and a signature is only valid for that particular transaction.\n\nThis tutorial shows how the signature is derived given a particular intention/transaction.\n\n# Let's get started\n\nIn our case, we start of with a simple intention and that is:\n\n**Upvote the blog post @xeroc/piston**\n\n## Operation\n\nIntentions on Steem (and other graphene based blockchains) are called **operations**. In our case, the operation has the form:\n\n    ['vote',\n       {'author': 'xeroc',\n        'permlink': 'piston',\n        'voter': 'xeroc',\n        'weight': 10000}]\n\nWe can clearly identify\n\n* the type of the operation (`vote`)\n* the author and permlink that identify the post (`xeroc`, `piston`)\n* the voter (also `xeroc` as I vote for my own post)\n* the weight of the vote (`10000` which corresponds to 100%)\n\n## Transaction\n\nIn the next step, we encapsulate this (and possible other) operations into a **transaction**. The purpose of this step is to allow multiple actions to be performed consecutively by appending multiple operations, to append the required signatures, and expiration and add the TaPOS parameters (see below). In our case (one vote operation), it takes the following form:\n\n    tx = {'ref_block_num': 36029,\n          'ref_block_prefix': 1164960351,\n          'expiration': '2016-08-08T12:24:17',\n          'operations': [['vote',\n                          {'author': 'xeroc',\n                           'permlink': 'piston',\n                           'voter': 'xeroc',\n                           'weight': 10000}]],\n          'extensions': [],\n          'signatures': [],\n          }\n\nWe notice that our operation is now part of the transaction (as part of the `operations` array) and that we now have a field for our `signatures` and an expiration. The expiration allows for transactions to expire if they are not included into a block by that time. Usually that date is about 30 seconds in the future.\n\nLet's discuss the `ref_block_*` parameters a little: The `ref_block_num` indicates a particular block in the past by referring to the block number which has this number as the last two bytes. The `ref_block_prefix` on the other hand is obtain from the block id of that particular reference block. It is one unsigned integer (4 bytes) of the block id, but not starting at the first position but with an offset of 4 bytes. This would be the corresponding python code to obtain the current head block and calculate those two parameters:\n\n    dynBCParams = noderpc.get_dynamic_global_properties()\n    ref_block_num = dynBCParams[\"head_block_number\"] & 0xFFFF\n    ref_block_prefix = struct.unpack_from(\"<I\", unhexlify(dynBCParams[\"head_block_id\"]), 4)[0]\n\nThe purpose of these two parameters is to prevent replay attacks in the case of a fork. Once two chains have forked, the two parameters identify two different blocks. Applying a signed transaction of one chain at another chain will invalidate the signature.\n\n## Serialization\n\nBefore we can start signing, we first need to define **what** is actually signed. And since JSON objects don't have a particular order and are unnecessary big in size, we first perform what is called a **serialization**. Technically speaking, if you serialize a (JSON) object, you will end up with a simple binary vector (read string) that contains the very same information but has a very strict structure as to where things need to be placed and how. It is essentially just a different representation of the content that makes **signing** easier and the signature unique and matching independent of how the JSON keys are sorted.\n\nThe nice thing about **signed** transactions in Steem is that the API nodes accept them in their JSON format so that we can read what it does in plain text. That said, a valid signed transaction takes the form:\n\n    {'expiration': '2016-08-09T10:06:15',\n     'extensions': [],\n     'operations': [['vote',\n                     {'author': 'piston',\n                      'permlink': 'xeroc',\n                      'voter': 'xeroc',\n                      'weight': 10000}]],\n     'ref_block_num': 61828,\n     'ref_block_prefix': 3564338418,\n     'signatures': ['201b35c7f94d2ae56d940863a8db37edff78e3b8f4935b6c6fc131a04b92d0f9596c368128ab298c446143915e35996a9644314fff88b6a6438946403ec7249a24']}\n\nWhile the `signatures` have been derived from the binary, serialized representation of the transaction!\n\nWe start by giving our operations numbers instead of calling them `vote`, `comment`, `transfer`, ..\n\n    # Operation types\n    operations = {}\n    operations[\"vote\"] = 0\n    # ....\n\nThese numbers are defined in the [steemd source code](https://github.com/steemit/steem/blob/master/libraries/chain/include/steemit/chain/protocol/operations.hpp#L10) or in [python-steem](https://github.com/xeroc/python-steemlib/blob/master/steembase/operations.py#L6).\n\nNow let's get started by serializing the first two entries as per the [definition of a transactions](https://github.com/steemit/steem/blob/master/libraries/chain/include/steemit/chain/protocol/transaction.hpp#L10):\n\n    buf = b\"\"\n\n    # ref_block_num\n    buf += struct.pack(\"<H\", tx[\"ref_block_num\"])\n\n    # ref_block_num\n    buf += struct.pack(\"<I\", tx[\"ref_block_prefix\"])\n\nIn this case, `<` denotes that we are storing integers in their **little-endian** form. `H` is a unsigned integer of length 2 (`uint16`) while `I` is an unsigned integer of length 4 (`uint32`).\nWe simple append them to our newly created serialization buffer `b`.\n\nThe next in our list of elements for the serialization is the expiration time. Now the difficulty is that the *actual time* is represented differently in the JSON object above and the serialized form for signing. That's why we are converting it to later be able to represent the **timestamp** as an unsigned integer (`uint32`).\n\n    # expiration\n    timeformat = '%Y-%m-%dT%H:%M:%S%Z'\n    buf += struct.pack(\"<I\", timegm(time.strptime((tx[\"expiration\"] + \"UTC\"), timeformat)))\n\nNext, we add the operations, one at a time. And for the serialization to know how many there will come, we first add the actual number of operations as a `varint` encoded integer first:\n\n    buf += bytes(varint(len(tx[\"operations\"])))\n\n\nwe now go through all of our operations (we just have the `vote` operation in our example) and the first thing we add to our serialization buffer is an `id` that identifies our operation. We have collected all of them above such that `operations[\"vote\"]=0`. This id is also `varint` encoded.\n\n    for op in tx[\"operations\"]:\n\n        # op[0] == \"vote\"\n        buf += varint(operations[op[0]])\n\nAll of the above is basically the same independent of the operations themselves. Now we need to distinguish how they are encoded/serialized and for sake of simplicity, I focus on the `vote` operation only.\nIt contains (as operation data) the following elements **in that order**:\n\n1. voter,\n2. author,\n3. permlink, and\n4. weight.\n\nThe first three are all represented as lenght-prefixed strings. That means:\n\n1. add the lenght of the string encoded as `varint` to the buffer, then\n2. add the string itself to the buffer\n\nThis we can do for the voter, the author and the permlink. The weight is a short (signed) integer of length 2. Instead of an unsigned integer, we here need to distinguish between upvotes and downvotes. Also note that 100% is represented as +10000 and -100% is -10000 and that we only use integer and no floats (the back-end doesn't know floats at all!)\n\n        if op[0] == \"vote\":\n            opdata = op[1]\n            buf += (varint(len(opdata[\"voter\"])) +\n                    bytes(opdata[\"voter\"], \"utf-8\"))\n            buf += (varint(len(opdata[\"author\"])) +\n                    bytes(opdata[\"author\"], \"utf-8\"))\n            buf += (varint(len(opdata[\"permlink\"])) +\n                    bytes(opdata[\"permlink\"], \"utf-8\"))\n            buf += struct.pack(\"<h\", int(opdata[\"weight\"]))\n\n## Serialization result\n\nSo much for adding operations to our serialization. Let's take a look at the binary form:\n\nFirst we serialize the TaPOS parameter `ref_block_num` (`36029`)\n\n    bd8c..............................................................\n\nand `ref_block_prefix` (`1164960351`) and obtain\n\n    ....5fe26f45......................................................\n\nThen we add our expiration `2016-08-08T12:24:17`\n\n    ............f179a857..............................................\n\nAfterwards, we need to append the number of operations (`01`)\n\n    ....................01............................................\n\nAnd go through our operation:\n\nthe operation id (`0`)\n\n    ......................00..........................................\n\nthe voter (`xeroc`, length `5`)\n\n    ........................057865726f63..............................\n\nthe author (`xeroc`, length `5`)\n\n    ....................................057865726f63..................\n\nthe permlink (`piston`, length `6`)\n\n    ................................................06706973746f6e....\n\nand the weight of the vote (`+1000`)\n\n    ..............................................................1027\n\nThe end result for our serialized transaction looks like this:\n\n    bd8c5fe26f45f179a8570100057865726f63057865726f6306706973746f6e1027\n\n# ECC Signing \n\nNow comes the hard part. The actual signing of the serialized transactions. That means that going forward we need:\n\n* The serialized buffer\n* The chain id (to distinguish different instances of the graphene chain and also prevent replay attacks)\n* The private keys you want to use for signing (whether those keys are sufficient to actually **authorize** the transaction is left to the developer to ensure)\n\nIn the case of **STEEM**, the chain id is a 256bit long `0` sequence (the one and only :P). We use this chain id, represent it in binary form and append the serialized buffer to obtain the *message*. Instead of signing the actual message, we are interested in signing the `SHA256` hash of the message. This hash of a message is called *digest*.\n\n    # Signing\n    chainid = \"0\" * int(256 / 4)\n    message = unhexlify(chainid) + buf\n    digest = hashlib.sha256(message).digest()\n\nNow we take all private keys and sign our transaction with all of them. Each private key will result in a single signature that needs to be added to the `signatures` key of the original transactions. In our case, we just work with a single private key, represented in WIF. We obtain the actual binary private key from the WIF (for sake of simplicity, I use the PrivateKey class from `steembase.account`)\n\n    wifs = [\"5JLw5dgQAx6rhZEgNN5C2ds1V47RweGshynFSWFbaMohsYsBvE8\"]\n    sigs = []\n    for wif in wifs:\n        p = bytes(PrivateKey(wif))  # binary representation of private key\n\nFortunately for us, we don't need to do all the Signing by hand, but can use the `ecdsa` python package. We obtain a *SigningKey* by loading our private key properly:\n\n        sk = ecdsa.SigningKey.from_string(p, curve=ecdsa.SECP256k1)\n\nNow, we are going to implement a loop that is crucial because the back-end only accepts *canonical* signatures and we have no way of knowing if the signature that is going to be produced will be canonical. That said we will derive a deterministic `k` parameter for the ECDSA signing and while generating this parameter will will add our loop counter to the digest before hashing. This results in a new deterministic `k` each round which will result in either a canonical or non-canonical signature.\n\n        cnt = 0\n        i = 0\n        while 1 :\n            cnt += 1\n            # Deterministic k\n            #\n            k = ecdsa.rfc6979.generate_k(\n                sk.curve.generator.order(),\n                sk.privkey.secret_multiplier,\n                hashlib.sha256,\n                hashlib.sha256(digest + bytes([cnt])).digest())\n\nThe signature is generated by using the proper ECDSA signing call for digests:\n\n            # Sign message\n            #\n            sigder = sk.sign_digest(\n                digest,\n                sigencode=ecdsa.util.sigencode_der,\n                k=k)\n\nNow we represent the signature in its `r` and `s` values and verify that it is canonical. If it is, then we break the loop and continue:\n\n            # Reformating of signature\n            #\n            r, s = ecdsa.util.sigdecode_der(sigder, sk.curve.generator.order())\n            signature = ecdsa.util.sigencode_string(r, s, sk.curve.generator.order())\n\n            # Make sure signature is canonical!\n            #\n            lenR = sigder[3]\n            lenS = sigder[5 + lenR]\n            if lenR is 32 and lenS is 32 :\n                # ........\n\nOnce we ensured that the signature is canonical, we derive the so called *recovery paramter*. It simplifies the verification of the signature as it links the signature to a **single** unique public key. Without this parameter, the back-end would need to test for multiple public keys instead of just one. So we derive this parameter, add 4 and 27 to stay compatible with other protocols and have now obtained our signature.\n\n                # Derive the recovery parameter\n                #\n                i = recoverPubkeyParameter(digest, signature, sk.get_verifying_key())\n                i += 4   # compressed\n                i += 27  # compact\n                break\n\nHaving derived a valid canonical signature, we format it in its hexadecimal representation and add it our transactions signatures. Note that we do not only add the signature, but also the recover parameter. This kind of signature is then called **compact** signature.\n\n        tx[\"signatures\"].append(\n            hexlify(\n                struct.pack(\"<B\", i) +\n                signature\n            ).decode(\"ascii\")\n        )\n\nDone.\n\nOnce you have derived your new `tx` including the signatures you could use python-steem to verify your transaction and it's signature by doing something like this:\n\n    from steembase import transactions\n    tx2 = transactions.Signed_Transaction(**tx)\n    tx2.deriveDigest(\"STEEM\")\n    pubkeys = [PrivateKey(p).pubkey for p in wifs]\n    tx2.verify(pubkeys, \"STEEM\")\n\nIf it doesn't throw an exception your did everything right!\n\nThe full source code that we have developed in this example can be found [on github](https://gist.github.com/9bda11add796b603d83eb4b41d38532b).\n\nHave fun coding.",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "steem",
      "children": 16,
      "children_abs_rshares": 0,
      "created": "2016-08-09T11:45:27",
      "curator_payout_value": {
        "amount": "74678",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 522496,
      "json_metadata": "{\"tags\": [\"piston\", \"python\", \"graphene\", \"cryptography\"]}",
      "last_payout": "2016-09-09T03:36:30",
      "last_update": "2016-08-09T12:12:39",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": 0,
      "net_votes": 302,
      "parent_author": "",
      "parent_permlink": "steem",
      "percent_steem_dollars": 10000,
      "permlink": "steem-transaction-signing-in-a-nutshell",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "steem-transaction-signing-in-a-nutshell",
      "title": "Steem transaction signing in a nutshell",
      "total_payout_value": {
        "amount": "1002662",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-08-10T20:04:03",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 365745,
      "beneficiaries": [],
      "body": "I am pleased to announce that [streemian.com](http://www.streemian.com)\nnow offers [RSS feeds](http://www.streemian.com/info/rss/).\n\nStreemian.com is hosted on [heroku](http://heroku.com) and is powered by\n[piston](http://piston.rocks).\n\n![](http://piston.readthedocs.io/en/develop/_images/ico-piston-typo.svg)\n\n### 10 most recent posts:\n\n    /rss/\n\n* [/rss](http://www.streemian.com/rss)\n\n### 10 most recent posts with tag `[tag]`:\n\n    /rss/[tag]\n\n* [/rss/steem](http://www.streemian.com/rss/steem)\n* [/rss/politics](http://www.streemian.com/rss/politics)\n* [/rss/bitshares](http://www.streemian.com/rss/bitshares)\n* ...\n\n### Blog posts of user `[author]`\n\n    /rss/@[author]\n\n* [/rss/@streemian](http://www.streemian.com/rss/@streemian)\n* [/rss/@ned](http://www.streemian.com/rss/@ned)\n* ...\n\n### Replies made to posts from `[author]`\n\n    /rss/@[author]/replies\n\n* [/rss/@streemian/replies](http://www.streemian.com/rss/@streemian/replies)\n* [/rss/@ned/replies](http://www.streemian.com/rss/@ned/replies)\n* ...",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "steem",
      "children": 18,
      "children_abs_rshares": 0,
      "created": "2016-07-06T08:11:03",
      "curator_payout_value": {
        "amount": "9448",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 41231,
      "json_metadata": "{\"tags\":[\"steem\"],\"links\":[\"http://www.streemian.com\"]}",
      "last_payout": "2016-08-23T13:37:06",
      "last_update": "2016-07-06T08:21:03",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": 0,
      "net_votes": 82,
      "parent_author": "",
      "parent_permlink": "steem",
      "percent_steem_dollars": 10000,
      "permlink": "streemian-com-now-offers-rss-feeds-for-steem",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "streemian-com-now-offers-rss-feeds-for-steem",
      "title": "Streemian.com now offers RSS feeds for STEEM",
      "total_payout_value": {
        "amount": "85893",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-07-29T10:31:15",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 488179,
      "beneficiaries": [],
      "body": "It's my pleasure to publish the [first python-based faucet for BitShares](https://github.com/xeroc/tapin).\n\nIt is API-compatible to the existing faucet and should play nicely with the wallet. All you need to do is deploy one at a (sub-)domain you control and redirect the wallet's faucet to that address.\n\n### Configuration\n\nThere is `config-example.py` that shows what has to go where as well as a `config.py` that is an example that works nicely with environmental variables together with *heroku*.\n\n### Deployment\n\nIt's uses a temporary SQLite database to store account names and prevent abuse. This database is created on the first query and nothing needs to be done extra. If you want to use a different SQL database you need to modify it in `__init__.py`.\n\nEasy deployment is available through a *heroku* deploy button. It will ask you for all required data and automatically run and host it for you depending on the plan you are using.\n\n### Development\n\nCurrently, *tapin* is still very basic and I am looking for people that want to help improve it beyond what is here already. Help is welcome. If you need something very specify, drop me a line and we can find a way to get what you need.\n\n### Funding\n\n*Tapin* was funded via the BitShares Python Worker is licensed under MIT and free to use for anything! That said, you don't need to upvote this post just to get this funded.",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "bitshares",
      "children": 1,
      "children_abs_rshares": 0,
      "created": "2016-07-04T11:56:42",
      "curator_payout_value": {
        "amount": "6584",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 37868,
      "json_metadata": "",
      "last_payout": "2016-08-24T08:45:48",
      "last_update": "2016-07-04T11:56:42",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": 0,
      "net_votes": 73,
      "parent_author": "",
      "parent_permlink": "bitshares",
      "percent_steem_dollars": 10000,
      "permlink": "tapin-python-based-bitshares-faucet-released",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "tapin-python-based-bitshares-faucet-released",
      "title": "[Tapin] Python-based BitShares Faucet Released",
      "total_payout_value": {
        "amount": "113364",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-06-01T11:22:30",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 0,
      "beneficiaries": [],
      "body": "![](http://i.imgur.com/SBfpMjX.jpg)",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "spam",
      "children": 1,
      "children_abs_rshares": 0,
      "created": "2016-06-01T11:10:18",
      "curator_payout_value": {
        "amount": "0",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 14862,
      "json_metadata": "",
      "last_payout": "2016-08-03T00:00:06",
      "last_update": "2016-06-01T11:10:18",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": -9898531526661,
      "net_votes": 3,
      "parent_author": "",
      "parent_permlink": "spam",
      "percent_steem_dollars": 10000,
      "permlink": "testing---wed-jun--1-130937-cest-2016",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "testing---wed-jun--1-130937-cest-2016",
      "title": "Testing - Wed Jun  1 13:09:37 CEST 2016",
      "total_payout_value": {
        "amount": "0",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": 0,
      "active": "2016-06-01T11:37:57",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 0,
      "beneficiaries": [],
      "body": "![](http://i.imgur.com/SBfpMjX.jpg)",
      "cashout_time": "1969-12-31T23:59:59",
      "category": "spam",
      "children": 2,
      "children_abs_rshares": 0,
      "created": "2016-06-01T11:24:36",
      "curator_payout_value": {
        "amount": "0",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 14869,
      "json_metadata": "{\"image\":[\"http://i.imgur.com/SBfpMjX.jpg\"]}",
      "last_payout": "2016-08-19T03:37:36",
      "last_update": "2016-06-01T11:24:36",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "1969-12-31T23:59:59",
      "net_rshares": -12227763950349,
      "net_votes": 7,
      "parent_author": "",
      "parent_permlink": "spam",
      "percent_steem_dollars": 10000,
      "permlink": "testing-a-new-bot",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "testing-a-new-bot",
      "title": "Testing a new bot",
      "total_payout_value": {
        "amount": "0",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": 0
    },
    {
      "abs_rshares": "30408682244",
      "active": "2016-08-22T18:41:12",
      "allow_curation_rewards": true,
      "allow_replies": true,
      "allow_votes": true,
      "author": "xeroc",
      "author_rewards": 2130,
      "beneficiaries": [],
      "body": "Please do not upvote",
      "cashout_time": "2016-09-19T09:21:15",
      "category": "spam",
      "children": 18,
      "children_abs_rshares": "125728956195",
      "created": "2016-08-19T08:25:36",
      "curator_payout_value": {
        "amount": "113",
        "nai": "@@000000013",
        "precision": 3
      },
      "depth": 0,
      "id": 667596,
      "json_metadata": "{\"tags\": [\"test\", \"spam\"]}",
      "last_payout": "2016-08-20T09:21:15",
      "last_update": "2016-08-19T08:25:36",
      "max_accepted_payout": {
        "amount": "1000000000",
        "nai": "@@000000013",
        "precision": 3
      },
      "max_cashout_time": "2016-09-04T09:29:39",
      "net_rshares": "27865778612",
      "net_votes": 92,
      "parent_author": "",
      "parent_permlink": "spam",
      "percent_steem_dollars": 10000,
      "permlink": "testing-tags",
      "reward_weight": 10000,
      "root_author": "xeroc",
      "root_permlink": "testing-tags",
      "title": "Testing tags",
      "total_payout_value": {
        "amount": "3166",
        "nai": "@@000000013",
        "precision": 3
      },
      "total_vote_weight": 0,
      "vote_rshares": "29137230428"
    }
  ]
}
