Skip to content

Commit

Permalink
txsource: delegation workload
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrus committed Mar 4, 2020
1 parent 8079357 commit 2aac4c7
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 1 deletion.
1 change: 1 addition & 0 deletions .changelog/2752.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
txsource: delegation workload
220 changes: 220 additions & 0 deletions go/oasis-node/cmd/debug/txsource/workload/delegation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package workload

import (
"context"
"fmt"
"math/rand"
"time"

"google.golang.org/grpc"

"github.com/oasislabs/oasis-core/go/common/crypto/signature"
memorySigner "github.com/oasislabs/oasis-core/go/common/crypto/signature/signers/memory"
"github.com/oasislabs/oasis-core/go/common/logging"
consensus "github.com/oasislabs/oasis-core/go/consensus/api"
"github.com/oasislabs/oasis-core/go/consensus/api/transaction"
runtimeClient "github.com/oasislabs/oasis-core/go/runtime/client/api"
staking "github.com/oasislabs/oasis-core/go/staking/api"
)

const (
// NameDelegation is the name of the delegation workload.
NameDelegation = "delegation"

// TODO: get from genesis params.
delegationDebondingEpochPeriod = 1

delegationNumAccounts = 10
delegateAmount = 100
)

var delegationLogger = logging.GetLogger("cmd/txsource/workload/delegation")

type delegation struct{}

func (delegation) Run(gracefulExit context.Context, rng *rand.Rand, conn *grpc.ClientConn, cnsc consensus.ClientBackend, rtc runtimeClient.RuntimeClient, fundingAccount signature.Signer) error {
var err error
ctx := context.Background()

fac := memorySigner.NewFactory()
accounts := make([]struct {
signer signature.Signer
reckonedNonce uint64
delegatedTo signature.PublicKey
debondingUntilEpoch uint64
}, delegationNumAccounts)

for i := range accounts {
accounts[i].signer, err = fac.Generate(signature.SignerEntity, rng)
if err != nil {
return fmt.Errorf("memory signer factory Generate account %d: %w", i, err)
}

// Fund the account with delegation amount.
// Funds for fee's will be transferred before making transactions.
if err = transferFunds(ctx, delegationLogger, cnsc, fundingAccount, accounts[i].signer.Public(), delegateAmount); err != nil {
return fmt.Errorf("account funding failure: %w", err)
}
}

MAIN:
for {
// There are multiple loop branches so check for termination here.
select {
case <-time.After(1 * time.Second):
case <-gracefulExit.Done():
delegationLogger.Debug("time's up")
return nil
}

// Get current epoch.
epoch, err := cnsc.GetEpoch(ctx, consensus.HeightLatest)
if err != nil {
return fmt.Errorf("GetEpoch: %w", err)
}

var signedTx *transaction.SignedTransaction
var gas transaction.Gas
// TODO: also do 'ChangeComssionSchedule` transactions as part of this
// workflow?
switch rng.Intn(2) {
case 0:
delegationLogger.Debug("escrow tx flow")

// Select an account that has no active delegations nor debonding
// funds.
perm := rng.Perm(delegationNumAccounts)
fromPermIdx := -1
var empty signature.PublicKey
for i := range accounts {
if accounts[perm[i]].delegatedTo == empty && accounts[perm[i]].debondingUntilEpoch < uint64(epoch) {
fromPermIdx = i
break
}
}
if fromPermIdx == -1 {
delegationLogger.Debug("all accounts already delegating or debonding, skipping delegation")
continue MAIN
}

// Select an account to delegate to.
// XXX: we could also cover the self-escrow case.
toPermIdx := (fromPermIdx + 1) % delegationNumAccounts
// Update local state.
accounts[perm[fromPermIdx]].delegatedTo = accounts[perm[toPermIdx]].signer.Public()

// Create escrow tx.
escrow := &staking.Escrow{
Account: accounts[perm[fromPermIdx]].delegatedTo,
}
if err = escrow.Tokens.FromInt64(delegateAmount); err != nil {
return fmt.Errorf("escrow amount error: %w", err)
}

tx := staking.NewAddEscrowTx(accounts[perm[fromPermIdx]].reckonedNonce, &transaction.Fee{}, escrow)
accounts[perm[fromPermIdx]].reckonedNonce++
gas, err = cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{
Caller: accounts[perm[fromPermIdx]].signer.Public(),
Transaction: tx,
})
if err != nil {
return fmt.Errorf("failed to estimate gas: %w", err)
}

tx.Fee.Gas = gas
feeAmount := int64(gas) * gasPrice
if err = tx.Fee.Amount.FromInt64(feeAmount); err != nil {
return fmt.Errorf("fee amount from int64: %w", err)
}

// Fund account to cover Escrow fees.
// We only do one escrow per account at a time, so `delegateAmount`
// funds (that are Escrowed) should already be in the balance.
fundAmount := int64(gas) * gasPrice // transaction costs
if err = transferFunds(ctx, delegationLogger, cnsc, fundingAccount, accounts[perm[fromPermIdx]].signer.Public(), fundAmount); err != nil {
return fmt.Errorf("account funding failure: %w", err)
}

// Sign transaction.
signedTx, err = transaction.Sign(accounts[perm[fromPermIdx]].signer, tx)
if err != nil {
return fmt.Errorf("transaction.Sign: %w", err)
}
delegationLogger.Debug("submitting escrow",
"from", accounts[perm[fromPermIdx]].signer.Public(),
"to", accounts[perm[fromPermIdx]].delegatedTo,
)
case 1:
delegationLogger.Debug("reclaim escrow tx")

// Select an account that has active delegation.
perm := rng.Perm(delegationNumAccounts)
fromPermIdx := -1
var empty signature.PublicKey
for i := range accounts {
if accounts[perm[i]].delegatedTo != empty {
fromPermIdx = i
break
}
}
if fromPermIdx == -1 {
delegationLogger.Debug("no accounts delegating, skipping reclaim")
continue MAIN
}

// Create ReclaimEscrow tx.
reclaim := &staking.ReclaimEscrow{
Account: accounts[perm[fromPermIdx]].delegatedTo,
}
if err = reclaim.Shares.FromInt64(delegateAmount); err != nil {
return fmt.Errorf("reclaim escrow amount error: %w", err)
}

tx := staking.NewReclaimEscrowTx(accounts[perm[fromPermIdx]].reckonedNonce, &transaction.Fee{}, reclaim)
accounts[perm[fromPermIdx]].reckonedNonce++
gas, err = cnsc.EstimateGas(ctx, &consensus.EstimateGasRequest{
Caller: accounts[perm[fromPermIdx]].signer.Public(),
Transaction: tx,
})
if err != nil {
return fmt.Errorf("failed to estimate gas: %w", err)
}

tx.Fee.Gas = gas
feeAmount := int64(gas) * gasPrice
if err = tx.Fee.Amount.FromInt64(feeAmount); err != nil {
return fmt.Errorf("fee amount from int64: %w", err)
}

// Fund account to cover reclaim escrow fees.
fundAmount := int64(gas) * gasPrice // transaction costs
if err = transferFunds(ctx, delegationLogger, cnsc, fundingAccount, accounts[perm[fromPermIdx]].signer.Public(), fundAmount); err != nil {
return fmt.Errorf("account funding failure: %w", err)
}

signedTx, err = transaction.Sign(accounts[perm[fromPermIdx]].signer, tx)
if err != nil {
return fmt.Errorf("transaction.Sign: %w", err)
}

delegationLogger.Debug("submitting reclaim escrow",
"from", accounts[perm[fromPermIdx]].delegatedTo,
"account", accounts[perm[fromPermIdx]].signer.Public(),
)

// Update local state.
accounts[perm[fromPermIdx]].delegatedTo = empty
// Add +1 to cover the case when epoch transition has/will happen
// before the transaction is executed.
accounts[perm[fromPermIdx]].debondingUntilEpoch = uint64(epoch) + delegationDebondingEpochPeriod + 1

default:
return fmt.Errorf("unimplemented delegation path")
}

// Submit transaction.
if err = cnsc.SubmitTx(ctx, signedTx); err != nil {
return fmt.Errorf("cnsc.SubmitTx: %w", err)
}
}
}
2 changes: 1 addition & 1 deletion go/oasis-node/cmd/debug/txsource/workload/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,10 @@ func (r *registration) Run(gracefulExit context.Context, rng *rand.Rand, conn *g
)

select {
case <-time.After(1 * time.Second):
case <-gracefulExit.Done():
registryLogger.Debug("time's up")
return nil
default:
}
}
}
1 change: 1 addition & 0 deletions go/oasis-node/cmd/debug/txsource/workload/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,5 @@ var ByName = map[string]Workload{
NameOversized: oversized{},
NameRegistration: &registration{},
NameParallel: parallel{},
NameDelegation: delegation{},
}
2 changes: 2 additions & 0 deletions go/oasis-test-runner/scenario/e2e/txsource.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var TxSourceMultiShort scenario.Scenario = &txSourceImpl{
workload.NameOversized,
workload.NameRegistration,
workload.NameParallel,
workload.NameDelegation,
},
timeLimit: timeLimitShort,
livenessCheckInterval: livenessCheckInterval,
Expand All @@ -52,6 +53,7 @@ var TxSourceMulti scenario.Scenario = &txSourceImpl{
workload.NameOversized,
workload.NameRegistration,
workload.NameParallel,
workload.NameDelegation,
},
timeLimit: timeLimitLong,
nodeRestartInterval: nodeRestartIntervalLong,
Expand Down
1 change: 1 addition & 0 deletions tests/fixture-data/txsource/staking-genesis.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"params": {
"debonding_interval": 2,
"gas_costs": {
"transfer": 10,
"burn": 10,
Expand Down

0 comments on commit 2aac4c7

Please sign in to comment.