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

starting v2 #12

Merged
merged 11 commits into from
Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

# Change Log
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## v2.0.0-alpha

### changed
- Rebranded payman into tzpay
- project structure
- added tests
- uses go-tezos v2.5.2-alpha
- forges operations locally
- uses enviroment variables for configuration
- docker image

13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM golang:latest

# Set the Current Working Directory inside the container
WORKDIR $GOPATH/src/github.com/goat-systems/tzpay

# Copy everything from the current directory to the PWD (Present Working Directory) inside the container
COPY . .

# Download all the dependencies
RUN go get -d -v ./...

# Install the package
RUN go install -v ./...
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PROJECT_NAME := "payman"
PROJECT_NAME := "tzpay"
VERSION := "v2.0.0"
PKG := "github.com/goat-systems/$(PROJECT_NAME)"
PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/)
Expand Down Expand Up @@ -27,6 +27,9 @@ dep: ## Get the dependencies
@go get -u golang.org/x/lint/golint
@go get -u honnef.co/go/tools/...

build: ## Get the dependencies
@go build

clean: ## Remove previous build
@rm -f $(PROJECT_NAME)

Expand Down
431 changes: 261 additions & 170 deletions README.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion _config.yml

This file was deleted.

247 changes: 247 additions & 0 deletions cli/internal/baker/baker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
package baker

import (
"context"
"math/big"
"unicode"

gotezos "github.com/goat-systems/go-tezos/v2"
"github.com/goat-systems/tzpay/v2/cli/internal/enviroment"
"github.com/pkg/errors"
)

// DelegationEarning -
type DelegationEarning struct {
Delegation string
Fee *big.Int
GrossRewards *big.Int
NetRewards *big.Int
Share float64
}

// Baker is a tezos baker that can get payouts and execute them.
type Baker struct {
gt gotezos.IFace
}

type processDelegationsInput struct {
delegations *[]string
stakingBalance *big.Int
frozenBalanceRewards *gotezos.FrozenBalance
blockHash string
}

type processDelegationsOutput struct {
delegationEarning DelegationEarning
err error
}

type processDelegationInput struct {
delegation string
stakingBalance *big.Int
frozenBalanceRewards *gotezos.FrozenBalance
blockHash string
}

// Payout contains all needed information for a payout
type Payout struct {
DelegationEarnings DelegationEarnings `json:"delegaions"`
Cycle int `json:"cycle"`
FrozenBalance *big.Int `json:"rewards"`
StakingBalance *big.Int `json:"staking_balance"`
Delegate string `json:"delegate"`
}

// DelegationEarnings contains list of DelegationEarning and implements sort.
type DelegationEarnings []DelegationEarning

func (d DelegationEarnings) Len() int { return len(d) }
func (d DelegationEarnings) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}

func (d DelegationEarnings) Less(i, j int) bool {
iRunes := []rune(d[i].Delegation)
jRunes := []rune(d[j].Delegation)

max := len(iRunes)
if max > len(jRunes) {
max = len(jRunes)
}

for idx := 0; idx < max; idx++ {
ir := iRunes[idx]
jr := jRunes[idx]

lir := unicode.ToLower(ir)
ljr := unicode.ToLower(jr)

if lir != ljr {
return lir < ljr
}

if ir != jr {
return ir < jr
}
}

return false
}

// NewBaker returns a pointer to a new Baker
func NewBaker(gt gotezos.IFace) *Baker {
return &Baker{gt: gt}
}

// Payouts returns all payouts for a cycle
func (b *Baker) Payouts(ctx context.Context, cycle int) (*Payout, error) {
params := enviroment.GetEnviromentFromContext(ctx)
frozenBalanceRewards, err := b.gt.FrozenBalance(cycle, params.Delegate)
if err != nil {
return nil, errors.Wrapf(err, "failed to get delegation earnings for cycle %d", cycle)
}

delegations, err := b.gt.DelegatedContractsAtCycle(cycle, params.Delegate)
if err != nil {
return nil, errors.Wrapf(err, "failed to get delegation earnings for cycle %d", cycle)
}

networkCycle, err := b.gt.Cycle(cycle)
if err != nil {
return nil, errors.Wrapf(err, "failed to get delegation earnings for cycle %d", cycle)
}

stakingBalance, err := b.gt.StakingBalance(networkCycle.BlockHash, params.Delegate)
if err != nil {
return nil, errors.Wrapf(err, "failed to get delegation earnings for cycle %d", cycle)
}

out := b.proccessDelegations(ctx, &processDelegationsInput{
delegations: delegations,
stakingBalance: stakingBalance,
frozenBalanceRewards: frozenBalanceRewards,
blockHash: networkCycle.BlockHash,
})

payouts := Payout{
Delegate: params.Delegate,
StakingBalance: stakingBalance,
Cycle: cycle,
FrozenBalance: frozenBalanceRewards.Rewards.Big,
}
for _, delegation := range out {
if delegation.err != nil {
err = errors.Wrapf(delegation.err, "failed to get payout for delegation %s", delegation.delegationEarning.Delegation)
} else {
payouts.DelegationEarnings = append(payouts.DelegationEarnings, delegation.delegationEarning)
}
}

return &payouts, err
}

func (b *Baker) proccessDelegations(ctx context.Context, input *processDelegationsInput) []processDelegationsOutput {
numJobs := len(*input.delegations)
jobs := make(chan processDelegationInput, numJobs)
results := make(chan processDelegationsOutput, numJobs)

for i := 0; i < 50; i++ {
go b.proccessDelegationWorker(ctx, jobs, results)
}

for _, pd := range *input.delegations {
jobs <- processDelegationInput{
delegation: pd,
stakingBalance: input.stakingBalance,
frozenBalanceRewards: input.frozenBalanceRewards,
blockHash: input.blockHash,
}
}
close(jobs)

var out []processDelegationsOutput
for i := 1; i <= numJobs; i++ {
out = append(out, <-results)
}
close(results)

return out
}

func (b *Baker) proccessDelegationWorker(ctx context.Context, jobs <-chan processDelegationInput, results chan<- processDelegationsOutput) {
for j := range jobs {
d, err := b.processDelegation(ctx, &j)
if err != nil {
results <- processDelegationsOutput{
err: err,
}
} else {
results <- processDelegationsOutput{
delegationEarning: *d,
}
}
}
}

func (b *Baker) processDelegation(ctx context.Context, input *processDelegationInput) (*DelegationEarning, error) {
params := enviroment.GetEnviromentFromContext(ctx)
delegationEarning := &DelegationEarning{Delegation: input.delegation}
balance, err := b.gt.Balance(input.blockHash, input.delegation)
if err != nil {
return nil, errors.Wrapf(err, "failed to process delegation earnings for delegation %s", input.delegation)
}

delegationEarning.Share = float64(balance.Int64()) / float64(input.stakingBalance.Int64())
grossRewardsFloat := delegationEarning.Share * float64(input.frozenBalanceRewards.Rewards.Big.Int64())
feeFloat := grossRewardsFloat * params.BakersFee

delegationEarning.GrossRewards = big.NewInt(int64(grossRewardsFloat))
delegationEarning.Fee = big.NewInt(int64(feeFloat))
delegationEarning.NetRewards = big.NewInt(0).Sub(delegationEarning.GrossRewards, delegationEarning.Fee)

return delegationEarning, nil
}

// ForgePayout converts Payout into operation contents and forges them locally
func (b *Baker) ForgePayout(ctx context.Context, payout Payout) (string, error) {
base := enviroment.GetEnviromentFromContext(ctx)
head, err := b.gt.Head()
if err != nil {
return "", errors.Wrap(err, "failed to forge payout")
}

counter, err := b.gt.Counter(head.Hash, base.Wallet.Address)
if err != nil {
return "", errors.Wrap(err, "failed to forge payout")
}

transactions := b.constructPayoutContents(ctx, *counter, payout)

forge, err := gotezos.ForgeTransactionOperation(head.Hash, transactions...)
if err != nil {
return "", errors.Wrap(err, "failed to forge payout")
}

return *forge, nil
}

func (b *Baker) constructPayoutContents(ctx context.Context, counter int, payout Payout) []gotezos.ForgeTransactionOperationInput {
base := enviroment.GetEnviromentFromContext(ctx)
var contents []gotezos.ForgeTransactionOperationInput
for _, delegation := range payout.DelegationEarnings {
counter++
if delegation.NetRewards.Int64() >= int64(base.MinimumPayment) {
contents = append(contents, gotezos.ForgeTransactionOperationInput{
Source: base.Wallet.Address,
Destination: delegation.Delegation,
Amount: gotezos.Int{Big: delegation.NetRewards},
Fee: gotezos.Int{Big: big.NewInt(int64(base.NetworkFee))}, //TODO: expose NewInt function in GoTezos
GasLimit: gotezos.Int{Big: big.NewInt(int64(base.GasLimit))},
Counter: counter,
StorageLimit: gotezos.Int{Big: big.NewInt(int64(0))},
})
}
}

return contents
}
Loading