common and core changed

This commit is contained in:
Dmitry Anderson 2024-11-17 22:24:19 +01:00
parent 6dfb61d447
commit 4e044cdad9
19 changed files with 688 additions and 176 deletions

10
common/block.go Normal file
View File

@ -0,0 +1,10 @@
package common
type BlockJson struct {
}
type Block struct {
Id int64
PrevHash []byte
Hash []byte
}

9
common/burning.go Normal file
View File

@ -0,0 +1,9 @@
package common
import "math"
const BurningRate = 0.001
func CalcBurning(trxAmount float64) float64 {
return BurningRate * math.Sqrt(trxAmount)
}

31
common/common.go Normal file
View File

@ -0,0 +1,31 @@
package common
import (
"encoding/base64"
"encoding/binary"
"errors"
)
const (
EncodingRawUrl = "encoring_row_url"
EncodingRawStd = "encoring_row_std"
)
func GetEncoding(encoding string) (*base64.Encoding, error) {
switch encoding {
case EncodingRawUrl:
return base64.RawURLEncoding, nil
case EncodingRawStd:
return base64.RawStdEncoding, nil
default:
return nil, errors.New("unsupported encoding")
}
}
// float64ToBytes converts float64 to []byte
// May be moved to nettools later
func float64ToBytes(f float64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, uint64(f))
return buf
}

View File

@ -1,11 +0,0 @@
package common
const VersionNumber = 0.01
const MinBurningAmount = 0.001
func CalcBurning(trxAmount float64) float64 {
if trxAmount < 1 {
return MinBurningAmount
}
return trxAmount * MinBurningAmount
}

4
common/http_err.go Normal file
View File

@ -0,0 +1,4 @@
package common
type HttpErr struct {
}

View File

@ -1,52 +0,0 @@
package common
import (
"bytes"
ed "crypto/ed25519"
"crypto/sha512"
"encoding/binary"
"time"
)
func Float64ToBytes(f float64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, uint64(f))
return buf
}
func GetTransactionHash(receiverPubKey []byte, message string, amount float64, createdAt time.Time) []byte {
amountBytes := Float64ToBytes(amount)
hash := sha512.New()
timestamp := createdAt.Unix()
_ = binary.Write(hash, binary.BigEndian, timestamp)
hash.Write([]byte(message))
hash.Write(receiverPubKey)
hash.Write(amountBytes)
return hash.Sum(nil)
}
func getSignMsg(txHash []byte, createdAt time.Time) ([]byte, error) {
buf := bytes.NewBuffer(txHash)
timestamp := createdAt.Unix()
if err := binary.Write(buf, binary.BigEndian, timestamp); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func SingTransaction(key ed.PrivateKey, txHash []byte, signedAt time.Time) (sig []byte, err error) {
msg, err := getSignMsg(txHash, signedAt)
if err != nil {
return nil, err
}
sig = ed.Sign(key, msg)
return sig, nil
}
func VerifyTransactionSignature(key ed.PublicKey, signature []byte, txHash []byte, signedAt time.Time) (valid bool, err error) {
msg, err := getSignMsg(txHash, signedAt)
if err != nil {
return false, err
}
return ed.Verify(key, msg, signature), nil
}

327
common/tx.go Normal file
View File

@ -0,0 +1,327 @@
package common
import (
"bytes"
"crypto/sha512"
"encoding/base64"
"encoding/binary"
"encoding/json"
"github.com/matchsystems/werr"
"golang.org/x/crypto/ed25519"
"io"
"time"
)
func GetTxHash(receiverPubKey []byte, message string, amount float64, blockId, timestamp, timestampNs int64) []byte {
hash := sha512.New()
_ = binary.Write(hash, binary.BigEndian, blockId)
_ = binary.Write(hash, binary.BigEndian, timestamp)
_ = binary.Write(hash, binary.BigEndian, timestampNs)
hash.Write(float64ToBytes(amount))
hash.Write([]byte(message))
hash.Write(receiverPubKey)
return hash.Sum(nil)
}
func GetBlockId(t time.Time) int64 {
return (t.UTC().Unix() / 100) * 100
}
type NewTxOptsJson struct {
Hash string `json:"hash"` // use function GetTxHash
// (CreatedAt / 100) * 100
BlockId int64 `json:"block_id"`
SenderPublicKey string `json:"sender_public_key"`
ReceiverPublicKey string `json:"receiver_public_key"`
IsReward bool `json:"is_reward"`
Amount float64 `json:"amount"`
Message string `json:"message"`
Signature string `json:"signature"`
CreatedAt int64 `json:"created_at"`
CreatedAtNs int64 `json:"created_at_ns"`
ByteEncoding string `json:"byte_encoding"`
}
// NewTxOpts sent from client to server to add a new transaction
type NewTxOpts struct {
Hash []byte
BlockId int64
SenderPublicKey []byte
ReceiverPublicKey []byte
IsReward bool
Amount float64
Message string
// Signed TX Hash with senders private key
Signature []byte
CreatedAt int64
CreatedAtNs int64 // Nanoseconds
}
type TxJson struct {
Hash string `json:"hash"` // use function GetTxHash
// (CreatedAt / 100) * 100
BlockId int64 `json:"block_id"`
SenderPublicKey string `json:"sender_public_key"`
ReceiverPublicKey string `json:"receiver_public_key"`
IsReward bool `json:"is_reward"`
Amount float64 `json:"amount"`
AmountBurned float64 `json:"amount_burned"`
Message string `json:"message"`
Signature string `json:"signature"`
CreatedAt int64 `json:"created_at"`
CreatedAtNs int64 `json:"created_at_ns"`
AddedAt time.Time `json:"added_at"`
IsAdded bool `json:"is_added"`
ByteEncoding string `json:"byte_encoding"`
}
// Tx is a transaction structure
// that is sent from server to the client
// with the information about requested transaction
type Tx struct {
Hash []byte
BlockId int64
SenderPublicKey []byte
ReceiverPublicKey []byte
IsReward bool
Amount float64
AmountBurned float64
Message string
// Signed TX Hash with senders private key
Signature []byte
CreatedAt int64
CreatedAtNs int64 // Nanoseconds
AddedAt time.Time
IsAdded bool
}
func MakeNewTxOpts(senderPubKey, senderPrivateKey, receiverPubKey []byte,
amount float64, msg string, isReward bool) *NewTxOpts {
now := time.Now().UTC()
createdAt := now.Unix()
createdAtNs := now.UnixNano()
blockId := (createdAt / 100) * 100
txHash := GetTxHash(receiverPubKey, msg, amount, blockId, createdAt, createdAtNs)
sig := ed25519.Sign(senderPrivateKey, txHash)
return &NewTxOpts{
Hash: txHash,
BlockId: blockId,
SenderPublicKey: senderPubKey,
ReceiverPublicKey: receiverPubKey,
IsReward: isReward,
Amount: amount,
Message: msg,
Signature: sig,
CreatedAt: createdAt,
CreatedAtNs: createdAtNs,
}
}
func (opts *NewTxOpts) ValidateBlockId() (valid bool) {
return opts.BlockId == (opts.CreatedAt/100)*100
}
func (opts *NewTxOpts) ValidateHash() (valid bool) {
return bytes.Equal(opts.Hash, GetTxHash(
opts.ReceiverPublicKey, opts.Message, opts.Amount, opts.BlockId, opts.CreatedAt, opts.CreatedAtNs,
))
}
func (opts *NewTxOpts) ValidateSignature() (valid bool) {
return ed25519.Verify(opts.SenderPublicKey, opts.Hash, opts.Signature)
}
func (opts *Tx) ValidateBlockId() (valid bool) {
return opts.BlockId == (opts.CreatedAt/100)*100
}
func (opts *Tx) ValidateHash() (valid bool) {
return bytes.Equal(opts.Hash, GetTxHash(
opts.ReceiverPublicKey, opts.Message, opts.Amount, opts.BlockId, opts.CreatedAt, opts.CreatedAtNs,
))
}
func (opts *Tx) ValidateSignature() (valid bool) {
return ed25519.Verify(opts.SenderPublicKey, opts.Hash, opts.Signature)
}
// HashString uses base64.RawURLEncoding encoding
func (opts *NewTxOpts) HashString() string {
return base64.RawURLEncoding.EncodeToString(opts.Hash)
}
// SignatureString uses base64.RawURLEncoding encoding
func (opts *NewTxOpts) SignatureString() string {
return base64.RawURLEncoding.EncodeToString(opts.Signature)
}
// HashString uses base64.RawURLEncoding encoding
func (opts *Tx) HashString() string {
return base64.RawURLEncoding.EncodeToString(opts.Hash)
}
// SignatureString uses base64.RawURLEncoding encoding
func (opts *Tx) SignatureString() string {
return base64.RawURLEncoding.EncodeToString(opts.Signature)
}
func (opts *NewTxOpts) ToJSON(byteEncodingStr string) ([]byte, error) {
encoding, err := GetEncoding(byteEncodingStr)
if err != nil {
return nil, werr.Wrapf(err, "error while trying to get byte->string encoding")
}
data := &NewTxOptsJson{
Hash: encoding.EncodeToString(opts.Hash),
BlockId: opts.BlockId,
SenderPublicKey: encoding.EncodeToString(opts.SenderPublicKey),
ReceiverPublicKey: encoding.EncodeToString(opts.ReceiverPublicKey),
IsReward: opts.IsReward,
Amount: opts.Amount,
Message: opts.Message,
Signature: encoding.EncodeToString(opts.Signature),
CreatedAt: opts.CreatedAt,
CreatedAtNs: opts.CreatedAtNs,
ByteEncoding: byteEncodingStr,
}
jsonBytes, err := json.Marshal(data)
if err != nil {
return nil, werr.Wrapf(err, "error while trying to marshal to json")
}
return jsonBytes, nil
}
func (opts *Tx) ToJSON(byteEncodingStr string) ([]byte, error) {
encoding, err := GetEncoding(byteEncodingStr)
if err != nil {
return nil, werr.Wrapf(err, "error while trying to get byte->string encoding")
}
data := &TxJson{
Hash: encoding.EncodeToString(opts.Hash),
BlockId: opts.BlockId,
SenderPublicKey: encoding.EncodeToString(opts.SenderPublicKey),
ReceiverPublicKey: encoding.EncodeToString(opts.ReceiverPublicKey),
IsReward: opts.IsReward,
Amount: opts.Amount,
AmountBurned: opts.AmountBurned,
Message: opts.Message,
Signature: encoding.EncodeToString(opts.Signature),
CreatedAt: opts.CreatedAt,
CreatedAtNs: opts.CreatedAtNs,
AddedAt: opts.AddedAt,
IsAdded: opts.IsAdded,
ByteEncoding: byteEncodingStr,
}
jsonBytes, err := json.Marshal(data)
if err != nil {
return nil, werr.Wrapf(err, "error while trying to marshal to json")
}
return jsonBytes, nil
}
func (opts *NewTxOptsJson) Process() (*NewTxOpts, error) {
out := &NewTxOpts{
BlockId: opts.BlockId,
IsReward: opts.IsReward,
Amount: opts.Amount,
Message: opts.Message,
CreatedAt: opts.CreatedAt,
CreatedAtNs: opts.CreatedAtNs,
}
encoding, err := GetEncoding(opts.ByteEncoding)
if err != nil {
return nil, werr.Wrapf(err, "error while trying to get byte->string encoding")
}
if out.Hash, err = encoding.DecodeString(opts.Hash); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode hash")
}
if out.SenderPublicKey, err = encoding.DecodeString(opts.SenderPublicKey); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode sender public key")
}
if out.ReceiverPublicKey, err = encoding.DecodeString(opts.ReceiverPublicKey); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode receiver public key")
}
if out.Signature, err = encoding.DecodeString(opts.Signature); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode signature")
}
return out, nil
}
func (opts *NewTxOpts) Process(isAdded bool, addedAt time.Time) (*Tx, error) {
out := &Tx{
Hash: opts.Hash,
BlockId: opts.BlockId,
SenderPublicKey: opts.SenderPublicKey,
ReceiverPublicKey: opts.ReceiverPublicKey,
IsReward: opts.IsReward,
Amount: opts.Amount,
AmountBurned: CalcBurning(opts.Amount),
Message: opts.Message,
Signature: opts.Signature,
CreatedAt: opts.CreatedAt,
CreatedAtNs: opts.CreatedAtNs,
AddedAt: addedAt,
IsAdded: isAdded,
}
return out, nil
}
func (tx *TxJson) Process() (*Tx, error) {
out := &Tx{
BlockId: tx.BlockId,
IsReward: tx.IsReward,
Amount: tx.Amount,
AmountBurned: tx.AmountBurned,
Message: tx.Message,
CreatedAt: tx.CreatedAt,
CreatedAtNs: tx.CreatedAtNs,
AddedAt: tx.AddedAt,
IsAdded: tx.IsAdded,
}
encoding, err := GetEncoding(tx.ByteEncoding)
if err != nil {
return nil, werr.Wrapf(err, "error while trying to get byte->string encoding")
}
if out.Hash, err = encoding.DecodeString(tx.Hash); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode hash")
}
if out.SenderPublicKey, err = encoding.DecodeString(tx.SenderPublicKey); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode sender public key")
}
if out.ReceiverPublicKey, err = encoding.DecodeString(tx.ReceiverPublicKey); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode receiver public key")
}
if out.Signature, err = encoding.DecodeString(tx.Signature); err != nil {
return nil, werr.Wrapf(err, "error while trying to decode signature")
}
return out, nil
}
func NewTxOptsFromJSON(marshaledData []byte) (opts *NewTxOpts, err error) {
data := &NewTxOptsJson{}
if err := json.Unmarshal(marshaledData, data); err != nil {
return nil, werr.Wrapf(err, "error while trying to unmarshal new transaction options")
}
return data.Process()
}
func NewTxOptsFromJsonReader(r io.Reader) (opts *NewTxOpts, err error) {
data := &NewTxOptsJson{}
if err := json.NewDecoder(r).Decode(&data); err != nil {
return nil, werr.Wrapf(err, "error while trying to unmarshal new transaction options")
}
return data.Process()
}
func TxFromJSON(marshaledData []byte) (opts *Tx, err error) {
data := &TxJson{}
if err := json.Unmarshal(marshaledData, data); err != nil {
return nil, werr.Wrapf(err, "error while trying to unmarshal new transaction options")
}
return data.Process()
}
func TxFromJsonReader(r io.Reader) (opts *Tx, err error) {
data := &TxJson{}
if err := json.NewDecoder(r).Decode(&data); err != nil {
return nil, werr.Wrapf(err, "error while trying to unmarshal new transaction options")
}
return data.Process()
}

74
common/tx_test.go Normal file
View File

@ -0,0 +1,74 @@
package common
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"testing"
"time"
)
func TestNewTx(t *testing.T) {
senderPublicKey, senderPrivateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatalf("failed to generate key pair: %s", err.Error())
}
_, receiverPublicKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("failed to generate key pair: %s", err.Error())
}
newTxOpts := MakeNewTxOpts(
senderPublicKey, senderPrivateKey, receiverPublicKey,
0.01, "", false)
newTxOptsJson, err := newTxOpts.ToJSON(EncodingRawUrl)
if err != nil {
t.Fatalf("failed to marshal JSON to tx: %s", err.Error())
}
newTxOptsFromJSON, err := NewTxOptsFromJSON(newTxOptsJson)
if err != nil {
t.Fatalf("failed to unmarshal tx from JSON: %s", err.Error())
}
if ok := newTxOptsFromJSON.ValidateBlockId(); !ok {
t.Fatalf("invalid block id")
}
if ok := newTxOptsFromJSON.ValidateHash(); !ok {
t.Fatalf("invalid hash. Got hash: %s", base64.RawURLEncoding.EncodeToString(newTxOptsFromJSON.Hash))
}
if ok := newTxOptsFromJSON.ValidateSignature(); !ok {
t.Fatalf("invalid signature")
}
}
func TestTx(t *testing.T) {
senderPublicKey, senderPrivateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatalf("failed to generate key pair: %s", err.Error())
}
_, receiverPublicKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("failed to generate key pair: %s", err.Error())
}
newTxOpts := MakeNewTxOpts(
senderPublicKey, senderPrivateKey, receiverPublicKey,
0.01, "", false)
tx, _ := newTxOpts.Process(false, time.Time{})
txJson, err := tx.ToJSON(EncodingRawUrl)
if err != nil {
t.Fatalf("failed to marshal JSON to tx: %s", err.Error())
}
txFromJson, err := TxFromJSON(txJson)
if err != nil {
t.Fatalf("failed to unmarshal tx from JSON: %s", err.Error())
}
if ok := txFromJson.ValidateBlockId(); !ok {
t.Fatalf("invalid block id")
}
if ok := txFromJson.ValidateHash(); !ok {
t.Fatalf("invalid hash. Got hash: %s", base64.RawURLEncoding.EncodeToString(txFromJson.Hash))
}
if ok := txFromJson.ValidateSignature(); !ok {
t.Fatalf("invalid signature")
}
}

7
common/version.go Normal file
View File

@ -0,0 +1,7 @@
package common
import "fmt"
var VersionNumber = []int{0, 1, 0}
var VersionNumberStr = fmt.Sprintf("v%d.%d.%d",
VersionNumber[0], VersionNumber[1], VersionNumber[2])

52
server/core/block.go Normal file
View File

@ -0,0 +1,52 @@
package core
import (
"crypto/sha512"
"fmt"
"mic-wallet/common"
"sync"
)
type Block struct {
ID int64
IgnoreSignatures map[string]struct{}
TXs map[string]*common.NewTxOpts
Lock *sync.RWMutex
PrevHash []byte
Hash []byte
}
func NewBlock(id int64, prevHash []byte) *Block {
return &Block{
ID: id,
IgnoreSignatures: make(map[string]struct{}),
TXs: make(map[string]*common.NewTxOpts),
Lock: &sync.RWMutex{},
PrevHash: prevHash,
}
}
func (b *Block) AddTx(tx *common.NewTxOpts) error {
b.Lock.Lock()
hashStr := tx.HashString()
sigStr := tx.SignatureString()
if _, ok := b.IgnoreSignatures[sigStr]; ok {
return fmt.Errorf("duplicate")
}
b.TXs[hashStr] = tx
b.Lock.Unlock()
return nil
}
func (b *Block) RemoveTx(hash string) {}
func (b *Block) CalcHash() []byte {
hash := sha512.New()
b.Lock.Lock()
hash.Write(b.PrevHash)
for _, tx := range b.TXs {
hash.Write(tx.Hash)
}
b.Hash = hash.Sum(nil)
return b.Hash
}

View File

@ -1,47 +0,0 @@
package core
import (
"context"
"mic-wallet/server/repository/entities"
"sync"
"time"
)
type BlockData struct {
Lock *sync.RWMutex
prevHash []byte
Transactions map[string]entities.NewTransactionOpts
}
// BlocksPipeline does block rotation, writes transaction into the block or block queue
// write blocks when they are finished.
type BlocksPipeline struct {
cooldown time.Duration
writeToBlock uint8
block1 *BlockData
block2 *BlockData
block3 *BlockData
}
func NewBlockPipeline(cooldown time.Duration) *BlocksPipeline {
return &BlocksPipeline{}
}
func (b *BlocksPipeline) loadPrevBlockHash(ctx context.Context) {
}
func (b *BlocksPipeline) rotate(ctx context.Context) error {
return nil
}
func (b *BlocksPipeline) NewTransaction(ctx context.Context, opts NewTransactionOpts) error {
return nil
}
func (b *BlocksPipeline) Run(ctx context.Context) error {
return nil
}

View File

@ -0,0 +1,84 @@
package core
import (
"context"
"github.com/matchsystems/werr"
"mic-wallet/server/repository"
"sync"
"time"
)
type CommitBlockFunc func(ctx context.Context, block *Block) error
type IBlocksCommiter interface {
// CommitBlock pushes blocks into the storage (db).
// Block must be not null
CommitBlock(ctx context.Context, block *Block)
}
type BlocksCommiter struct {
BlocksQueue []*Block
Lock *sync.RWMutex
CountLim int32
CountBusy int32
TaskDeadline time.Duration
ErrChan chan error
CommitBlockFunc CommitBlockFunc
}
func NewBlocksCommiter(repo *repository.Repository, errChan chan error, workersCountLim int) *BlocksCommiter {
return &BlocksCommiter{
BlocksQueue: make([]*Block, 0),
Lock: &sync.RWMutex{},
CountLim: 0,
CountBusy: 0,
TaskDeadline: 0,
ErrChan: make(chan error, workersCountLim),
CommitBlockFunc: func(ctx context.Context, block *Block) error {
return CommitBlock(ctx, repo, block)
},
}
}
func (bc *BlocksCommiter) execute(ctx context.Context, block *Block) {
if bc.TaskDeadline > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, bc.TaskDeadline)
defer cancel()
}
errChan := make(chan error)
go func() {
if err := bc.CommitBlockFunc(ctx, block); err != nil {
errChan <- err
}
}()
select {
case <-ctx.Done():
errChan <- werr.Wrapf(ctx.Err(), "ctx error while commiting block %d", block.ID)
case err := <-errChan:
bc.ErrChan <- werr.Wrapf(err, "error while commiting block %d", block.ID)
}
}
func (bc *BlocksCommiter) CommitBlock(ctx context.Context, block *Block) {
bc.Lock.Lock()
if bc.CountBusy >= bc.CountLim {
bc.BlocksQueue = append(bc.BlocksQueue, block)
return
}
bc.CountBusy++
bc.Lock.Unlock()
for {
bc.execute(ctx, block)
bc.Lock.Lock()
if len(bc.BlocksQueue) < 1 {
bc.CountBusy--
bc.Lock.Unlock()
return
}
block = bc.BlocksQueue[0]
bc.BlocksQueue = bc.BlocksQueue[1:]
bc.Lock.Unlock()
}
}

View File

@ -0,0 +1,56 @@
package core
import (
"context"
"errors"
"github.com/matchsystems/werr"
"mic-wallet/common"
"time"
)
type IBlocksCreator interface {
Run(ctx context.Context) error
WriteTx(opts *common.NewTxOpts) error
// GetTx Searches for tx in every block
GetTx(block int64, hash string) *common.NewTxOpts
GetTxs() []*common.NewTxOpts
}
type BlocksCreator struct {
blocks map[int64]*Block
commiter IBlocksCommiter
prevBlockHash []byte
}
func NewBlocksCreator(commiter IBlocksCommiter) *BlocksCreator {
return &BlocksCreator{
blocks: make(map[int64]*Block),
commiter: commiter,
}
}
func (bc *BlocksCreator) Run(ctx context.Context) error {
var blockID int64
for {
blockID = common.GetBlockId(time.Now())
if oldBlock, ok := bc.blocks[blockID-100]; ok { // Commit and delete old block
bc.prevBlockHash = oldBlock.CalcHash()
bc.commiter.CommitBlock(ctx, oldBlock)
delete(bc.blocks, blockID-100)
}
bc.blocks[blockID] = NewBlock(blockID, bc.prevBlockHash)
time.Sleep(time.Until(time.Unix(blockID+100, 0)))
}
}
func (bc *BlocksCreator) WriteTx(opts *common.NewTxOpts) error {
currentBlockID := common.GetBlockId(time.Now())
if currentBlockID < opts.BlockId {
return errors.New("block with such id wasn't created yet. " +
"Ensure that you generate block IDs in UTC timezone")
} else if currentBlockID > opts.BlockId {
return errors.New("block id is out of date")
}
return werr.Wrapf(bc.blocks[currentBlockID].AddTx(opts),
"error adding transaction to block %d", currentBlockID)
}

View File

@ -0,0 +1,10 @@
package core
import (
"context"
"mic-wallet/server/repository"
)
func CommitBlock(ctx context.Context, repo *repository.Repository, block *Block) error {
return nil
}

View File

@ -1,15 +0,0 @@
package core
import "time"
type NewTransactionOpts struct {
Hash []byte
BlockId int64
SenderPublicKey []byte
ReceiverPublicKey []byte
IsReward bool
Amount float64
Message string
Signature []byte
CreatedAt time.Time
}

View File

@ -1,31 +0,0 @@
package logic
import (
"bytes"
"errors"
"github.com/matchsystems/werr"
"math"
"mic-wallet/common"
"mic-wallet/server/repository/entities"
"time"
)
func ValidateNewTransaction(tx *entities.NewTransactionOpts, txHash []byte) error {
if bytes.Compare(tx.ReceiverPublicKey, tx.SenderPublicKey) == 0 {
return errors.New("cannot send to yourself")
}
valid, err := common.VerifyTransactionSignature(tx.SenderPublicKey, tx.Signature, txHash, tx.CreatedAt)
if err != nil {
return werr.Wrapf(err, "failed to verify signature")
}
if !valid {
return errors.New("invalid signature")
}
now := time.Now().UTC()
// Sent data has to be in UTC timezone
if time.Duration(math.Abs(float64(tx.CreatedAt.Sub(now)))) > time.Minute*1 {
return errors.New("invalid transaction created_at time")
}
return nil
}

View File

@ -3,24 +3,27 @@
SET TIME ZONE 'UTC';
CREATE TABLE blocks (
id BIGSERIAL PRIMARY KEY,
-- UNIX Timestamp
-- But not just any timestamp, some timestamp
id BIGINT PRIMARY KEY,
-- SHA512 sum of all hashes inside the block + prev_hash
-- 32 bytes long
hash BYTEA UNIQUE,
-- NULL only on the first block
prev_hash BYTEA UNIQUE,
started_at TIMESTAMP NOT NULL,
finished_at TIMESTAMP,
)
CREATE TABLE IF NOT EXISTS transactions (
-- SHA512 -- 32 bytes long
hash BYTEA PRIMARY KEY,
block_id BIGINT REFERENCES blocks(id) NOT NULL,
-- SHA512 -- 64 bytes long
hash BYTEA(64) PRIMARY KEY,
-- references block id
-- but may be added before the block itself
block_id BIGINT,
-- ED25519 32 bytes long
sender_public_key BYTEA NOT NULL,
receiver_public_key BYTEA NOT NULL,
-- ED25519 key -- 32 bytes long
sender_public_key BYTEA(32) NOT NULL,
receiver_public_key BYTEA(32) NOT NULL,
-- Rewards doesn't decrease sender's balance
-- but generate additional crypto inside the system.
@ -34,19 +37,18 @@ CREATE TABLE IF NOT EXISTS transactions (
-- When sign is decrypted with the sender's
-- public key we should get the created_at
-- time in unix format
signature BYTEA NOT NULL,
signature BYTEA(24) NOT NULL,
-- Time provided by the client
created_at TIMESTAMP,
created_at BIGINT NOT NULL,
created_at_ns BIGINT NOT NULL,
-- Time when transaction was added to the server
added_at TIMESTAMP SET DEFAULT CURRENT TIMESTAMP,
-- Prevent block duplicates
UNIQUE(sendrt_public_key, signature)
-- NOTE: Will be extended with smart contracts data
);
CREATE INDEX idx_blocks_hash ON blocks (hash);
CREATE INDEX idx_blocks_prev_hash ON prev_hash (prev_hash);
CREATE INDEX idx_blocks_prev_hash ON blocks (prev_hash);
CREATE INDEX idx_transactions_block_id ON transactions (block_id);
CREATE INDEX idx_transactions_sender_pk ON transactions (sender_public_key);
CREATE INDEX idx_transactions_receiver_pk ON transactions (receiver_public_key);

View File

@ -49,6 +49,7 @@ type (
Message string `db:"message"`
Signature []byte `db:"signature"`
CreatedAt time.Time `db:"created_at"`
CreatedAtNs int64 `db:"created_at_ns"`
AddedAt time.Time `db:"added_at"`
}
@ -63,5 +64,6 @@ type (
Message string `db:"message"`
Signature []byte `db:"signature"`
CreatedAt time.Time `db:"created_at"`
CreatedAtNs int64 `db:"created_at_ns"`
}
)

View File

@ -18,8 +18,8 @@ import (
)
func (s *Server) Setup(ctx context.Context) error {
// =============================
// Set up the transactions queue
// ================================
// Set up the transactions pipeline
// ===================
// Setting up database
@ -79,7 +79,7 @@ func (s *Server) Setup(ctx context.Context) error {
addr := fmt.Sprintf("%s:%d", s.Cfg.Addr, s.Cfg.HttpPort)
s.HttpListener, err = net.Listen("tcp", addr)
if err != nil {
return werr.Wrapf(err, "failed to listen http on address %s", addr)
return werr.Wrapf(err, "failed to listen http_api_entities on address %s", addr)
}
}
if s.Cfg.HttpsPort > 0 {