[pybitshares/uptick] How to add new on-chain features
This post serves as a quick guide into the underlying functionality of pybitshares and uptick.
In this tutorial, we add a new feature that allows you to create a new committee member that can then be voted by BTS hodlers.
1. Implement operation
This step sets up a new class
that tells the library how that particular operation is called and what it does.
The definition of those are burried deeply into bitshares-core. In this case, we take a closer look into porotocol/committee-member.hpp, identify the struct's members and add them in the same order to pybitshares:
struct committee_member_create_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
/// The account which owns the committee_member. This account pays the fee for this operation.
account_id_type committee_member_account;
string url;
account_id_type fee_payer()const { return committee_member_account; }
void validate()const;
};
For python-bitshares:
class Committee_member_create(GrapheneObject):
def __init__(self, *args, **kwargs):
if isArgsThisClass(self, args):
self.data = args[0].data
else:
if len(args) == 1 and len(kwargs) == 0:
kwargs = args[0]
super().__init__(OrderedDict([
('fee', Asset(kwargs["fee"])),
('committee_member_account', ObjectId(kwargs["committee_member_account"], "account")),
('url', String(kwargs["url"])),
]))
What is important here is that the class name is identical to the operations listed here, except that the first letter is capital.
2. Test operation
Now we go further and test the new operation against the cli_wallet. For that, we start a cli wallet and expose the cli_wallet's API locally at port 8092
. We then install the new operation into the compareConstructedTX()
call like this.
When calling python3 tests/test_transactions.py
, we now get
{'expiration': '2016-04-06T08:29:27',
'extensions': [],
'operations': [[29,
{'committee_member_account': '1.2.0',
'fee': {'amount': 0, 'asset_id': '1.3.0'},
'url': 'foobar'}]],
'ref_block_num': 34294,
'ref_block_prefix': 3707022213,
'signatures': ['2035e1564b63c8fe55c0ba176044468ffbddffe0241792625de8365e55522f2488261ac0dd2e22aca4aeb6da48b0773837e89c4f36cfd86df1eb78119fe8a2d21e']}
================================================================================
soll: f68585abf4dce7c80457011d0000000000000000000006666f6f6261720001
ist: f68585abf4dce7c80457011d0000000000000000000006666f6f6261720001
True
Which compares the serialization of cli-wallet with pybitshares.
In this case, it all looks fine and we add a unit test like this
Since we initially don't know the output to compare with, we can use self.doIt(True)
to let the unittest provide the string we want to compare it with later on.
We execute the unit test with
python3 -m unittest tests.test_transactions.Testcases.test_committee_create
The outputed string is then stored in self.cm
so that the unittest succeeeds afterwards.
3. Expose the functionality in bitshares.py
We now write an API into bitshares.py to expose the feature for easier usage:
Here, all we do is make sure we use some account (default if not present), make sure the account exists, construct the operation and forward it for signing through finalizeOp
. This signs it with the active key of account
if the key is present in the wallet.
4. Integrate into uptick
uptick
is a command line tool that makes heavy use of pybitshares and python-click.
using the new feature is easy as all we need to do is add a new method like here
@main.command()
@click.pass_context
@onlineChain
@click.argument('url', type=str)
@click.option(
"--account",
help="Account that takes this action",
default=config["default_account"],
type=str)
@unlockWallet
def createcommittee(ctx, url, account):
""" Setup a committee account for your account
"""
pprint(ctx.bitshares.create_committee_member(
url,
account=account
))
I hope this makes it clear how easy it is to use python bitshares and uptick.
If you decide to take on some of the missing operations, please feel free to send pull requests my way.
Is there a specific reason why pybitshares break the python convention on class names? This should be CommitteeMemberCreate for example. :)
Yes, the reason is that I don't want the developer to manually provide the OperationID for that operation and instead obtain it automatically from the list of operation ids by using the operation name:
https://github.com/xeroc/python-graphenelib/blob/master/graphenebase/objects.py#L19-L38
Thanks for sharing this, it's quite helpful
Hey @xeroc , this is great information to us , thanks for the enlighten.
thanks for the job we are waiting for your next welfare .
Why are you powering up 100%??
Good job. Keep it up
brilliant & exceptional!
Congratulations @xeroc! You have received a personal award!
2 Years on Steemit
Click on the badge to view your own Board of Honor on SteemitBoard.
Cool stuff dude! I'm not really good with coding, I tried two different times, but hey its still really spectacular!
Great info . It is always nice to get fresh intel regarding pybitshares. Thanks
Congratulation
Today 2 years ago you joined SteemItThank you, for making SteemIt great and Steem on for more years to come!
(You are being celebrated here)