Skip to content

Blockchains share a single state between each node participating in the network, called the distributed ledger. Nine Chronicles uses Libplanet to manage the blockchain state, which in turn implements many of the gameโ€™s features, and Lib9c is the implementation.

๐Ÿ’ก

This document is based on Libplanet 5.2.2, Lib9c 1.17.3, and Bencodex 0.16.0 versions.

Address โ€‹

In Libplanet, an address represents a specific location on the blockchain, and is primarily used to identify accounts and their state. Libplanet has a class Address that acts as an address and is usually derived from a public key[1] or generated as a hexadecimal string of 40 or 42 digits.

cs
PrivateKey privateKey = new PrivateKey();
PublicKey publicKey = privateKey.PublicKey;
Address addressFromPublickKey = publicKey.Address;
// or
Address addressFromPrivateKey = privateKey.Address;

Address addressViaHexString1 = new Address("1234567890abcdef1234567890abcdef12345678");
// or
Address addressViaHexString2 = new Address("0x1234567890abcdef1234567890abcdef12345678");

World(IWorld) โ€‹

In Libplanet, blockchain state can be managed through the IWorld interface, which provides methods to retrieve and update accounts (IAccount, CurrencyAccount). In this context, an account is a bundle of states associated with a particular address in the world.

cs
public IWorld Foo(IWorld world)
{
    Address address = new PrivateKey().Address;
    IAccount account = world.GetAccount(address);
    world = world.SetAccount(address, account);

    Currency currency = Currency.Uncapped("$", 0, null);
    CurrencyAccount currencyAccount = world.GetCurrencyAccount(currency);
    world = world.SetCurrencyAccount(currency, currencyAccount);

    return world;
}

Account(IAccount) โ€‹

In Libplanet, you can manage status through the IAccount interface.

cs
public IAccount Foo(IAccount account)
{
    Address address = new PrivateKey().Address;
    IValue? value = account.GetState(address);
    if (value is null)
    {
        account = account.SetSate(address, (Bencodex.Types.Integer)1);
    }
    else
    {
        account = account.RemoveState(address);
    }

    return account;
}

CurrencyAccount โ€‹

CurrencyAccount represents an account associated with a specific currency, and is primarily used to manage balance status.

cs
public CurrencyAccount Foo(
    CurrencyAccount currencyAccount,
    Address sender,
    Address recipient)
{
    Currency currency = Currency.Uncapped("$", 0, null);
    FungibleAssetValue balance = currencyAccount.GetBalance(sender, currency);
    // balance: 0.0 $

    FungibleAssetValue amount = new FungibleAssetValue(currency, 1, 0);
    currencyAccount = currencyAccount.MintAsset(sender, amount);
    balance = currencyAccount.GetBalance(sender, currency);
    // balance: 1.0 $

    currencyAccount = currencyAccount.TransferAsset(sender, recipient, amount);
    balance = currencyAccount.GetBalance(sender, currency);
    // balance: 0.0 $
    balance = currencyAccount.GetBalance(recipient, currency);
    // balance: 1.0 $

    currencyAccount = currencyAccount.BurnAsset(recipient, amount);
    balance = currencyAccount.GetBalance(recipient, currency);
    // balance: 0.0 $

    balance = currencyAccount.GetBalance(sender, currency);
}

IValue โ€‹

Libplanet stores all state serialized in the Bencodex.Types.IValue interface type. There are many different implementations, including Bencodex.Types.Binary, Bencodex.Types.Integer, Bencodex.Types.Text, Bencodex.Types.List, and Bencodex.Types.Dictionary.

cs
public IAccount Add(IAccount account, Address address, int value)
{
    IValue? state = account.GetState(address);
    return state switch
    {
        null => account.SetState(address, (Bencodex.Types.Integer)value),
        Bencodex.Types.Null => account.SetState(address, (Bencodex.Types.Integer)value),
        Bencodex.Types.Integer i => account.SetState(address, i + value),
        _ => throw new UnexpectedTypeException();
    }
}

  1. The public key derived from the private key. Libplanet.Crypto.PublicKey โ†ฉ๏ธŽ