My First Client

An introduction to client development on testnet using the official SDKs.

Getting Started#

In this tutorial, we demonstrate the key elements of a basic client using the official SDKs to interact with the Blockchain. The code for the tutorial is available here: my-first-client. The code in this project can be run from the root of the project directory by issuing the make command..

The example code uses the official Client SDKs. Currently, Go, Java, and Python are available. These libraries are developed to simplify aspects of the development process. If your language is not currently supported, or on the upcoming roadmap (Rust), then you will want to refer to the low-level JSON-RPC API. To request additional functionality or to track when it is implemented, you can submit a GitHub issue on the corresponding project repository.

To see advanced usage, refer to the Reference Wallet project.

Setup#

All code examples are shared in the my-first-client repo on GitHub.

Clone the repo:#

git clone https://github.com/libra/my-first-client.git

Each SDK has the following system requirements:

  • Java: Java 8+
  • Go: Go v1.1+
  • Python: Python v3.7+, pipenv

Run the examples:#

make

or

make python for Python

make java for Java

Connecting to the network#

The first thing your client code will need to do is connect to the network.

client = testnet.create_client();

Creating wallets#

Wallets are addresses on the Blockchain that may issue transactions that send or receive funds. Private and Public key are needed.

# generate private key for sender account
sender_private_key = Ed25519PrivateKey.generate()
# generate auth key for sender account
sender_auth_key = AuthKey.from_public_key(sender_private_key.public_key())
print(f"Generated sender address: {utils.account_address_hex(sender_auth_key.account_address())}")

Requesting Coin1 from the faucet#

A faucet is a blockchain tool for testing blockchain transactions by providing coins for testing. When instructed, the testnet faucet will send the requested amount of Coin1 to the address provided.

Here, we request 100 Coin1 from the faucet and deposit it in Wallet A.

faucet = testnet.Faucet(client)
testnet.Faucet.mint(faucet, sender_auth_key.hex(), 10000000, “Coin1”)

Getting a balance#

In the previous step we requested 100 Coin1 from the faucet. We can verify that our test wallet now has the expected balance.

# connect to testnet
client = testnet.create_client()
# generate private key
private_key = Ed25519PrivateKey.generate()
# generate auth key
auth_key = AuthKey.from_public_key(private_key.public_key())
print(f"Generated address: {utils.account_address_hex(auth_key.account_address())}")
# create account
faucet = testnet.Faucet(client)
testnet.Faucet.mint(faucet, auth_key.hex(), 1340000000, "Coin1")
# get account information
account = client.get_account(auth_key.account_address())
print("Account info:")
print(account)

Sending Coins#

Note: There are several types of peer to peer transactions. These transactions shall have regulatory and compliance requirements that must be followed. To learn more about requirements, please visit the Prospective VASPs document here.

Next we demonstrate sending 10 units of Coin1 from a Sender wallet to a Receiver wallet.

# connect to testnet
client = testnet.create_client()
# generate private key for sender account
sender_private_key = Ed25519PrivateKey.generate()
# generate auth key for sender account
sender_auth_key = AuthKey.from_public_key(sender_private_key.public_key())
print(f"Generated sender address: {utils.account_address_hex(sender_auth_key.account_address())}")
# create sender account
faucet = testnet.Faucet(client)
testnet.Faucet.mint(faucet, sender_auth_key.hex(), 100000000, "Coin1")
# get sender account
sender_account = client.get_account(sender_auth_key.account_address())
# generate private key for receiver account
receiver_private_key = Ed25519PrivateKey.generate()
# generate auth key for receiver account
receiver_auth_key = AuthKey.from_public_key(receiver_private_key.public_key())
print(f"Generated receiver address: {utils.account_address_hex(receiver_auth_key.account_address())}")
# create receiver account
faucet = testnet.Faucet(client)
testnet.Faucet.mint(faucet, receiver_auth_key.hex(), 10000000, CURRENCY)
# create script
script = stdlib.encode_peer_to_peer_with_metadata_script(
currency=utils.currency_code(CURRENCY),
payee=receiver_auth_key.account_address(),
amount=10000000,
metadata=b'', # no requirement for metadata and metadata signature
metadata_signature=b'',
)
# create transaction
raw_transaction = libra_types.RawTransaction(
sender=sender_auth_key.account_address(),
sequence_number=sender_account.sequence_number,
payload=libra_types.TransactionPayload__Script(script),
max_gas_amount=1_000_000,
gas_unit_price=0,
gas_currency_code=CURRENCY,
expiration_timestamp_secs=int(time.time()) + 30,
chain_id=CHAIN_ID,
)
# sign transaction
signature = sender_private_key.sign(utils.raw_transaction_signing_msg(raw_transaction))
public_key_bytes = utils.public_key_bytes(sender_private_key.public_key())
signed_txn = utils.create_signed_transaction(raw_transaction, public_key_bytes, signature)
# submit transaction
client.submit(signed_txn)
# wait for transaction
client.wait_for_transaction(signed_txn)

We can verify that 10 Coin1 was sent by verifying the sender’s wallet balance is 90 and receiver’s balance is 10.

Transaction Intent (DIP-5)#

DIP-5 introduces a standard way for communicating information about a specific transaction. DIP-5 encodes a destination address, currency, and amount. You can use this information to handle the user’s intent.

Each SDK provides the following helper function for processing this information.

//generate private key for new account
PrivateKey privateKey = new Ed25519PrivateKey(new Ed25519PrivateKeyParameters(new SecureRandom()));
//generate auth key for new account
AuthKey authKey = AuthKey.ed24419(privateKey.publicKey());
//create IntentIdentifier
AccountIdentifier accountIdentifier = new AccountIdentifier(TestnetPrefix, authKey.accountAddress());
IntentIdentifier intentIdentifier = new IntentIdentifier(accountIdentifier, CURRENCY, 10000000L);
String intentIdentifierString = intentIdentifier.encode();
System.out.println("Encoded IntentIdentifier: " + intentIdentifierString);
//deserialize IntentIdentifier
IntentIdentifier decodedIntentIdentifier = decode(TestnetPrefix, intentIdentifierString);
System.out.println("Account (HEX) from intent: " + Hex.encode(decodedIntentIdentifier.getAccountIdentifier().getAccountAddress().value));
System.out.println("Amount from intent: " + decodedIntentIdentifier.getAmount());
System.out.println("Currency from intent: " + decodedIntentIdentifier.getCurrency());

Subscribing to events#

To monitor and react to transactions, you may subscribe to events.

In the example below, we will setup a wallet with 100 Coin1 and then call the mint to add 1 Coin1 ten times.

https://github.com/libra/my-first-client/blob/master/python/src/get_events_example.py
import time
from random import randrange
from threading import Thread
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from libra import testnet, AuthKey, utils
CURRENCY = "Coin1"
"""get_events_example demonstrates how to subscribe to a specific events stream base on events key"""
def main():
# connect to testnet
client = testnet.create_client()
# generate private key
private_key = Ed25519PrivateKey.generate()
# generate auth key
auth_key = AuthKey.from_public_key(private_key.public_key())
print(f"Generated address: {utils.account_address_hex(auth_key.account_address())}")
# create new account
faucet = testnet.Faucet(client)
testnet.Faucet.mint(faucet, auth_key.hex(), 100000000, CURRENCY)
# get account events key
account = client.get_account(auth_key.account_address())
events_key = account.received_events_key
# start minter to demonstrates events creation
start_minter(client, auth_key)
# demonstrates events subscription
subscribe(client, events_key)
def subscribe_(client, events_key):
start = 0
for x in range(0, 15):
events = client.get_events(events_key, start, 10)
start += len(events)
print(f"{len(events)} new events found")
time.sleep(3)
for i in range(0, len(events)):
print(f"Event # {i + 1}:")
print(events[i])
def minter(client, auth_key):
for x in range(0, 10):
amount = 1000000
faucet = testnet.Faucet(client)
testnet.Faucet.mint(faucet, auth_key.hex(), amount, CURRENCY)
time.sleep(1)
def subscribe(client, events_key):
Thread(target=subscribe_, args=(client, events_key,)).start()
def start_minter(client, auth_key):
Thread(target=minter, args=(client, auth_key)).start()
if __name__ == "__main__":
main()
tags: core tutorials sdks#
Ask the community for support