Factom Protocol Docs
  • Welcome Developers
  • Protocol Overview
  • Getting Started
    • Installing Factom
    • Developer Sandbox
    • Hello-World Examples
      • Golang
      • JavaScript
      • Python
    • API Reference
      • Factomd
      • Factom-walletd
      • Debug
    • CLI Reference
      • Run Factom Federation
      • Common Tasks
      • Factom CLI Commands
    • Data Structures
      • Building blocks
      • User elements
      • Block types
    • SDKs
      • Open Source SDKs
      • Commercial SDKs
    • Testnet
      • Installation
      • Node monitoring
      • Maintenance
      • Interacting with Factom blockchain
      • Faucet
      • Frequently Asked Questions (FAQ)
      • Useful links
  • Infrastructure
    • Control Panel
    • Explorer
      • Overview
      • Block types
      • Usage
    • Open Node
  • Technologies
    • FAT Tokenization
    • Voting Protocols
  • Protocol development
    • Factom Improvement Proposals
    • Hacking Factom
  • Wallets
    • Ledger Nano S Wallet
    • Enterprise Wallet
      • Install
      • Create FCT Address
      • Send and Receive FCT
      • Backup Wallet
      • Restore From a Backup Seed
      • Importing/Exporting your private key(s)
      • Select Your Factomd Node
      • Upgrade to a Secure Wallet
    • FAT Wallet
    • MyFactomWallet
    • Paper Wallet
  • Authority Node Operators
    • ANO Guides & Tutorials
      • Getting started
      • Generating your server identity
      • Setting the coinbase address and node efficiency
      • Joining the docker swarm
      • Securing your node with SSH and iptables
      • Updating and brainswapping your node
    • ANO Tools and Resources
Powered by GitBook
On this page
  • Requirements
  • Setup
  • Setting up the Factom sandbox
  • Setting up the Python environment
  • Installing the Python package(s)
  • Usage
  • Initiating Factomd and FactomWalletd instances
  • Retrieving wallet balances using factomd
  • Purchasing Entry Credits using the imported Factoid address
  • Creating chains and entries
  • Reading chains and entries created
  • Error handling
  • Full code
  • Conclusion
  • Troubleshooting

Was this helpful?

  1. Getting Started
  2. Hello-World Examples

Python

PreviousJavaScriptNextAPI Reference

Last updated 6 years ago

Was this helpful?

In this tutorial we will be using the Factom to interact with the factomd and factom-walletd APIs in order to secure some data on the Factom blockchain, also known as factomizing. This tutorial is using Python 3.6.7 that comes with a fresh Ubuntu 18.04 install, but versions 3.4, 3.5, 3.7 most likely work as well.

Requirements

  • Python 3+

  • Git

Setup

Setting up the Factom sandbox

For development it is recommended to run a local installation of factomd and factom-walletd, there are a few reasons for this:

  • No need to spend money on Entry Credits

  • Getting slightly familiar with how a Factom node works.

  • Set your own block time, default for mainnet is 600 seconds (10 minutes pr. block)

With that said, if you don't have a Factom sandbox setup, head over to the and follow one of the three setup methods.

Setting up the Python environment

A good idea is to create a Python virtual environment to keep your Python packages isolated to your project. This will avoid any package version conflicts between different Python projects on your computer. First, make a directory containing your virtual environments, or navigate to it if you already have one.

mkdir ~/venvs
cd ~/venvs

Now create the project specific virtual environment:

python3 -m venv hello-world-fct

This will create a new directory in your current folder, named whatever you specified in the command. Here we named the virtual environment "hello-world-fct", you can change this to a different name that suits you. If the command fails to run, follow the instructions given in the command line and try again. Now activate the environment:

source hello-world-fct/bin/activate

Your environment is now active and we can move on to installing the Python package(s)!

Installing the Python package(s)

pip install git+https://github.com/TRGG3R/factom-api.git

That's it! Lets start writing some code!

Usage

Everything is setup and ready now. All you need is your favorite IDE and make sure you select / activate the Python interpreter you setup earlier.

Initiating Factomd and FactomWalletd instances

hello-world-fct-.py
from factom import Factomd, FactomWalletd

FCT_ADDR = 'YOUR_FCT_ADDRESS'
EC_ADDR = 'YOUR_GENERATED_EC_ADDRESS'

# Initiate walletd and factomd instances
walletd = FactomWalletd()
factomd = Factomd(
                host='http://localhost:8088',
                fct_address=FCT_ADDR,
                ec_address=EC_ADDR,
                username='hello',
                password='world')

Retrieving wallet balances using factomd

Let’s check the wallet balances on both the FCT and EC addresses we imported and generated earlier. The FCT balance will be shown in Factoshis( Factoids * 10^-8) :

hello-world-fct.py
# Retrieve FCT and EC balance
fct_balance = factomd.factoid_balance(FCT_ADDR)
ec_balance = factomd.entry_credit_balance(EC_ADDR)

print(fct_balance)
print(ec_balance)

If you provided your wallet info when initiating the factomd instance you can run the above lines without providing the addresses:

hello-world-fct.py
# Retrieve FCT and EC balance
fct_balance = factomd.factoid_balance()
ec_balance = factomd.entry_credit_balance()

print(fct_balance)
print(ec_balance)

You should see the following results:

{'balance': 2000000000000}  # 2000000000000 Factoshis
{'balance': 0}

At the moment we have 20000 Factoids and 0 Entry Credits. We need to fund our Entry Credit address by purchasing some Entry Credits, as those are required in order to record data on-chain.

Purchasing Entry Credits using the imported Factoid address

The wallet daemon provides a method fct_to_ec() to burn Factoids and mint Entry Credits. We will use that method, but first we need to know what the Factoid to Entry Credit exchange rate is:

hello-world-fct.py
# Check the EC rate
ec_rate = factomd.entry_credit_rate()
print(ec_rate)

Result:

{'rate': 1000}

This tells us we'll need to burn 1000 Factoshis (0.00001 FCT) in exchange for 1 Entry Credit, so let's purchase 10 entry credits for 10000 Factoshis (0.0001 FCT).

hello-world-fct.py
# Purchase some EC with Factoids
r = walletd.fct_to_ec(factomd=factomd,
                      amount=10000, 
                      fct_address=fct_address, 
                      ec_address=ec_address)
print(r)

Result:

{'message': 'Successfully submitted the transaction', 'txid': 'c60e4fee59876ce5ee242d9da15d4764a2880f36efac8c18bc7306986b041fb5'}

The transaction was successfully submitted with txid: c60e4fee59876ce5ee242d9da15d4764a2880f36efac8c18bc7306986b041fb5. Let's check the balance again on our wallets as shown earlier. The result should be:

{'balance': 1999999978000}
{'balance': 10}

Note that the FCT balance has been reduced by 22K instead of 10K Factoshis due to the network transaction fees.

We've successfully funded our Entry Credit Address with 10 Entry Credits. Let's move on to creating chains and entries.

Creating chains and entries

The wallet client has a new_chain() method that handles everything needed to create a new chain. To make a new chain you need to provide:

  • Your factomd instance, where the creation message will be submitted

  • A list of external IDs

  • Entry content

  • Your Entry Credit address

hello-world-fct.py
# Create new chain!
new_chain = walletd.new_chain(factomd=factomd,
                              ext_ids=["Hello", "world", "FCT!"],
                              content="Hello world Factom in Python!",
                              ec_address=EC_ADDR)

print(new_chain)

Result:

{'message': 'Entry Reveal Success', 'entryhash': '698f6262dd2f9a84b46675fe89ce55c456f2245c1d5a9dcfbc982d764fb294e7', 'chainid': 'e1ebf8bd4a4b20816c83b674895064bbe77bdc467967480daa7f851b1595d4dc'}

If nothing shows up you might need to wait a few more seconds / minutes, depending on what you set your block time to.

Our chain is successfully created and is visible in the explorer, now lets add some entries to that chain.

hello-world-fct.py
# Chainid retrieved from the previous result
chain_id = 'e1ebf8bd4a4b20816c83b674895064bbe77bdc467967480daa7f851b1595d4dc'

# Add a new entry to existing chain
new_entry = walletd.new_entry(factomd=factomd,
                              chain_id=chain_id,
                              ext_ids=['Some', 'random', 'ext ids'],
                              content='Second entry in chain!',
                              ec_address=EC_ADDR)
print(new_entry)

Result:

{'message': 'Entry Reveal Success', 'entryhash': 'eee39f41044db49aa63e61789a4effd34a1aad6b928e138c66b4afc720dfa987', 'chainid': 'e1ebf8bd4a4b20816c83b674895064bbe77bdc467967480daa7f851b1595d4dc'}

Success! Let's check the explorer again to see if some new entries show up on the same chain:

As we see in the image above, both the initial entry and new entry exist on the chain! You can also read chains and entries created directly in Python!

Reading chains and entries created

Sometimes the entries in your chain may reference each other, thus you may want to scan the entire chain in order to very its integrity. The factomd client provides a read_chain() method which iterates over all entry-containing blocks and returns a list of entries in reverse order.

hello-world-fct.py
chain_id = 'e1ebf8bd4a4b20816c83b674895064bbe77bdc467967480daa7f851b1595d4dc'

# Read the chain
chain_data = factomd.read_chain(chain_id=chain_id)

print(chain_data )

Result:

[{'chainid': 'e1ebf8bd4a4b20816c83b674895064bbe77bdc467967480daa7f851b1595d4dc', 'extids': ['Some', 'random', 'ext ids'], 'content': 'Second entry in chain!'}, {'chainid': 'e1ebf8bd4a4b20816c83b674895064bbe77bdc467967480daa7f851b1595d4dc', 'extids': ['Hello', 'world', 'FCT!'], 'content': 'Hello world Factom in Python!'}]

Success! The whole chain is returned with all of its entries. The latest entry is returned first.

Error handling

The Python library returns a number of different error codes in case of a problem. The API method you tried to call will raise a factom.exceptions.FactomAPIError with details about the error.

For example, if we try to create a new chain with the exact same parameters as a previously created chain you’ll see something similar to this:

Traceback (most recent call last):
  File "/hello-world-fct/main.py", line 65, in <module>
    ec_address=EC_ADDR)
  File "/dev/hello-world-fct/lib/python3.6/site-packages/factom/client.py", line 256, in new_chain
    'ecpub': ec_address or self.ec_address
  File "/dev/hello-world-fct/lib/python3.6/site-packages/factom/client.py", line 73, in _request
    handle_error_response(resp)
  File "/dev/hello-world-fct/lib/python3.6/site-packages/factom/exceptions.py", line 19, in handle_error_response
    raise codes[code](message=message, code=code, data=data, response=resp)
factom.exceptions.InvalidParams: -32602: Invalid params

Details about the error are attached to the exception instance, thus a good idea is to wrap each API call into try blocks such as:

hello-world-fct.py
from factom.exceptions import FactomAPIError

try:
    new_chain = walletd.new_chain(factomd=factomd,
                                  ext_ids=["Hello", "world", "FCT!"],
                                  content="Hello world Factom in Python!",
                                  ec_address=EC_ADDR)
except FactomAPIError as e:
    print(e.data)

As this chain already exists, it'll return the following message:

Chain e1ebf8bd4a4b20816c83b674895064bbe77bdc467967480daa7f851b1595d4dc already exists

Full code

Below is the full code featuring everything mentioned above, with some extra additions.

hello-world-fct.py
from time import sleep

from factom import Factomd, FactomWalletd
from factom.exceptions import FactomAPIError

def wait_for_entry(entryhash):
    """
    Wait for an entry to be recorded on-chain.
    
    This can be detected by making sure that the entry hash is not present
    in the list of pending entries.
    
    :param str entryhash: the hash of the entry to wait for
    :returns: None
    """
    while True:
        r_pending_entries = factomd.pending_entries()
        if any(entry["entryhash"] == entryhash for entry in r_pending_entries):
            sleep(1)
        else:
            break

# Wallets, remember to import the FCT address and generate the EC address.
FCT_ADDR = 'FA2jK2HcLnRdS94dEcU27rF3meoJfpUcZPSinpb7AwQvPRY6RL1Q'
EC_ADDR = 'EC2qpLisdttfe6JHPmQ7DiZqYet2Ga5rzeWAbx2QWSMTMB5G5K4X'

if __name__ == "__main__":
    # Default params
    walletd = FactomWalletd()
    
    # You can also specify fct and ec addresses, change host, or specify RPC credentials, for example:
    factomd = Factomd(host='http://localhost:8088',
                      fct_address=FCT_ADDR,
                      ec_address=EC_ADDR,
                      username='rpc_username',
                      password='rpc_password')
                        
    # Check balance on wallets
    fct_balance = factomd.factoid_balance(FCT_ADDR)
    ec_balance = factomd.entry_credit_balance(EC_ADDR)
    print(f'\n Factoshi (FCT*10^(⁻8)) balance: {fct_balance['balance']}')
    print(f'Entry Credit (EC) balance: {ec_balance['balance']}\n')
    
    # Check entry credit rate
    ec_rate = factomd.entry_credit_rate()
    
    # Print result
    print(f'Entry credit rate is: {ec_rate["rate"]} Factoshis')
    
    # Purchase some entry credits with our imported FCT wallet if our EC balance is less than 20.
    if ec_balance['balance'] < 20:
        print('Balance is less than 20 Entry Credits, purchasing 50 more.')
        fct_to_ec = walletd.fct_to_ec(factomd, 50000, fct_address=FCT_ADDR, ec_address=EC_ADDR)
        
        # Print result
        print(fct_to_ec)
    
    # Wait a little
    print(f'Sleeping for 5 seconds..\n')
    sleep(5)
    
    # Check updated balances
    fct_balance = factomd.factoid_balance(FCT_ADDR)
    ec_balance = factomd.entry_credit_balance(EC_ADDR)
    print(f'Factoshi (FCT*10^(⁻8)) balance: {fct_balance['balance']}')
    print(f'Entry Credit (EC) balance: {ec_balance['balance']}\n')
    
    # Initiate some variables
    new_chain = None
    chain_id = None
    entry_hash = None
    
    try:
        # Create new chain!
        # The cost of creating a new chain is 10 ECs.
        new_chain = walletd.new_chain(factomd=factomd,
                                      ext_ids=['Hello', 'world', 'FCT!'],
                                      content='Hello world Factom in Python!',
                                      ec_address=EC_ADDR)
                                        
        # Store chain ID and entry hash
        chain_id = new_chain['chainid']
        entry_hash = new_chain['entryhash']
        
        # Print result
        print(f'New chain created with chain ID: {chain_id} and entry hash: {entry_hash}')
        
        print('Waiting for the newly created chain to appear on the blockchain')
        # Wait for the new chain to appear on the blockchain
        wait_for_entry(entryhash=entry_hash)
        
    except FactomAPIError as e:
        print(f'{e.data}.\n')
        # If chain already exists retrieve the chainid.
        chain_id = e.data.split()[1]
    
    new_entry = None
    try:    
        # Create a new entry in the chain created above using the chainid
        print(f'Creating a new entry to the chain with chain ID: {chain_id}.')
        new_entry = walletd.new_entry(factomd=factomd,
                                      chain_id=chain_id,
                                      ext_ids=['Some', 'random', 'ext id'],
                                      content='Second entry in chain!',
                                      ec_address=EC_ADDR)
        # Print result
        print(f'{new_entry}\n')
    except FactomAPIError as e:
        print(e.data)
        
    # Read the whole chain including the entries
    chain_data = None
    try:
        print(f'Reading the whole chain:')
        chain_data = factomd.read_chain(chain_id=chain_id)
        
        # Print result
        print(chain_data)
        
    except FactomAPIError as e:    
        print(e.data)

Conclusion

You now have all the tools available and ready to start fully interacting with the Factom blockchain in Python!

Troubleshooting

This section will be filled out once users start reporting issues, problems or relevant questions they might have.

We will be using the . Install it via pip:

Success! Lets head over to the to see if our entry exists. Search for the chainid, found in the result above, in the search box as seen below:

For a full list of the error codes in the library see .

Python library
Developer Sandbox Setup Guide
Factom API Python client library
local Factom explorer
here