-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from qubic/14-utility-and-documentation
Implement asset transfers and new tests.
- Loading branch information
Showing
10 changed files
with
771 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
|
||
}) | ||
} | ||
|
||
} |
Oops, something went wrong.