BSIP: 72
Title: Tanks and Taps: A General Solution for Smart Contract Asset Handling
Authors: Nathan Hourt <[email protected]>
Status: Accepted
Type: Protocol
Created: 2019-08-27
Updated: 2020-03-16
Discussion: https://github.com/bitshares/bsips/issues/178
This BSIP proposes the addition of novel, generally-applicable asset handling functionality to BitShares. These additions will support the development of a wide range of financial decentralized applications (dapps), advancing BitShares as an alternative platform to existing Turing Complete Smart Contract Platforms (TCSCPs) for dapp development. Although the changes will not add Turing Completeness to BitShares, and TCSCPs will continue to offer more flexible capabilities, the proposed infrastructure will offer a simple, ready-to-use interface to dapp developers which will facilitate more rapid development and a reduced time to market compared with generic Turing Complete platforms, which require dapps to define their own asset management logic. Furthermore, dapps utilizing the proposed functionality will share a consistent, easy-to-visualize asset handling model which will become increasingly familiar to users of dapps based on BitShares, giving them confidence when using BitShares dapps that they understand how their assets are being handled and what options are available to themselves or others at the lowest level.
One of the main promises of blockchain technology is the processing and execution of "smart contracts," a formalized agreement between parties where the terms are evaluated and enforced automatically rather than relying on trusted intermediaries. Initially, there was Bitcoin, which offers a simple smart contract definition language allowing the transfer of digital tokens between cryptographic keys. Subsequently, the industry has created multiple Turing Complete Smart Contracting Platforms (TCSCPs), such as Ethereum and EOS, in order to provide more advanced smart contract definition languages which can specify any programmable algorithm as a smart contract.
BitShares was created as a financial services smart contracting platform, providing high-performance decentralized financial contracts including an asset exchange, stable-valued assets, recurring payments, and an advanced multi-signature named account system. This curated selection of contracts is developed and supported by a highly competent team of developers who are committed to the quality, security, and correctness of the supported smart contracts. These assurances of contract quality give BitShares an advantage over TCSCPs, where contracts are user-contributed with no expectation of testing or correctness standards; however, to date, neither the more flexible Turing Complete model nor the quality-assured Curated model have clearly succeeded, either in terms of developer support or user adoption.
This proposal is to implement within BitShares a general framework for smart contract asset handling, capable of supporting a great many decentralized applications (dapps) and real world smart contracts. The benefit of such a uniform asset handling framework, as opposed to the TCSCP approach of generally supporting any and all potential asset management designs, is twofold. Firstly, it provides, across all BitShares dapps, a simple and consistent representation of user funds, making it easy for users to understand and reason about the status of assets within the contract, and what options exist for moving assets through the contract. Secondly, specifying a particular structure for asset handling gives developers a clear path for implementing their dapp's asset management, reducing their task from the creation of a novel asset handling architecture for their specific dapp, to merely expressing their dapp's asset movement possibilities within an existing, battle-tested framework.
With this asset handling infrastructure implemented, BitShares would enjoy significant advantages over TCSCPs for dapp development. Much of the complexity of designing and implementing dapps which handle user funds lies in the creation of secure mechanisms for holding funds within the contract, and the represention of these mechanisms to the user in a comprehensible fashion. By providing a simple and consistent framework for asset handling, BitShares offers developers a simpler and faster implementation path by providing sturdy and supported infrastructure for asset handling which provides users a familiar and consistent representation of the status of contract funds.
The proposed asset handling framework is a series of asset depositories called "tanks," which have one or more withdrawal mechanisms called "taps." Tanks are simple database objects which track a balance of asset contained within them. A tank has one or more taps on it, which allow withdrawing asset from the tank. Taps are locked such that only specific authorities can open them, and may have restrictions on when or why they can be opened or how much asset can flow through them. Taps are connected to other asset depositories, such as another tank, or an account, and when opened, the asset that flows through them are deposited there. More asset may be added to a tank at any time.
All tanks will be required to have at least one tap which is capable of draining the entire tank immediately. This tap can be thought of as an emergency release valve, and will typically have an authority requiring a supermajority approval of all contract parties. This tap will typically not be connected to any destination until and unless it is used. Its purpose is to handle scenarios where real-world conditions have gone outside the bounds of scenarios planned for under contract (for example, a contract party dies or is otherwise rendered incapable of completing the contract as negotiated), and it becomes necessary to renegotiate the contract mid-flight, potentially rewriting rules for how contract funds can be handled. In such emergent scenarios, it is imperative that the contract platform have a contingency that allows contract parties to reallocate funds based upon renegotiated terms, since the prior-negotiated rules of fund allocation may no longer be usable. In most cases, if the emergency tap is used, it will be connected to a new tank with newly negotiated taps immediately before it is opened, and all funds will be drained to the new tank.
Taps are somewhat more intricate than tanks, as they define the authorities, limitations, and requirements for opening them. First, a tap specifies an authority required to open it. Next, it specifies requirements to open it, such as a requirement for a written reason for the withdrawal, or an approval from a second authority. Finally, it specifies limitations as to how much asset can flow through it once opened.
The power of this model lies in its simplicity. It mimics how money moves in real-world contracts: a balance is locked up for a purpose in advance, when exactly how that balance will be spent is not yet known. As expenditures arise, they are paid out of the balance by an authorized party. Only certain kinds of expenditures can be paid from the balance, and in some cases, a justification or review is required. Eventually, the balance may need to be topped up to support further expenditures. It is anticipated that this model will be general enough to support many smart contract use cases.
A further advantage of this model is that it is easy to visualize, both the current state of contract funds, and the effects a transaction under consideration will have. This is crucial, as it allows the development of intuitive GUIs which represent the status of contract funds to users in a readily accessible fashion, giving them confidence that they understand how their money is being handled and how they can interact with it under terms of contract.
The Tanks and Taps protocol is designed around several fundamental types, including tank_object
, tap
, tap_requirement
, tank_attachment
, and connection
. A tank_object
is an object which contains a volume of asset. A tap
is an object attached to a tank_object
which is capable of releasing asset from the tank. A tap_requirement
is a static_variant
of various requirement types which can be attached to a tap
to impose limits and requirements on when and why a tap
can be opened, and how much asset can flow through it. A tank_attachment
is a static_variant
which may contain one of several structures to be stored on the tank to provide additional functionality for the tank or taps. Finally, a connection
is a static_variant
of various types which specify what shall be done with asset that has flowed through a tap
. A tank_object
has one or more tap
s; a tap
may contain zero or more tap_requirement
s and is optionally connected to a destination where all released funds will go. A tap
must be connected to a destination before it can be opened, and the destination must allow the incoming connection in order for the tap
to open and flow successfully.
When a tap
is opened, the amount of asset to release must be known. A tap
can be opened in one of two modes: to release a fixed amount of asset, or to release the maximum amount of asset available. In the former case, the amount to release is set in the transaction that opens the tap
. In the latter case, the amount to release is limited by the tap_requirement
s and the amount of asset remaining in the tank, and the actual amount released is equal to the lower of the two limits.
A tap_requirement
is attached to a tap
and locks the tap
until certain conditions are met. tap_requirement
s contain several parameters which are defined when the requirement is created, and may require arguments to be passed in the transaction when unlocking or opening the tap
. Some tap_requirement
s are stateful; requirement state is stored by the tank_object
. Initially, the following tap_requirement
types are specified:
immediate_flow_limit
Specifies a fixed maximum amount of asset that may flow through the tap in a given openingcumulative_flow_limit
Specifies a maximum amount of asset that may flow through the tap before it locks permanently (works in conjunction with anasset_flow_meter
)periodic_flow_limit
[Stateful] Specifies a maximum amount of asset that may flow through the tap before it locks until a predefined time period elapses (works in conjunction with anasset_flow_meter
)time_lock
Specifies time periods during which the tap is lockedminimum_tank_level
Specifies a minimum amount in the tank, such that the tap cannot drain the tank past that minimumreview_requirement
[Stateful] Specifies a reviewer authority which must approve requests to unlock the tapdocumentation_requirement
Requires documentation of the reason for opening the tap, such as a cost justification, without requiring review of this documentationdelay_requirement
[Stateful] Requires a delay before tap unlocks, with optional veto authority which can prevent tap from unlocking even after the delay expireshash_preimage_requirement
Requires a byte sequence which hashes to a predefined digest to unlock the tapticket_requirement
[Stateful] Requires a ticket (see code specification below) signed by a predefined key granting permission to release a limited volume of fundsexchange_requirement
[Stateful] Checks anasset_flow_meter
(see tank attachments below) and applies an exchange rate to calculate the maximum flow limit
A tank_attachment
is attached to a tank and provides additional functionality for the tank or associated taps. Attachments may be created to perform actions when certain events occur on the tank, track statistics, restrict what kinds of actions can be taken on the tank or who can take them, authorize updates to tank components, etc. Attachments can receive asset, but cannot store it, thus attachments which receive asset must specify a connection
to a further destination for funds it receives. Tank attachments cannot directly move asset into or out of a tank; only tap
s can take asset out of a tank, and only a connection
can put asset into a tank. Tank attachments may be stateful, and their state will be stored by the hosting tank_object
. Initially, the following tank attachments are specified:
asset_flow_meter
[Stateful] An attachment which can receive funds, and tracks the amount of asset that has flowed through it, then releases the asset to a predefined connectiontap_opener
An attachment which, when it receives asset, queues a predetermined tap to be opened automatically after the current flow stopsattachment_connect_authority
An attachment which allows a specified authority to update theconnection
a specified attachment on the same tank releases asset to
A connection
provides a common interface for moving asset within Tanks and Taps structures. At a data level, a connection is small and simple, storing only the ID of the object receiving the asset; however, the connection logic will record the source of the deposit and the path the asset takes as it moves, and this information may be used when processing the deposit to trigger events, log statistics, or detect errors. Initially, the following connection
types are specified:
- An account ID, to which the asset will be deposited as a balance
- A tank ID, to add asset to the tank balance
- A tank attachment ID, to send asset through a tank attachment which can receive funds
Deposit Path Restrictions A tank and its attachments can always receive asset from that same tank or its own attachments; however, it is necessary to restrict deposits from remote sources (other tanks or accounts) so that they are processed as intended and cannot be misdirected by a misconfigured transaction or contract. These restrictions are specified with the authorized_connections_type
, which is present on all tanks and attachments which receive asset. The authorized_connections_type
specifies for a receiver of asset what remote sources are recognized to send asset to it. It may also allow funds from all sources. Funds coming from an unauthorized source will be rejected and the transaction sending them will fail.
Tank Lifecycle Once created, a tank can remain in existence indefinitely and can be filled and emptied many times. It is preferred, however, that unused tanks be destroyed. There are two mechanisms by which a tank is destroyed: first is by a destructor tap, and second is by the tank_destroy
operation. A destructor tap is a kind of tap
which can destroy the tank after the tank is emptied, if configured to do so by the tap_open
operation. Any tap
can be created as a destructor tap, but the emergency tap must be a destructor tap. This is expected to be the most common method of destroying a tank, as it can be done within normal usage of the tank. Alternatively, a tank can be destroyed by using the tank_destroy
operation; however, this operation requires the emergency tap authority and is therefore unlikely to be frequently used in practice.
To incentivize the destruction of tanks that are no longer being used, a deposit of core asset is required to create a new tank. This deposit is held for the lifetime of the tank, and is released when the tank is destroyed. The deposit is returned directly to the account which pays the fee for the operation that destroys the tank; it is not sent through a tap
.
Restricted Assets Some assets specify account whitelists or blacklists, to restrict which accounts may transact in that asset. It is desired that these assets be able to utilize the Tanks and Taps infrastructure without opening the possibility of using Tanks and Taps to circumvent the restrictions on asset ownership; therefore, a restricted asset check will be enforced in three instances. First, when asset is released through a connection
to an account balance, a restriction check will be performed on that account. Second, every time a tap
is opened, the account which paid the fee on the transaction causing the tap to be opened is checked for authorization to use the released asset. Third, when a connection
receives asset from an account, that account checked for authorization to handle the asset. If any of these authorization checks fails, the transaction is rejected as invalid. With these checks in place, restricted assets can be used in conjunction with the Tanks and Taps architecture without compromising the efficacy of the asset restrictions.
Pseudocode definitions of these types are provided below:
attachment_id_type {
/// ID of the tank hosting the attachment
tank_id_type tank_id;
/// ID of the attachment on the tank
uint16 attachment_id;
}
type connection = static_variant<tank_id_type, account_id_type, attachment_id_type>;
struct all_sources{};
type authorized_connections_type = static_variant<flat_set<connection>, all_sources>;
struct unlimited_flow{};
type tap_flow_limit = static_variant<share_type, unlimited_flow>;
asset_flow_meter {
state_type {
/// The amount of asset that has flowed through the meter
share_type metered_amount;
}
/// The type of asset which can flow through this meter
asset_id_type asset_type;
/// The connection where the metered asset is released to
connection destination;
/// What remote sources, if any, can deposit to this meter
authorized_connections_type remote_sources;
/// The authority which may reset the meter; if null, only the emergency tap authority is accepted
optional<authority> reset_authority;
}
tap_opener {
/// Index of the tap to open (must be on the same tank as the opener)
uint16 tap_index;
/// The amount to release
tap_flow_limit release_amount;
/// The connection that asset is released to after flowing through the opener
connection destination;
/// What remote sources, if any, can deposit to this opener
authorized_connections_type remote_sources;
}
attachment_connect_authority {
/// Authority that may reconnect an attachment
authority connect_authority;
/// Attachment which may be reconnected
attachment_id_type attachment_id;
}
type tank_attachment = static_variant<asset_flow_meter, tap_opener,
attachment_connect_authority>;
immediate_flow_limit { share_type limit; }
cumulative_flow_limit {
struct state_type {
/// The amount of asset released so far
share_type amount_released;
};
share_type limit;
}
periodic_flow_limit {
state_type {
/// Sequence number of the period during which the last withdrawal took place
uint32 period_num = 0;
/// The amount released during the period
share_type amount_released;
}
/// Duration of periods in seconds
uint32 period_duration_sec;
/// Maximum cumulative amount to release in a given period
share_type limit;
}
time_lock {
/// If true, the tap is initially locked
bool start_locked;
/// At each of these times, the tap will switch between locked and unlocked --
/// must all be in the future
vector<time_point_sec> lock_unlock_times;
}
minimum_tank_level {
/// Minimum tank balance; tap cannot drain tank below this balance
share_type minimum_level;
}
review_requirement {
/// This type describes a request for withdrawal waiting for review or
/// for redemption
request_type {
/// Amount requested for release -- reset if reviewer denies request
optional<tap_flow_limit> request_amount;
/// Optional comment about request, max 150 chars -- reset if reviewer
/// denies request
optional<string> request_comment;
/// Starts false, set to true if request is approved, and back to false after
/// a release of funds
bool approved;
}
state_type {
/// Number of requests made so far; used to assign request IDs
uint16 request_counter;
/// Map of request ID to request
flat_map<uint16, request_type> pending_requests;
}
/// Authority which approves or denies requests
authority reviewer;
/// Maximum allowed number of pending requests; zero means no limit
index_type request_limit;
}
documentation_requirement {
/* no fields; if this requirement is present, evaluator requires a nonempty
* documentation argument exist, but does no further verification upon it
*/
}
delay_requirement {
/// This type describes a request for withdrawal waiting for its delay to pass
request_type {
/// When the request matures and can be consumed
optional<time_point_sec> delay_period_end;
/// Amount requested
optional<tap_flow_limit> request_amount;
/// Optional comment about request; max 150 chars
optional<string> request_comment;
}
state_type {
/// Number of requests made so far; used to assign request IDs
uint16 request_counter;
/// Map of request ID to request
flat_map<uint16, request_type> pending_requests;
}
/// Authority which can veto request during review period; if veto occurs,
/// reset state values
optional<authority> veto_authority;
/// Period in seconds after unlock request until tap unlocks; when tap opens,
/// all state values are reset
uint32 delay_period_sec;
/// Maximum allowed number of outstanding requests; zero means no limit
uint16 request_limit;
}
hash_preimage_requirement {
type hash_type = static_variant<sha256, ripemd160, hash160>;
/// Specified hash value
hash_type hash;
/// Size of the preimage in bytes; a preimage of a different size will be rejected
/// If null, a matching preimage of any size will be accepted
optional<uint16> preimage_size;
}
ticket_requirement {
/// The type of the ticket that must be signed to unlock the tap
struct ticket_type {
/// ID of the tank containing the tap this ticket is for
tank_id_type tank_id;
/// Index of the tap this ticket is for
uint16 tap_index;
/// Maximum asset release authorized by this ticket
tap_flow_limit max_withdrawal;
/// Must be equal to tickets_consumed to be valid
uint16 ticket_number;
}
state_type {
/// Number of tickets that have been used to authorize a release of funds
uint16 tickets_consumed;
}
/// Key that must sign tickets to validate them
public_key_type ticket_signer;
}
exchange_requirement {
/// The maximum release amount will be:
/// meter_reading / tick_amount * release_per_tick - amount_released
state_type {
/// The amount of asset released so far
share_type amount_released;
}
/// The ID of the meter to check
tank_attachment_id_type meter_id;
/// The amount to release per tick of the meter
share_type release_per_tick;
/// Amount of metered asset per tick
share_type tick_amount;
/// Authority which can reset the amount released; if null, only the
/// emergency tap authority is authorized
optional<authority> reset_authority;
}
type tap_requirement = static_variant<immediate_flow_limit, cumulative_flow_limit,
periodic_flow_limit, time_lock, minimum_tank_level,
review_requirement, documentation_requirement,
delay_requirement, hash_preimage_requirement,
ticket_requirement, exchange_requirement>;
tap {
/// The connected connection, if present
optional<connection> connected_connection;
/// The authority to open the tap; if null, anyone can open the tap if they can
/// satisfy the requirements
optional<authority> open_authority;
/// The authority to connect and disconnect the tap. If unset, tap must be connected
/// on creation, and the connection cannot be later modified -- emergency tap must
/// specify a connect_authority
optional<authority> connect_authority;
/// Requirements for opening this tap and releasing asset
vector<tap_requirement> requirements;
/// If true, this tap can be used to destroy the tank when it empties
bool destructor_tap;
}
tank_object {
/// Amount of asset contained in the tank
asset contained_asset;
/// Taps on this tank. ID 0 must be present, and must not have any tap_requirements
flat_map<uint16, tap> taps;
/// Counter of taps added; used to assign tap IDs
uint16 tap_counter;
/// Attachments on this tank
flat_map<uint16, tank_attachment> attachments;
/// Counter of attachments added; used to assign attachment IDs
uint16 attachment_counter;
/// What remote sources, if any, can deposit to this tank
authorized_connections_type remote_sources;
/// Amount of the deposit paid to create the tank (deposit is always core asset)
share_type deposit_amount;
}
This proposal additionally seeks to add eight operation types to the protocol:
tank_create
Creates a new tank, specifying taps, tap requirements, and sink connectionstank_update
Updates a tank, adding or removing taps (including requirements and sinks)tank_delete
Deletes a tank (fails if the tank contains asset)tank_query
Pass arguments to tap requirements or tank attachments to perform actions or comply with requirementstap_open
Release funds through a tap, passing arguments as necessary to satisfy tap requirementstap_connect
Connect a tap to a destinationaccount_fund_connection
Move asset from an account to a connection (such as a tank)connection_fund_account
Virtual operation, generated when a connection deposits asset into an account balance
Pseudocode definitions of these operations are provided below:
tank_create {
/// Account that pays fee and deposit
account_id_type fee_payer;
/// Deposit of core asset paid to create the tank
share_type deposit_amount;
/// Type of asset the tank will contain
asset_id_type tank_asset;
/// Taps on the tank -- index 0 must be present and must have no tap_requirements
vector<tap> taps;
/// Attachments on the tank
vector<tank_attachment> attachments;
/// Sources that are authorized to deposit to the tank
authorized_connections_type authorized_sources;
}
tank_update {
/// Account that pays fee
account_id_type fee_payer;
/// Authority to update tank: must match tank->taps[0].open_authority
authority update_authority;
/// ID of tank to update
tank_id_type tank_to_update;
/// Change in deposit amount on tank; credited or debited to fee payer
share_type deposit_delta;
/// IDs of taps to remove
flat_set<uint16> taps_to_remove;
/// Map of ID-to-new-value for taps to replace
/// Note that state data for all requirements of replaced taps will be deleted
flat_map<uint16, tap> taps_to_replace;
/// New taps to add
vector<tap> taps_to_add;
/// IDs of attachments to remove
flat_set<uint16> attachments to remove;
/// Map of ID-to-new-value for attachments to replace
/// Note that state data for replaced attachments will be deleted
flat_map<uint16, tank_attachment> attachments_to_replace;
/// New attachments to add
vector<tank_attachment> attachments to add;
/// Set this field to replace the tank's deposit source authorizations
optional<authorized_connections_type> new_authorized_sources;
}
tank_delete {
/// Account that pays fee
account_id_type fee_payer;
/// Amount of deposit reclaimed for deleting tank
share_type deposit_amount;
/// Authority to delete tank: must match tank->taps[0].open_authority
authority delete_authority;
/// ID of tank to delete; tank must be empty of asset to destroy
tank_id_type tank_to_delete;
}
tank_query {
/// Account that pays the fee
account_id_type fee_payer;
/// Authorities required to perform the queries
flat_set<authority> required_authorities;
/// ID of the tank to be queried
tank_id_type tank_id;
/// Contents of the queries to run (format is implementation-defined)
vector<tank_query_type> queries;
}
tap_open {
/// Account that pays the fee
account_id_type fee_payer;
/// Authorities required to satisfy the tap's requirements/open authority
flat_set<authority> required_authorities;
/// Any queries which should be run prior to opening the tap
/// (format is implementation-defined)
vector<tank_query_type> queries;
/// ID of the tank with the tap to be opened
tank_id_type tank_id;
/// Index of the tap to open
uint16 tap_index;
/// Amount to release from the tap
tap_flow_limit release_amount;
/// Total number of taps opened by this transaction, i.e. due to tap_openers
uint16 taps_to_open;
/// If emptying tank via a destructor tap, the deposit is returned to fee_payer
/// Specify amount of deposit here to enable tank destruction
optional<share_type> claimed_deposit;
}
tap_connect {
/// Account that pays the fee
account_id_type fee_payer;
/// Authority to connect the tap: must match
/// tank_id->taps[tap_index].connect_authority
authority connect_authority;
/// ID of the tank holding the tap to be opened
tank_id_type tank_id;
/// Index of the tap to be connected
uint16 tap_index;
/// Connection to connect the tap to; if null, tap will be disconnected
optional<connection> new_connection;
/// Clear the tap connect authority after connecting the tap; if true,
/// new_connection must be specified
bool clear_connect_authority;
}
account_fund_connection {
/// Account that provides funds, and pays fee; must be authorized to handle asset
account_id_type funding_account;
/// Destination for the funds
connection funding_destination;
/// Amount that the account is moving through the connection
asset funding_amount;
}
connection_fund_account {
/// Account receiving funds
account_id_type recipient;
/// Amount the recipient received
asset amount_received;
/// The path of connections the asset took to arrive at the account, including the origin
vector<tnt::connection> asset_path;
}
Additionally, these new committee parameters will be defined:
max_deposit_path_length
The maximum number of connections in a deposit pathmax_taps_to_open
The maximum number of taps a single transaction can opentank_deposit_amount
The amount of BTS required for a tank deposit Further parameters may be defined to control the amounts of tank deposits based on the details of the tank.
Many of the tap requirements and tank attachments support modes of user interaction which may or may not result in the immediate movement of asset. Those interactions which do cause movement of asset are performed using the tap_open
operation, and those which do not are performed using the tank_query
operation. Both of these operations will accept arguments specifying what actions should be taken on which requirements/attachments.
Initially, the following tank attachment queries will be supported:
asset_flow_meter
- Reset the meter to zero (No arguments)
attachment_connect_authority
- Reconnect the specified attachment (Accepts a
connection
argument)
- Reconnect the specified attachment (Accepts a
The following tap requirement queries will be supported:
review_requirement
- Create a request to open the tap (Accepts a
request_type
argument and an optional string reason) - Resolve a request to open the tap (Accepts a request ID, a boolean decision, and an optional string reason)
- Cancel a request (Accepts a request ID and an optional string reason)
- Consume an approved request and open the tap (Accepts a request ID)
- Create a request to open the tap (Accepts a
documentation_requirement
- Document the reason for the release of asset (Accepts a string reason)
delay_requirement
- Create a request to open the tap (Accepts a
request_type
argument) - Veto a request to open the tap (Accepts a request ID and an optional string reason)
- Cancel a request (Accepts a request ID and an optional string reason)
- Consume a matured request and open the tap (Accepts a request ID)
- Create a request to open the tap (Accepts a
hash_preimage_requirement
- Reveal the hash preimage and open the tap (Accepts a data buffer)
ticket_requirement
- Redeem a ticket and open the tap (Accepts a
ticket_type
argument)
- Redeem a ticket and open the tap (Accepts a
exchange_requirement
- Reset the amount released (No arguments; meter is required to be at zero when processing)
Please note that while this specification attempts to provide a sufficient level of technical detail to convey the essence of Tanks and Taps, some detail has been elided for brevity, and the final implementation may diverge from the specification in order to improve the correctness, stability, efficiency, maintainability, or functionality of the Tanks and Taps framework.
The proposed modifications to the BitShares protocol will add eight new operation types, one new database object, and several new committee-controlled chain parameters. These modifications add new features, and do not modify or restrict any existing features, and therefore all currently supported use cases should be unaffected. In return, however, the new features will provide a powerful platform for financial dapp and smart contract development, and this platform can be augmented to become even more powerful in the future, requiring only minimal changes to support additional use cases. The proposed changes, in conjunction with future updates, will make BitShares increasingly suitable as a financial dapp platform, notably offering dapp developers simplified development and a reduced time to market as compared with Turing Complete Smart Contract Platform alternatives which offer greater flexibility at the price of increased complexity.
This document is created for the betterment of humanity and is hereby placed into the public domain.