BSIP: 0060
Title: BitShares URI scheme
Authors: John Titor, Stefan Schießl, Abit More
Status: Draft
Type: Informational (Client Protocol)
Created: 2019-02-11
Discussion: https://github.com/bitshares/bsips/issues/131
A Uniform Resource Identifier (URI) is a string of characters that unambiguously identifies a particular resource.
The most common form of URI is the Uniform Resource Locator (URL), frequently referred to informally as a web address.
This BSIP defines a Uniform Resource Name (URN), for the BitShares network.
Currently, BitShares lack any URL/URI scheme, forcing users to laborously copy and paste several pieces of data from/to different bitshares software.
Bitcoin (and other cryptocurrencies) generally have an URI scheme, making it possible to embed payment links into websites, emails and other forms of digital media.
Having a schema also enables support to generate and read QR codes. QR codes are of great convenience to the users, and is a defacto method of transfering URLs between desktop and mobile applications.
Even a mere existance of this BSIP could greatly raise interoperability between different BitShares software implementations.
Some clients have gone ahead and implemented their own incompatible schemas, which were never agreed on. This is not healthy for the ecosystem.
The URI schema outlined in this BSIP is aimed to provide flexibility and future extensibility, while adhering to general Internet standards.
The most requested form of an URI (representing a potential transfer) is defined here, along with a more generic, all-base-covering approach.
The BitShares URI conforms to general URI structure, outlined in RFC1738.
url = protocol ":" path [ "?" params ]
Each URI points to an "noun", an object of some kind, and does not represent action (or "verb"). However, since BitShares Operation is an object, this scheme can express operations.
The protocol name is bitshares
.
protocol = "bitshares"
path = object_path | asset_path | account_path |
operation_path | block_path | transaction_path |
blind_receipt_path | public_key_path | market_path
All paths follow the path_type/path_ref
structure.
path_type
is always a string, and could be one of the object
, asset
, account
, operation
, market
, blind_receipt
, public_key
, block
or transaction
. Future additional types are allowed.
path_ref
s differ by path type. For example, a graphene object is referenced by X.Y.Z 3-integer id, while a blind commitment might be referenced by its 33-byte hash.
Note, that as BitShares blockchain evolves, some of the addressing schemes might get expanded, in which case the bitshares-core consensus always supersedes the rules specified in this BSIP.
Parameters are URL-encoded. Parameters are key/value pairs separated by "=" symbol, delimited by "&".
?key1=value1&key2=value2
Extended syntax for nesting deep objects is also allowed, so those are valid:
?obj1[key1]=value1&obj1[key2]=value2
?array[]=value1&array[]=value2
See Operation path definition for examples and rationale.
Raw Object paths start with "object", followed by a graphene_id
, which is 3 integers separated by a dot symbol, e.g. "1.2.0", as seen on BitShares blockchain.
Example: object/1.2.0
object_path = "object" "/" graphene_id
graphene_id = digit *[digit] "." digit *[digit] "." digit *[digit]
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
"8" | "9"
Asset paths start with the string "asset", followed by an asset_id_or_symbol
, which could be a asset_id
of the object, or its "symbol" property.
Example: asset/BTS
asset_path = "asset" "/" asset_id_or_symbol
asset_id_or_symbol = asset_id | asset_symbol
asset_id = "1.3." digit *[digit]
asset_symbol = asset_symbol_segment [ "." | asset_symbol_segment ]
asset_symbol_segment = hialpha *[ hialpha | digit ] [ hialpha | digit ]
hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
"J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
"S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
Account paths start with the string "account", followed by an account_id_or_name
, which could be a graphene_id
of the object, or its "name" property.
Example: account/dan
account_path = "account" "/" account_id_or_name
account_id_or_name = account_id | account_name
account_id = "1.2." digit *[digit]
account_name = account_name_segment *[ "." account_name_segment ]
account_name_segment = lowalpha *[ lowalpha | digit | "-" ] [ lowalpha | digit ]
lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
"i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
"q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
"y" | "z"
Market paths start with the word "market", followed by two asset_id_or_symbol
s.
Example:
market/BTS/USD
market/BTS_USD
market_path = "market" "/" asset_id_or_symbol [ "/" | "_" ] asset_id_or_symbol
Note:
the /
character has been chosen as the most preferable separator between the assets.
Due to historical reasons, the _
separator had already been used in some applications
before the first version of this document was published.
New applications may consider supporting both for compatibility.
Public Key paths start with the "public_key" word, followed by bitshares_public_key
.
A label
parameter MAY be present.
Example: public_key/BTS7T2swLv3QqBAzP1hByB5khUjNFEahtp1fYEHL2GFubMvNGVXyG?label=nathan
Client guideline: add key as a blind contact with label provided.
public_key_path = "public_key" "/" bitshares_public_key
bitshares_public_key = "BTS" 50*[ base58 ]
Operation paths start with the word "operation", followed by operation_name
(such as "transfer"), with all and any of the operation parameters as params
part.
Example: operation/transfer?to=init0&from=fox&asset=BTS&amount=1.3
Client guideline: open an interface to perform described operation, pre-populate input fields with parameters provided.
operation_path = "operation" "/" operation_name
operation_name = "transfer" | "limit_order_create" | "limit_order_cancel" |
"call_order_update" | "bid_collateral" | "account_create" |
"account_update" | "account_whitelist" | "account_upgrade" |
"account_transfer" | "asset_create" | "asset_update" |
"asset_update" | "asset_update_feed" | "asset_issue" |
"asset_reserve" | "asset_fund_fee_pool" |
"asset_settle" | "asset_global_settle" |
"asset_publish_feed" | "witness_create" |
"witness_update" | "proposal_create" |
"proposal_update" | "proposal_delete" |
"withdraw_permission_create" | "withdraw_permission_update" |
"withdraw_permission_claim" | "withdraw_permission_delete" |
"committee_member_create" | "committee_member_update" |
"custom" | "assert" |
"committee_member_update_global_parameters" |
"vesting_balance_create" | "vesting_balance_withdraw" |
"worker_create" | "balance_claim" | "override_transfer" |
"transfer_to_blind" | "blind_transfer" |
"transfer_from_blind" |
"asset_settle_cancel" | "asset_claim_fees" |
"asset_update_issuer" | "asset_claim_pool" |
"htlc_create" | "htlc_redeem" | "htlc_extend"
Here, parameter names MUST match the operation parameters. For example, classic Transfer operation has the following fields in the BitShares specification: from
, to
and memo
. To include those fields, same parameter names from
, to
and memo
MUST be used when URL is formed.
Deep objects MAY be represented via extended URL parameter syntax, for example, in BitShares, amounts are usually encoded as:
amount: { "asset_id": <asset_id>, "amount": <atomic_integer> }
This MAY be represented as ?amount[asset_id]=1.2.0&amount[amount]=255
.
Wallet software SHOULD respect such notation, making the process of adding new operations to the scheme seamless.
However, for simplicity sake, a shortening method SHOULD be implemented. Continuing with the amount example, a transfer operation MAY paramterize as
asset=<asset_id_or_symbol>
amount=<decimal_amount>
Similarly, the memo
field COULD be plain-text, and not the whole object.
A conversion table is provided below:
Operation | URI Parameter | Operation Parameter |
---|---|---|
transfer | asset=SYMBOL | amount[asset_id]=ASSET_ID |
transfer | amount=DECIMAL | amount[amount]=ATOMIC_INTEGER |
transfer | memo=CLEARTEXT | memo[message]=CIPHERTEXT, memo[nonce]=NONCE, ... |
limit_order_create | sell_asset=SYMBOL | asset_to_sell[asset_id]=ASSET_ID |
limit_order_create | sell_amount=DECIMAL | asset_to_sell[amount]=ATOMIC_INTEGER |
limit_order_create | buy_asset=SYMBOL | min_to_receive[asset_id]=ASSET_ID |
limit_order_create | buy_amount=DECIMAL | min_to_receive[amount]=ATOMIC_INTEGER |
override_transfer | asset=SYMBOL | amount[asset_id]=ASSET_ID |
override_transfer | amount=DECIMAL | amount[amount]=ATOMIC_INTEGER |
vesting_balance_withdraw | asset=SYMBOL | amount[asset_id]=ASSET_ID |
vesting_balance_withdraw | amount=DECIMAL | amount[amount]=ATOMIC_INTEGER |
asset_issue | asset=SYMBOL | asset_to_issue[asset]=ASSET_ID |
asset_issue | amount=DECIMAL | asset_to_issue[amount]=ATOMIC_INTEGER |
asset_issue | to=ACCOUNT_NAME | issue_to_account=ACCOUNT_ID |
asset_issue | memo=CLEARTEXT | memo[message]=CIPHERTEXT, memo[nonce]=NONCE, ... |
asset_fund_fee_pool | asset=SYMBOL | asset_id=ASSET_ID |
asset_fund_fee_pool | amount=DECIMAL | amount=ATOMIC_INTEGER |
asset_reserve | asset=SYMBOL | amount_to_reserve[asset_id]=ASSET_ID |
asset_reserve | amount=DECIMAL | amount_to_reserve[amount]=ATOMIC_INT |
account_update | vote_for=OBJECT_ID | new_options[votes][ ]=VOTE_FOR_ID |
account_update | vote_against=OBJECT_ID | new_options[votes][ ]=VOTE_AGAINST_ID |
Each operation MAY also have an optional fee_asset
parameter, with desired asset_id_or_symbol
of the fee asset.
Operation names are defined here:
http://docs.bitshares.org/tutorials/construct-transaction.html#_CPPv2N8graphene5chain9operationE
NOTE: ATOMIC_INTEGER specified here and above refers to indivisible "satoshi" amounts of tokens, as used internally on BitShares blockchain.
Blind Receipt paths start with the word "blind_receipt", followed by base58_blind_receipt
.
Client guideline: it might be a good idea to try and accept such blind transfer.
blind_receipt_path = "blind_receipt" "/" base58_blind_receipt
base58_blind_receipt = base58 *base58
base58 = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" |
"9" | "A" | "B" | "C" | "D" | "E" | "F" | "G" |
"H" | "J" | "K" | "L" | "M" | "N" | "P" | "Q" |
"R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" |
"Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" |
"h" | "i" | "j" | "k" | "m" | "n" | "o" | "p" |
"q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
"y" | "z"
Block / TRX / Operation paths all start with the word "block", but can point to either a whole block, either a transaction in that block, either an op within that transaction (with virtual op to help navigate).
Examples are:
block/2333 (points to block 2333)
block/2333/6 (points to tx 6 in block 2333)
block/2333/6/0 (points to op 0 in tx 6 in block 2333)
Client guideline: show block explorer, highlight operation.
block_path = "block" "/" block_num [ "/" trx_in_block [
"/" op_in_trx [ "/" virtual_op ] ] ]
block_num = digit *[digit]
trx_in_block = digit *[digit]
op_in_trx = digit *[digit]
virtual_op = digit *[digit]
Example: transaction/35212ab2cf4683202e10c16516f5ea64637d7d47
Client guideline: show transaction on block explorer.
transaction_path = "transaction" "/" transaction_hash
transaction_hash = 40*[ alpha | digit ]
alpha = lowalpha | hialpha
BitShares clients MUST NOT act on URIs without getting the user's authorization. They SHOULD require the user to manually approve each operation individually, though in some cases they MAY allow the user to automatically make this decision.
Specific guidelines listed in this BSIP for each URI type are mere suggestions, clients MAY choose to select their own behaviors.
Graphical BitShares clients SHOULD register themselves as the handler for the "bitshares:" URI scheme by default, if no other handler is already registered. If there is already a registered handler, they MAY prompt the user to change it once when they first run the client.
A shortening scheme was proposed, during the discussion, which allows to replace each path_type
string with a shorter version.
It would win us some bytes in the URL, although it would also make parsers a bit harder to write. The proposed short versions are:
full type | short type |
---|---|
object | ob |
operation | op |
block | bl |
transaction | trx |
In addition, instead of spelling out operation type (e.g. "transfer"), its id number could be used (so the operation/transfer?to=...
could be shortened to op/0?to=...
).
Some concerns were raised about the (byte-) length of the URI, in particular relation to QR code generation. It should be noted, that long URLs are not really a problem, and the QR code standard makes it possible to encode very long messages without any troubles.
For reference, Monero (a popular cryptocurrency) wallet addresses are 95 bytes long, and their URLs look like monero:4AgaTcpcQkXdmwtJnzX62r9vG8idcn6q845uoeh7mcfKehLcSrMi1zdJDZDJudMnNR3gK4PmKV4gXCyX7L1NUc3Y8aMNTvp?tx_amount=0.00123
this. In contrast, a transfer operation bitshares:operation/transfer?to=very_long_account_name&asset=BTS&amount=0.00123
from this BSIP, takes only 79 bytes, yet expresses much more data.
Adding an agreed-on standard for the URI schema will allow different BitShares software to interoperate and share data on user interface level.
It will also enable potential support for QR codes, to be used for payments, sharing contacts, and other features.
This BSIP is placed in the public domain.
Prior work, bitshares1 XTS URLs: https://github.com/bitshares/bitshares1-core/wiki/XTS-URLs