Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Requested Go examples #8

Open
LINCKODE opened this issue Aug 23, 2024 · 3 comments
Open

Requested Go examples #8

LINCKODE opened this issue Aug 23, 2024 · 3 comments

Comments

@LINCKODE
Copy link
Member

No description provided.

@LINCKODE
Copy link
Member Author

Transaction creation and broadcasting:

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"github.com/qubic/go-node-connector/types"
	"github.com/qubic/go-schnorrq"
	"io"
	"net/http"
	"time"
)

// Create simple transaction and broadcast it to the network

const ApiURL = "https://testapi.qubic.org"

const SenderSeed = ""
const SenderID = ""
const TransactionAmount = 5
const DestinationID = ""

func CreateAndBroadcastTransaction() error {

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()

	subSeed, err := types.GetSubSeed(SenderSeed)
	if err != nil {
		return errors.Wrap(err, "getting sub-seed from seed")
	}

	latestTick, err := GetLatestTick(ctx, ApiURL)
	if err != nil {
		return errors.Wrap(err, "getting latest tick")
	}

	targetTick := latestTick + 10

	transaction, err := CreateTransaction(SenderID, DestinationID, TransactionAmount, targetTick, subSeed)
	if err != nil {
		return errors.Wrap(err, "creating transaction")
	}

	err = BroadCastTransaction(ctx, transaction, ApiURL)
	if err != nil {
		return errors.Wrap(err, "broadcasting transaction")
	}

	return nil
}

func CreateTransaction(sourceID, destinationID string, amount int64, targetTick uint32, subSeed [32]byte) (*types.Transaction, error) {
	transaction, err := types.NewSimpleTransferTransaction(sourceID, destinationID, amount, targetTick)
	if err != nil {
		return nil, errors.Wrap(err, "creating transaction")
	}

	unsignedDigest, err := transaction.GetUnsignedDigest()
	if err != nil {
		return nil, errors.Wrap(err, "getting transaction unsigned digest")
	}

	signature, err := schnorrq.Sign(subSeed, transaction.SourcePublicKey, unsignedDigest)
	if err != nil {
		return nil, errors.Wrap(err, "failed to sign transaction")
	}
	transaction.Signature = signature

	return &transaction, nil

}

func GetLatestTick(ctx context.Context, apiUrl string) (uint32, error) {

	request, err := http.NewRequestWithContext(ctx, http.MethodGet, apiUrl+"/v1/latestTick", nil)
	if err != nil {
		return 0, errors.Wrap(err, "creating latest tick request")
	}

	response, err := http.DefaultClient.Do(request)
	if err != nil {
		return 0, errors.Wrap(err, "performing latest tick request")
	}
	defer response.Body.Close()

	data, err := io.ReadAll(response.Body)
	if err != nil {
		return 0, errors.Wrap(err, "reading latest tick response")
	}

	if response.StatusCode != http.StatusOK {
		return 0, errors.New(fmt.Sprintf("Status not 200! Info: %s", data))
	}

	type LatestTickStruct struct {
		LatestTick uint32 `json:"latestTick"`
	}

	var latestTick LatestTickStruct
	err = json.Unmarshal(data, &latestTick)
	if err != nil {
		return 0, errors.Wrap(err, "un-marshalling latest tick response")
	}

	return latestTick.LatestTick, nil
}

func BroadCastTransaction(ctx context.Context, transaction *types.Transaction, apiUrl string) error {

	transactionID, err := transaction.ID()
	if err != nil {
		return errors.Wrap(err, "getting transaction ID")
	}

	finalUrl := apiUrl + "/v1/broadcast-transaction"

	// Encode transaction
	encodedTransaction, err := transaction.EncodeToBase64()
	if err != nil {
		return errors.Wrap(err, "encoding transaction")
	}

	// Create request payload from encoded transaction
	requestPayload := struct {
		EncodedTransaction string `json:"encodedTransaction"`
	}{
		EncodedTransaction: encodedTransaction,
	}
	buffer := new(bytes.Buffer)

	err = json.NewEncoder(buffer).Encode(requestPayload)
	if err != nil {
		return errors.Wrap(err, "encoding request payload")
	}

	//Create and send request
	request, err := http.NewRequestWithContext(ctx, http.MethodPost, finalUrl, buffer)
	if err != nil {
		return errors.Wrap(err, "creating broadcast request")
	}

	response, err := http.DefaultClient.Do(request)
	if err != nil {
		return errors.Wrap(err, "performing broadcast request")
	}
	defer response.Body.Close()

	data, err := io.ReadAll(response.Body)
	if err != nil {
		return errors.Wrap(err, "reading broadcast response")
	}

	if response.StatusCode != http.StatusOK {
		fmt.Printf("DEBUG: %s\n", finalUrl)
		return errors.New(fmt.Sprintf("Status not 200! Info: %s", data))
	}

	type ResponseInfo struct {
		PeersBroadcasted   uint32 `json:"peersBroadcasted"`
		EncodedTransaction string `json:"encodedTransaction"`
	}

	var info ResponseInfo
	err = json.Unmarshal(data, &info)
	if err != nil {
		return errors.Wrap(err, "un-marshalling broadcast response")
	}

	fmt.Printf("Broadcasted to %d peers.\n", info.PeersBroadcasted)
	fmt.Printf("Transaction ID: %s\n", transactionID)
	fmt.Printf("Target tick: %d\n", transaction.Tick)

	return nil
}

@LINCKODE
Copy link
Member Author

Query last tick and it's transactions

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"io"
	"net/http"
	"time"
)

// Query the last network tick and the transactions in it

const archiverHTTPAddress = "https://testapi.qubic.org"

func RunHTTPExample() error {

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()

	httpClient := http.DefaultClient

	// Get Status
	status, err := FetchStatusHTTP(ctx, httpClient, archiverHTTPAddress)
	if err != nil {
		return errors.Wrap(err, "fetching archiver status")
	}

	fmt.Printf("Last processed tick: %d\n", status.LastProcessedTick.TickNumber)
	fmt.Printf("Current epoch: %d\n", status.LastProcessedTick.Epoch)

	// Get transactions in the latest tick

	transactions, err := GetTickTransactionsHTTP(ctx, httpClient, archiverHTTPAddress, status.LastProcessedTick.TickNumber)
	if err != nil {
		return errors.Wrap(err, "fetching tick transactions")
	}

	fmt.Printf("Found %d transactions\n", len(transactions.Transactions))

	for _, transaction := range transactions.Transactions {
		fmt.Printf("  Transaction ID: %s\n", transaction.Transaction.TxId)
		fmt.Printf("  Transfered amount: %s\n", transaction.Transaction.Amount)
		fmt.Printf("  From: %s\n", transaction.Transaction.SourceId)
		fmt.Printf("  To: %s\n", transaction.Transaction.DestId)
		println("-------------------------------")

	}

	return nil

}

func FetchStatusHTTP(ctx context.Context, httpClient *http.Client, baseURL string) (*Status, error) {

	statusRequest, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/v1/status", nil)
	if err != nil {
		return nil, errors.Wrap(err, "creating status request")
	}

	response, err := httpClient.Do(statusRequest)
	if err != nil {
		return nil, errors.Wrap(err, "performing status request")
	}
	defer response.Body.Close()

	data, err := io.ReadAll(response.Body)
	if err != nil {
		return nil, errors.Wrap(err, "reading status response")
	}

	if response.StatusCode != http.StatusOK {
		return nil, errors.New(fmt.Sprintf("Status not 200! Info: %s", data))
	}

	var status Status

	err = json.Unmarshal(data, &status)
	if err != nil {
		return nil, errors.Wrap(err, "un-marshalling status response")
	}

	return &status, nil
}

func GetTickTransactionsHTTP(ctx context.Context, httpClient *http.Client, baseURL string, tickNumber uint32) (*TickTransactions, error) {

	finalURL := fmt.Sprintf(baseURL+"/v2/ticks/%d/transactions?transfers=true", tickNumber)

	tickTransactionsRequest, err := http.NewRequestWithContext(ctx, "GET", finalURL, nil)
	if err != nil {
		return nil, errors.Wrap(err, "creating tick transactions request")
	}

	response, err := httpClient.Do(tickTransactionsRequest)
	if err != nil {
		return nil, errors.Wrap(err, "performing tick transactions request")
	}
	defer response.Body.Close()

	data, err := io.ReadAll(response.Body)
	if err != nil {
		return nil, errors.Wrap(err, "reading tick transactions response")
	}

	if response.StatusCode != http.StatusOK {
		return nil, errors.New(fmt.Sprintf("Status not 200! Info: %s", data))
	}

	var transactions TickTransactions

	err = json.Unmarshal(data, &transactions)
	if err != nil {
		return nil, errors.Wrap(err, "un-marshalling tick transactions response")
	}

	return &transactions, nil

}

type Status struct {
	LastProcessedTick struct {
		TickNumber uint32 `json:"tickNumber"`
		Epoch      uint32 `json:"epoch"`
	} `json:"lastProcessedTick"`
	LastProcessedTicksPerEpoch map[string]uint32 `json:"lastProcessedTicksPerEpoch"`
	SkippedTicks               []struct {
		StartTick uint32 `json:"startTick"`
		EndTick   uint32 `json:"endTick"`
	} `json:"skippedTicks"`
	ProcessedTickIntervalsPerEpoch []struct {
		Epoch     uint32 `json:"epoch"`
		Intervals []struct {
			InitialProcessedTick uint32 `json:"initialProcessedTick"`
			LastProcessedTick    uint32 `json:"lastProcessedTick"`
		} `json:"intervals"`
	}
	EmptyTicksPerEpoch map[string]uint32 `json:"emptyTicksPerEpoch"`
}

type TickTransactions struct {
	Transactions []struct {
		Transaction struct {
			SourceId     string `json:"sourceId"`
			DestId       string `json:"destId"`
			Amount       string `json:"amount"`
			TickNumber   uint32 `json:"tickNumber"`
			InputType    uint32 `json:"inputType"`
			InputSize    uint32 `json:"inputSize"`
			InputHex     string `json:"inputHex"`
			SignatureHex string `json:"signatureHex"`
			TxId         string `json:"txId"`
		} `json:"transaction"`
		Timestamp string `json:"timestamp"`
		MoneyFlew bool   `json:"moneyFlew"`
	} `json:"transactions"`
}

@LINCKODE
Copy link
Member Author

GRPC version of above example

const archiverGRPCAddress = "213.170.135.5:8003"

func RunGRPCExample() error {

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()

	// GRPC client creation
	connection, err := grpc.NewClient(archiverGRPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		return errors.Wrap(err, "creating grpc connection")
	}
	defer connection.Close()

	archiverClient := protobuff.NewArchiveServiceClient(connection)

	// Get status
	status, err := FetchStatusGRPC(ctx, archiverClient)
	if err != nil {
		return errors.Wrap(err, "fetching status")
	}

	fmt.Printf("Last processed tick: %d\n", status.LastProcessedTick.TickNumber)
	fmt.Printf("Current epoch: %d\n", status.LastProcessedTick.Epoch)

	// Get transactions in the latest tick

	transactions, err := GetTickTransactionsGRPC(ctx, archiverClient, status.LastProcessedTick.TickNumber, true)
	if err != nil {
		return errors.Wrap(err, "fetching tick transactions")
	}

	fmt.Printf("Found %d transactions\n", len(transactions.Transactions))

	for _, transaction := range transactions.Transactions {
		fmt.Printf("  Transaction ID: %s\n", transaction.Transaction.TxId)
		fmt.Printf("  Transfered amount: %d\n", transaction.Transaction.Amount)
		fmt.Printf("  From: %s\n", transaction.Transaction.SourceId)
		fmt.Printf("  To: %s\n", transaction.Transaction.DestId)
		println("-------------------------------")

	}

	return nil
}

func FetchStatusGRPC(ctx context.Context, archiverClient protobuff.ArchiveServiceClient) (*protobuff.GetStatusResponse, error) {

	result, err := archiverClient.GetStatus(ctx, nil)
	if err != nil {
		return nil, errors.Wrap(err, "performing request")
	}

	return result, nil
}

func GetTickTransactionsGRPC(ctx context.Context, archiverClient protobuff.ArchiveServiceClient, tickNumber uint32, transfersOnly bool) (*protobuff.GetTickTransactionsResponseV2, error) {

	request := protobuff.GetTickTransactionsRequestV2{
		TickNumber: tickNumber,
		// Do not filter, we want to see all transactions
		Approved:  false,
		Transfers: transfersOnly,
	}

	result, err := archiverClient.GetTickTransactionsV2(ctx, &request)
	if err != nil {
		return nil, errors.Wrap(err, "performing request")
	}

	return result, nil

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant