flow

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2020 License: Apache-2.0 Imports: 10 Imported by: 310

README

Flow Go SDK GoDoc

The Flow Go SDK provides a set of packages for Go developers to build applications that interact with the Flow network.

Note: This SDK is also fully compatible with the Flow Emulator and can be used for local development.

What is Flow?

Flow is a new blockchain for open worlds. Read more about it here.

Table of Contents

Getting Started

Installing

To start using the SDK, install Go 1.13 or above and run go get:

go get github.com/onflow/flow-go-sdk
Generating Keys

The signature scheme supported in Flow accounts is ECDSA. It can be coupled with the hashing algorithms SHA2-256 or SHA3-256.

Here's how you can generate an ECDSA-P256 private key:

import "github.com/onflow/flow-go-sdk/crypto"

// deterministic seed phrase (this is only an example, please use a secure random generator for the key seed)
seed := []byte("elephant ears space cowboy octopus rodeo potato cannon pineapple")

privateKey, err := crypto.GeneratePrivateKey(crypto.ECDSA_P256, seed)

The private key can then be encoded as bytes (i.e. for storage):

encPrivateKey := privateKey.Encode()

A private key has an accompanying public key:

publicKey := privateKey.PublicKey()

The example above uses an ECDSA key-pair of the elliptic curve P-256. Flow also supports the curve secp256k1. Here's how you can generate an ECDSA-SECp256k1 private key:

privateKey, err := crypto.GeneratePrivateKey(crypto.ECDSA_secp256k1, seed)
Creating an Account

Once you have generated a key-pair, you can create a new account using its public key.

import (
    "github.com/onflow/flow-go-sdk"
    "github.com/onflow/flow-go-sdk/crypto"
    "github.com/onflow/flow-go-sdk/templates"
)

// generate a new private key for the account (this is only an example, please use a secure random generator for the key seed)
ctx := context.Background()
// generate a new private key for the account (this is only an example, please use a secure random generator for the key seed)
seed := []byte("elephant ears space cowboy octopus rodeo potato cannon pineapple")
privateKey, _ := crypto.GeneratePrivateKey(crypto.ECDSA_P256, seed)

// get the public key from the private key
publicKey := privateKey.PublicKey()

// construct an account key from the public key
accountKey := flow.NewAccountKey().
    SetPublicKey(publicKey).
    SetHashAlgo(crypto.SHA3_256).             // pair this key with the SHA3_256 hashing algorithm
    SetWeight(flow.AccountKeyWeightThreshold) // give this key full signing weight

// generate an account creation script
// this creates an account with a single public key and no code
script, _ := templates.CreateAccount([]*flow.AccountKey{accountKey}, nil)

// connect to an emulator running locally
c, err := client.New("localhost:3569")
if err != nil {
    panic("failed to connect to emulator")
}

payer, payerKey, payerSigner := examples.RootAccount(c)

tx := flow.NewTransaction().
    SetScript(script).
    SetGasLimit(100).
    SetProposalKey(payer, payerKey.ID, payerKey.SequenceNumber).
    SetPayer(payer)

err = tx.SignEnvelope(payer, payerKey.ID, payerSigner)
if err != nil {
    panic("failed to sign transaction")
}

err = c.SendTransaction(ctx, *tx)
if err != nil {
    panic("failed to send transaction")
}

result, err := c.GetTransactionResult(ctx, tx.ID())
if err != nil {
    panic("failed to get transaction result")
}

var myAddress flow.Address

if result.Status == flow.TransactionStatusSealed {
    for _, event := range result.Events {
        if event.Type == flow.EventAccountCreated {
            accountCreatedEvent := flow.AccountCreatedEvent(event)
            myAddress = accountCreatedEvent.Address()
            }
	}
}
Signing a Transaction

Below is a simple example of how to sign a transaction using a crypto.PrivateKey:

import (
    "github.com/onflow/flow-go-sdk"
    "github.com/onflow/flow-go-sdk/crypto"
)

var (
    myAddress    flow.Address
    myAccountKey flow.AccountKey
    myPrivateKey crypto.PrivateKey
)

tx := flow.NewTransaction().
    SetScript(script).
    SetGasLimit(100).
    SetProposalKey(myAddress, myAccountKey.ID, myAccountKey.SequenceNumber).
    SetPayer(myAddress)

Transaction signing is done using the crypto.Signer interface. The simplest (and least secure) implementation of crypto.Signer is crypto.InMemorySigner.

Signatures can be generated more securely using hardware keys stored in a device such as an HSM. The crypto.Signer interface is intended to be flexible enough to support a variety of signer implementations and is not limited to in-memory implementations.

// construct a signer from your private key and configured hash algorithm
mySigner := crypto.NewInMemorySigner(myPrivateKey, myAccountKey.HashAlgo)

err := tx.SignEnvelope(myAddress, myAccountKey.ID, mySigner)
if err != nil {
    panic("failed to sign transaction")
}
Sending a Transaction

You can submit a transaction to the network using the Access API Client.

import "github.com/onflow/flow-go-sdk/client"

// connect to an emulator running locally
c, err := client.New("localhost:3569")
if err != nil {
    panic("failed to connect to emulator")
}

ctx := context.Background()

err = c.SendTransaction(ctx, tx)
if err != nil {
    panic("failed to send transaction")
}
Querying Transaction Results

After you have submitted a transaction, you can query its status by ID:

result, err := c.GetTransactionResult(ctx, tx.ID())
if err != nil {
    panic("failed to fetch transaction result")
}

The result includes a Status field that will be one of the following values:

  • UNKNOWN - The transaction has not yet been seen by the network.
  • PENDING - The transaction has not yet been included in a block.
  • FINALIZED - The transaction has been included in a block.
  • EXECUTED - The transaction has been executed but the result has not yet been sealed.
  • SEALED - The transaction has been executed and the result is sealed in a block.
if result.Status == flow.TransactionStatusSealed {
  fmt.Println("Transaction is sealed!")
}

The result also contains an Error that holds the error information for a failed transaction.

if result.Error != nil {
    fmt.Printf("Transaction failed with error: %v\n", result.Error)
}
Querying Blocks

You can use the GetLatestBlock method to fetch the latest sealed or unsealed block:

// fetch the latest sealed block
isSealed := true
latestBlock, err := c.GetLatestBlock(ctx, isSealed)
if err != nil {
    panic("failed to fetch latest sealed block")
}

// fetch the latest unsealed block
isSealed := false
latestBlock, err := c.GetLatestBlock(ctx, isSealed)
if err != nil {
    panic("failed to fetch latest unsealed block")
}

A block contains the following fields:

  • ID - The ID (hash) of the block.
  • ParentBlockID - The ID of the previous block in the chain.
  • Height - The height of the block in the chain.
  • CollectionGuarantees - The list of collections included in the block.
Executing a Script

You can use the ExecuteScriptAtLatestBlock method to execute a read-only script against the latest sealed execution state.

This functionality can be used to read state from the blockchain.

Scripts must be in the following form:

  • A single main function with a single return value

This is an example of a valid script:

fun main(): Int { return 1 }
import "github.com/onflow/cadence"

script := []byte("fun main(): Int { return 1 }")

value, err := c.ExecuteScript(ctx, script)
if err != nil {
    panic("failed to execute script")
}

ID := value.(cadence.Int)

// convert to Go int type
myID := ID.Int()
Querying Events

You can query events with the GetEventsForHeightRange function:

import "github.com/onflow/flow-go-sdk/client"

blocks, err := c.GetEventsForHeightRange(ctx, client.EventRangeQuery{
    Type:       "flow.AccountCreated",
    StartHeight: 10,
    EndHeight:   15,
})
if err != nil {
    panic("failed to query events")
}
Event Query Format

An event query includes the following fields:

Type

The event type to filter by. Event types are namespaced by the account and contract in which they are declared.

For example, a Transfer event that was defined in the Token contract deployed at account 0x55555555555555555555 will have a type of A.0x55555555555555555555.Token.Transfer.

Read the language documentation for more information on how to define and emit events in Cadence.

StartHeight, EndHeight

The blocks to filter by. Events will be returned from blocks in the range StartHeight to EndHeight, inclusive.

Event Results

The GetEventsForHeightRange function returns events grouped by block. Each block contains a list of events matching the query in order of execution.

for _, block := range blocks {
    fmt.Printf("Events for block %s:\n", block.BlockID)
    for _, event := range block.Events {
        fmt.Printf(" - %s", event)
    }
}
Querying Accounts

You can query the state of an account with the GetAccount function:

import "github.com/onflow/flow-go-sdk"

address := flow.HexToAddress("01")

account, err := c.GetAccount(ctx, address)
if err != nil {
    panic("failed to fetch account")
}

A flow.Account contains the following fields:

  • Address: flow.Address - The account address.
  • Balance: uint64 - The account balance.
  • Code: []byte - The code deployed at this account.
  • Keys: []flow.AccountKey - A list of the public keys associated with this account.

Examples

The examples directory contains code samples that use the SDK to interact with the Flow Emulator.

Documentation

Overview

Package flow provides libraries and tools for building Go applications on Flow.

Index

Examples

Constants

View Source
const (
	EventAccountCreated string = "flow.AccountCreated"
	EventAccountUpdated string = "flow.AccountUpdated"
)

List of built-in account event types.

View Source
const AccountKeyWeightThreshold int = 1000

AccountKeyWeightThreshold is the total key weight required to authorize access to an account.

View Source
const (
	// AddressLength is the size of an account address.
	AddressLength = 20
)

Variables

View Source
var (
	// ZeroAddress represents the "zero address" (account that no one owns).
	ZeroAddress = Address{}
	// RootAddress is the address of the Flow root account.
	RootAddress = BytesToAddress(big.NewInt(1).Bytes())
)
View Source
var DefaultHasher crypto.Hasher

DefaultHasher is the default hasher used by Flow.

View Source
var ZeroID = Identifier{}

ZeroID is the empty identifier.

Functions

This section is empty.

Types

type Account

type Account struct {
	Address Address
	Balance uint64
	Code    []byte
	Keys    []*AccountKey
}

An Account is an account on the Flow network.

type AccountCreatedEvent

type AccountCreatedEvent Event

An AccountCreatedEvent is emitted when a transaction creates a new Flow account.

This event contains the following fields: - Address: Address

func (AccountCreatedEvent) Address

func (evt AccountCreatedEvent) Address() Address

Address returns the address of the newly-created account.

type AccountKey

type AccountKey struct {
	ID             int
	PublicKey      crypto.PublicKey
	SigAlgo        crypto.SignatureAlgorithm
	HashAlgo       crypto.HashAlgorithm
	Weight         int
	SequenceNumber uint64
}

An AccountKey is a public key associated with an account.

func DecodeAccountKey

func DecodeAccountKey(b []byte) (*AccountKey, error)

DecodeAccountKey decodes the RLP byte representation of an account key.

func NewAccountKey

func NewAccountKey() *AccountKey

NewAccountKey returns an empty account key.

func (AccountKey) Encode

func (a AccountKey) Encode() []byte

Encode returns the canonical RLP byte representation of this account key.

func (*AccountKey) FromPrivateKey

func (a *AccountKey) FromPrivateKey(privKey crypto.PrivateKey) *AccountKey

FromPrivateKey sets the public key and signature algorithm based on the provided private key.

func (*AccountKey) SetHashAlgo

func (a *AccountKey) SetHashAlgo(hashAlgo crypto.HashAlgorithm) *AccountKey

SetHashAlgo sets the hash algorithm for this account key.

func (*AccountKey) SetPublicKey

func (a *AccountKey) SetPublicKey(pubKey crypto.PublicKey) *AccountKey

SetPublicKey sets the public key for this account key.

func (*AccountKey) SetSigAlgo

func (a *AccountKey) SetSigAlgo(sigAlgo crypto.SignatureAlgorithm) *AccountKey

SetSigAlgo sets the signature algorithm for this account key.

func (*AccountKey) SetWeight

func (a *AccountKey) SetWeight(weight int) *AccountKey

SetWeight sets the weight for this account key.

func (AccountKey) Validate

func (a AccountKey) Validate() error

Validate returns an error if this account key is invalid.

An account key can be invalid for the following reasons: - It specifies an incompatible signature/hash algorithm pairing - (TODO) It specifies a negative key weight

type Address

type Address [AddressLength]byte

An Address is a 20-byte identifier for a Flow account.

func BytesToAddress

func BytesToAddress(b []byte) Address

BytesToAddress returns Address with value b.

If b is larger than len(h), b will be cropped from the left.

func HexToAddress

func HexToAddress(h string) Address

HexToAddress converts a hex string to an Address.

func (Address) Bytes

func (a Address) Bytes() []byte

Bytes returns the byte representation of this address.

func (Address) Hex

func (a Address) Hex() string

Hex returns the hex string representation of this address.

func (Address) MarshalJSON

func (a Address) MarshalJSON() ([]byte, error)

func (*Address) SetBytes

func (a *Address) SetBytes(b []byte)

SetBytes sets this address to the value of b.

If b is larger than len(a) it will panic.

func (Address) Short

func (a Address) Short() string

Short returns the string representation of this address with leading zeros removed.

func (Address) String

func (a Address) String() string

String returns the string representation of this address.

func (*Address) UnmarshalJSON

func (a *Address) UnmarshalJSON(data []byte) error

type Block

type Block struct {
	BlockHeader
	BlockPayload
}

A Block is a set of state mutations applied to the Flow blockchain.

type BlockHeader

type BlockHeader struct {
	ID       Identifier
	ParentID Identifier
	Height   uint64
}

A BlockHeader is a summary of a full block.

type BlockPayload

type BlockPayload struct {
	CollectionGuarantees []*CollectionGuarantee
	Seals                []*BlockSeal
}

A BlockPayload is the full contents of a block.

A payload contains the collection guarantees and seals for a block.

type BlockSeal

type BlockSeal struct{}

TODO: define block seal struct

type Collection

type Collection struct {
	TransactionIDs []Identifier
}

A Collection is a list of transactions bundled together for inclusion in a block.

func (Collection) Encode

func (c Collection) Encode() []byte

Encode returns the canonical RLP byte representation of this collection.

func (Collection) ID

func (c Collection) ID() Identifier

ID returns the canonical SHA3-256 hash of this collection.

type CollectionGuarantee

type CollectionGuarantee struct {
	CollectionID Identifier
}

A CollectionGuarantee is an attestation signed by the nodes that have guaranteed a collection.

type Event

type Event struct {
	// Type is the qualified event type.
	Type string
	// TransactionID is the ID of the transaction this event was emitted from.
	TransactionID Identifier
	// TransactionIndex is the index of the transaction this event was emitted from, within its containing block.
	TransactionIndex int
	// EventIndex is the index of the event within the transaction it was emitted from.
	EventIndex int
	// Value contains the event data.
	Value cadence.Event
}

func (Event) Encode

func (e Event) Encode() []byte

Encode returns the canonical RLP byte representation of this event.

func (Event) ID

func (e Event) ID() string

ID returns the canonical SHA3-256 hash of this event.

func (Event) String

func (e Event) String() string

String returns the string representation of this event.

type Identifier

type Identifier [32]byte

An Identifier is a 32-byte unique identifier for an entity.

func BytesToID

func BytesToID(b []byte) Identifier

BytesToID constructs an identifier from a byte slice.

func HashToID

func HashToID(hash []byte) Identifier

func (Identifier) Bytes

func (i Identifier) Bytes() []byte

Bytes returns the bytes representation of this identifier.

func (Identifier) Hex

func (i Identifier) Hex() string

Hex returns the hexadecimal string representation of this identifier.

func (Identifier) String

func (i Identifier) String() string

String returns the string representation of this identifier.

type ProposalKey

type ProposalKey struct {
	Address        Address
	KeyID          int
	SequenceNumber uint64
}

A ProposalKey is the key that specifies the proposal key and sequence number for a transaction.

type Transaction

type Transaction struct {
	Script             []byte
	ReferenceBlockID   Identifier
	GasLimit           uint64
	ProposalKey        ProposalKey
	Payer              Address
	Authorizers        []Address
	PayloadSignatures  []TransactionSignature
	EnvelopeSignatures []TransactionSignature
}

A Transaction is a full transaction object containing a payload and signatures.

Example
// Mock user accounts

adrianLaptopKey := &flow.AccountKey{
	ID:             3,
	SequenceNumber: 42,
}

adrianPhoneKey := &flow.AccountKey{ID: 2}

adrian := flow.Account{
	Address: flow.HexToAddress("01"),
	Keys: []*flow.AccountKey{
		adrianLaptopKey,
		adrianPhoneKey,
	},
}

blaineHardwareKey := &flow.AccountKey{ID: 7}

blaine := flow.Account{
	Address: flow.HexToAddress("02"),
	Keys: []*flow.AccountKey{
		blaineHardwareKey,
	},
}

// Transaction preparation

tx := flow.NewTransaction().
	SetScript([]byte(`transaction { execute { log("Hello, World!") } }`)).
	SetReferenceBlockID(flow.Identifier{0x01, 0x02}).
	SetGasLimit(42).
	SetProposalKey(adrian.Address, adrianLaptopKey.ID, adrianLaptopKey.SequenceNumber).
	SetPayer(blaine.Address).
	AddAuthorizer(adrian.Address)

fmt.Printf("Transaction ID (before signing): %s\n\n", tx.ID())

// Signing

err := tx.SignPayload(adrian.Address, adrianLaptopKey.ID, test.MockSigner([]byte{1}))
if err != nil {
	panic(err)
}

err = tx.SignPayload(adrian.Address, adrianPhoneKey.ID, test.MockSigner([]byte{2}))
if err != nil {
	panic(err)
}

err = tx.SignEnvelope(blaine.Address, blaineHardwareKey.ID, test.MockSigner([]byte{3}))
if err != nil {
	panic(err)
}

fmt.Println("Payload signatures:")
for _, sig := range tx.PayloadSignatures {
	fmt.Printf(
		"Address: %s, Key ID: %d, Signature: %x\n",
		sig.Address,
		sig.KeyID,
		sig.Signature,
	)
}
fmt.Println()

fmt.Println("Envelope signatures:")
for _, sig := range tx.EnvelopeSignatures {
	fmt.Printf(
		"Address: %s, Key ID: %d, Signature: %x\n",
		sig.Address,
		sig.KeyID,
		sig.Signature,
	)
}
fmt.Println()

fmt.Printf("Transaction ID (after signing): %s\n", tx.ID())
Output:

Transaction ID (before signing): 142a7862e1718bae9bae109d950a45a2540f98593edf1916ad89f4e22a0960c9

Payload signatures:
Address: 0000000000000000000000000000000000000001, Key ID: 2, Signature: 02
Address: 0000000000000000000000000000000000000001, Key ID: 3, Signature: 01

Envelope signatures:
Address: 0000000000000000000000000000000000000002, Key ID: 7, Signature: 03

Transaction ID (after signing): ae9ef40a6f74117ded103e8bd8c8b0fb97c5a2b3503c1e7ba4aa467f662e31fb

func NewTransaction

func NewTransaction() *Transaction

NewTransaction initializes and returns an empty transaction.

func (*Transaction) AddAuthorizer

func (t *Transaction) AddAuthorizer(address Address) *Transaction

AddAuthorizer adds an authorizer account to this transaction.

func (*Transaction) AddEnvelopeSignature

func (t *Transaction) AddEnvelopeSignature(address Address, keyID int, sig []byte) *Transaction

AddEnvelopeSignature adds an envelope signature to the transaction for the given address and key ID.

func (*Transaction) AddPayloadSignature

func (t *Transaction) AddPayloadSignature(address Address, keyID int, sig []byte) *Transaction

AddPayloadSignature adds a payload signature to the transaction for the given address and key ID.

func (*Transaction) Encode

func (t *Transaction) Encode() []byte

Encode serializes the full transaction data including the payload and all signatures.

func (*Transaction) EnvelopeMessage

func (t *Transaction) EnvelopeMessage() []byte

EnvelopeMessage returns the signable message for the transaction envelope.

This message is only signed by the payer account.

func (*Transaction) ID

func (t *Transaction) ID() Identifier

ID returns the canonical SHA3-256 hash of this transaction.

func (*Transaction) PayloadMessage

func (t *Transaction) PayloadMessage() []byte

func (*Transaction) SetGasLimit

func (t *Transaction) SetGasLimit(limit uint64) *Transaction

SetGasLimit sets the gas limit for this transaction.

func (*Transaction) SetPayer

func (t *Transaction) SetPayer(address Address) *Transaction

SetPayer sets the payer account for this transaction.

func (*Transaction) SetProposalKey

func (t *Transaction) SetProposalKey(address Address, keyID int, sequenceNum uint64) *Transaction

SetProposalKey sets the proposal key and sequence number for this transaction.

The first two arguments specify the account key to be used, and the last argument is the sequence number being declared.

func (*Transaction) SetReferenceBlockID

func (t *Transaction) SetReferenceBlockID(blockID Identifier) *Transaction

SetReferenceBlockID sets the reference block ID for this transaction.

func (*Transaction) SetScript

func (t *Transaction) SetScript(script []byte) *Transaction

SetScript sets the Cadence script for this transaction.

func (*Transaction) SignEnvelope

func (t *Transaction) SignEnvelope(address Address, keyID int, signer crypto.Signer) error

SignEnvelope signs the full transaction (payload + payload signatures) with the specified account key.

The resulting signature is combined with the account address and key ID before being added to the transaction.

This function returns an error if the signature cannot be generated.

func (*Transaction) SignPayload

func (t *Transaction) SignPayload(address Address, keyID int, signer crypto.Signer) error

SignPayload signs the transaction payload with the specified account key.

The resulting signature is combined with the account address and key ID before being added to the transaction.

This function returns an error if the signature cannot be generated.

type TransactionResult

type TransactionResult struct {
	Status TransactionStatus
	Error  error
	Events []Event
}

type TransactionSignature

type TransactionSignature struct {
	Address     Address
	SignerIndex int
	KeyID       int
	Signature   []byte
}

A TransactionSignature is a signature associated with a specific account key.

type TransactionStatus

type TransactionStatus int

TransactionStatus represents the status of a transaction.

const (
	// TransactionStatusUnknown indicates that the transaction status is not known.
	TransactionStatusUnknown TransactionStatus = iota
	// TransactionStatusPending is the status of a pending transaction.
	TransactionStatusPending
	// TransactionStatusFinalized is the status of a finalized transaction.
	TransactionStatusFinalized
	// TransactionStatusExecuted is the status of an executed transaction.
	TransactionStatusExecuted
	// TransactionStatusSealed is the status of a sealed transaction.
	TransactionStatusSealed
)

func (TransactionStatus) String

func (s TransactionStatus) String() string

String returns the string representation of a transaction status.

Directories

Path Synopsis
Package templates provides functions for generating common Cadence scripts.
Package templates provides functions for generating common Cadence scripts.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL