Skip to content

Commit

Permalink
Merge pull request #16 from qubic/14-utility-and-documentation
Browse files Browse the repository at this point in the history
Implement asset transfers and new tests.
  • Loading branch information
0xluk authored Feb 10, 2025
2 parents 4dabee8 + 67fb30c commit 9abc70c
Show file tree
Hide file tree
Showing 10 changed files with 771 additions and 163 deletions.
120 changes: 120 additions & 0 deletions types/live_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package types

import (
"bytes"
"encoding/json"
"github.com/pkg/errors"
"io"
"net/http"
)

type LiveServiceClient struct {
BaseUrl string
}

func NewLiveServiceClient(baseUrl string) LiveServiceClient {
return LiveServiceClient{
BaseUrl: baseUrl,
}
}

func (lsc *LiveServiceClient) GetTickInfo() (*TickInfoResponse, error) {

request, err := http.NewRequest(http.MethodGet, lsc.BaseUrl+"/v1/tick-info", nil)
if err != nil {
return nil, errors.Wrap(err, "creating tick info request")
}

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

if response.StatusCode != http.StatusOK {
return nil, lsc.handleHttpError(response.Body)
}

var responseBody TickInfoResponse
err = json.NewDecoder(response.Body).Decode(&responseBody)
if err != nil {
return nil, errors.Wrap(err, "decoding tick info response")
}

return &responseBody, nil
}

func (lsc *LiveServiceClient) BroadcastTransaction(tx Transaction) (*TransactionBroadcastResponse, error) {

if tx.Signature == [64]byte{} {
return nil, errors.New("cannot broadcast unsigned transaction")
}

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

requestPayload := TransactionBroadcastRequest{
EncodedTransaction: encodedTransaction,
}

buff := new(bytes.Buffer)
err = json.NewEncoder(buff).Encode(requestPayload)
if err != nil {
return nil, errors.Wrap(err, "encoding transaction broadcast payload")
}

request, err := http.NewRequest(http.MethodPost, lsc.BaseUrl+"/v1/broadcast-transaction", buff)
if err != nil {
return nil, errors.Wrap(err, "creating transaction broadcast request")
}

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

if response.StatusCode != http.StatusOK {
return nil, lsc.handleHttpError(response.Body)
}

var responseBody TransactionBroadcastResponse
err = json.NewDecoder(response.Body).Decode(&responseBody)
if err != nil {
return nil, errors.Wrap(err, "decoding transaction broadcast response")
}

return &responseBody, nil
}

func (lsc *LiveServiceClient) handleHttpError(responseBody io.Reader) error {

data, err := io.ReadAll(responseBody)
if err != nil {
return errors.Wrap(err, "reading error body")
}
errorString := string(data)

return errors.Errorf("response status not OK : %s", errorString)
}

type TransactionBroadcastRequest struct {
EncodedTransaction string `json:"encodedTransaction"`
}

type TransactionBroadcastResponse struct {
PeersBroadcasted uint32 `json:"peersBroadcasted"`
EncodedTransaction string `json:"encodedTransaction"`
TransactionId string `json:"transactionId"`
}

type TickInfoResponse struct {
TickInfo struct {
Tick uint32 `json:"tick"`
Duration uint32 `json:"duration"`
Epoch uint32 `json:"epoch"`
InitialTick uint32 `json:"initialTick"`
} `json:"tickInfo"`
}
61 changes: 61 additions & 0 deletions types/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package types

import (
"github.com/pkg/errors"
"github.com/qubic/go-schnorrq"
)

type Signer struct {
seed string

pubKey [32]byte
}

func NewSigner(seed string) (*Signer, error) {

wallet, err := NewWallet(seed)
if err != nil {
return nil, errors.Wrap(err, "creating wallet")
}

pubKey := wallet.PubKey

return &Signer{
seed: seed,
pubKey: pubKey,
}, nil
}

// SignTx Returns the signed transaction. The original transaction object is not modified, and the returned value should be used after signing.
func (s *Signer) SignTx(tx Transaction) (Transaction, error) {

if tx.SourcePublicKey != s.pubKey {
return Transaction{}, errors.New("source public key does not match signer")
}

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

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

signature, err := schnorrq.Sign(subSeed, tx.SourcePublicKey, unsignedDigest)
if err != nil {
return Transaction{}, errors.Wrap(err, "creating signature")
}

return Transaction{
SourcePublicKey: tx.SourcePublicKey,
DestinationPublicKey: tx.DestinationPublicKey,
Amount: tx.Amount,
Tick: tx.Tick,
InputType: tx.InputType,
InputSize: tx.InputSize,
Input: tx.Input,
Signature: signature,
}, nil
}
73 changes: 73 additions & 0 deletions types/signer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package types

import (
"github.com/qubic/go-schnorrq"
"testing"
)

func TestSigner_SignTx(t *testing.T) {

testData := []struct {
name string
senderSeed string
senderIdentity string
destinationIdentity string
}{
{
name: "TestSign_1",
senderSeed: "yfcqxawkwvhnwwxnhxqbzufpnbxxvkpuueermpcxoiugqokwbmurqjq",
senderIdentity: "LZTPJBQKOYLBFEWWFVEFDFOOFEWCUSSNNKLOXGDQJGBTYUMJVAOSYHIGYDOM",
destinationIdentity: "UIJLDDELETUYEHFKZPQGVOOOTLHCNQWAZAXHLSXWMEDLRQEWKNSJVZIGFPBD",
},
{
name: "TestSign_2",
senderSeed: "oqrtktmxmowfwpliikiogiczvpelmuaamreundljwnnpjojvtsfhtgd",
senderIdentity: "UIJLDDELETUYEHFKZPQGVOOOTLHCNQWAZAXHLSXWMEDLRQEWKNSJVZIGFPBD",
destinationIdentity: "ZFEEMHFUDDGUJBFXDVHXOHKDSELCAWDCUTASNOAMQDTZWILDTCDCSNQGHEGN",
},
{
name: "TestSign_3",
senderSeed: "eivjjmrusievohpkmqaxanvvkbsglxigqevbnxfqswmtuxbhmphjpzw",
senderIdentity: "ZFEEMHFUDDGUJBFXDVHXOHKDSELCAWDCUTASNOAMQDTZWILDTCDCSNQGHEGN",
destinationIdentity: "COVLRIWUCHTKZFGXEFYFSVNWDXECAYJXXSLSKDETUCCDMTRTAWCLSFOCJJSA",
},
{
name: "TestSign_4",
senderSeed: "edsllpxbhvsrqdnhxpinwabnmkgjyrbszgbtcmuertkefhmsqtptcgj",
senderIdentity: "COVLRIWUCHTKZFGXEFYFSVNWDXECAYJXXSLSKDETUCCDMTRTAWCLSFOCJJSA",
destinationIdentity: "CSPGQLVUJIUCFESXLVBHVYSGDRLDJNVUGWMKQLPYNFKRPQAAFOKILXBFIIUJ",
},
}

for _, data := range testData {
t.Run(data.name, func(t *testing.T) {

tx, err := NewSimpleTransferTransaction(data.senderIdentity, data.destinationIdentity, 0, 0)
if err != nil {
t.Fatalf("creating simple transfer transaction: %s", err)
}

signer, err := NewSigner(data.senderSeed)
if err != nil {
t.Fatalf("creating signer: %s", err)
}

tx, err = signer.SignTx(tx)
if err != nil {
t.Fatalf("signing tx: %s", err)
}

unsignedDigest, err := tx.GetUnsignedDigest()
if err != nil {
t.Fatalf("getting unsigned digest: %s", err)
}

err = schnorrq.Verify(tx.SourcePublicKey, unsignedDigest, tx.Signature)
if err != nil {
t.Fatalf("verifying signature: %s", err)
}

})
}

}
Loading

0 comments on commit 9abc70c

Please sign in to comment.