Hello World

Note

All GridWorks repos assume Python 3.10 or higher, docker, and the Algorand sandbox. Also, For these `hello world` examples, please clone this repo in order to spin up a local rabbit broker.

GridWorks is message-driven, following the reactive manifesto. The basic building blocks are GNodes (‘G’ stands for the electric grid). GNode actors communicate via:

  1. Asynchronous message-passing on a RabbitMQ Broker;

  2. API calls via FastAPI interfaces; and

  3. Calls to the Algorand blockchain ledger, typically usually smart contract ABI method calls.

Hello Rabbit

Here is a simple example of the first. Before running, start a development world rabbit broker. From the top level of this repo:

  1. ./arm.sh (if your computer has an arm chip)

  2. ./x86.sh (x86 chip)

Wait for the rabbit admin page to load (username/passwd smqPublic)

from gridworks.actor_base import ActorBase
from gridworks.enums import GNodeRole
from gridworks.enums import MessageCategory
from gridworks.gw_config import GNodeSettings
from gridworks.types import HeartbeatA_Maker


class HelloGNode(ActorBase):
    def __init__(self, settings: GNodeSettings):
        super().__init__(settings=settings)
        self.settings: GNodeSettings = settings

    def prepare_for_death(self) -> None:
        self.actor_main_stopped = True


def demo():
    settings = GNodeSettings()

    settings.g_node_alias = "d1.hello"
    settings.g_node_role_value = "GNode"

    gn = HelloGNode(settings=settings)
    gn.start()

    input(
        f"Go to http://0.0.0.0:15672/#/queues and wait for the d1.hello-Fxxxx queue to appear."
    )
    assert gn.g_node_role == GNodeRole.GNode
    hb = HeartbeatA_Maker(my_hex=0, your_last_hex="a").tuple

    print("Broadcasting a heartbeat on rabbitmq")
    gn.send_message(payload=hb, message_category=MessageCategory.RabbitJsonBroadcast)

    print("Inpsect the dummy ear queue to examine the message (click on GetMessage)")
    input("http://0.0.0.0:15672/#/queues/d1__1/dummy_ear_q")
    input(f"Hit return to tear down the GNode rabbit actor")
    gn.stop()


if __name__ == "__main__":
    demo()

Hello Algorand

Before you start, clone the Algorand sandbox and start it up in dev mode by running ./sandbox up dev at its top level directory (you will need docker installed).

In order for an Algorand Account or Smart Contract to do any actions on-chain, it must be funded. The dev sandbox is running a local dev Algorand blockchain on your computer, which comes with a couple pre-funded genesis accounts. The gridworks package has a method called dev_fund_to_min which can be called in the dev environment to fund an account from one of the sandbox genesis accounts.

Create and fund a dev account
import gridworks.algo_utils as algo_utils
from gridworks.algo_utils import BasicAccount
import gridworks.dev_utils.algo_setup as algo_setup

acct = BasicAccount()
assert algo_utils.algos(acct.addr) == 0
algo_setup.dev_fund_to_min(addr=acct.addr, min_algos=3)
assert algo_utils.algos(acct.addr) == 3
algo_setup.dev_fund_to_min(addr=acct.addr, min_algos=2)
assert algo_utils.algos(acct.addr) == 3

The first Algorand transaction that occurs in any full simulation is the creation of a TaValidator Certificate. (For quick context, a TaValidator is an entity involved in establishing the link between GridWorks avatars for real-world devices attached to the electric grid.)

This certificate is an example of an Algorand Non-fungible Token (NFT). In particular, the certificate is an Algorand Standard Asset whose total is 1 (this is how uniqueness in enforced). You may have heard about NFTs in the context of art. We are using NFTs in a similar way - s an identifiable object that can only be owned by a single entity.

All ASAs have creators - identified by the Algorand address that pays the fee for the creation transaction (aka the sender). One of the criterion for an ASA being a TaValidator Certificate is that the creator’s address must be a 2-signature MultiSig address (examine all of the criteria here, and also note that this is enforced in the validation of a tavalidator.algo.create type, in Axiom 3).

Gridworks has a MultiAccount object used for this purpose:

Creating a 2-sig MultiAccount[GnfAdminAddr, ValidatorAddr]
from gridworks.gw_config import Public
from gridworks.algo_utils import BasicAccount
from gridworks.algo_utils import MultisigAccount

validator_acct = BasicAccount()
gnf_admin_addr = Public().gnf_admin_addr
multi = MultisigAccount(
   version=1,
   threshold=2,
   addresses = [gnf_admin_addr, validator_acct.addr]
)

print(multi.addr)

GridWorks always uses its MultiAccount instead of the algosdk.futures.transaction.Multisig object. The algosdk Multisig object is not designed for multiple transactions, as it stores transaction signatures. Gotcha note. Sometimes Gridworks methods duck-type BasicAccount and MultiAccount. This only works if the method only accesses their public address (e.g. acct.addr). MultiAccounts do not have a secret key, since it does not store the private information of their signatories.

To continue with more tutorial-type instructions, please go to the Millinocket tutorial.