Skip to content

Commit

Permalink
tools: allow specification of RewardPoolBalance in genesis (#4643)
Browse files Browse the repository at this point in the history
  • Loading branch information
barnjamin authored Nov 1, 2022
1 parent 15efc02 commit a8f1b4a
Show file tree
Hide file tree
Showing 5 changed files with 746 additions and 28 deletions.
41 changes: 29 additions & 12 deletions gen/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,28 @@ func GenerateGenesisFiles(genesisData GenesisData, consensus config.ConsensusPro
return fmt.Errorf("couldn't make output directory '%s': %v", outDir, err.Error())
}

return generateGenesisFiles(outDir, proto, consensusParams, genesisData.NetworkName, genesisData.VersionModifier, allocation, genesisData.FirstPartKeyRound, genesisData.LastPartKeyRound, genesisData.PartKeyDilution, genesisData.FeeSink, genesisData.RewardsPool, genesisData.Comment, genesisData.DevMode, verboseOut)
return generateGenesisFiles(
proto, consensusParams, allocation, genesisData, outDir, verboseOut,
)
}

func generateGenesisFiles(outDir string, protoVersion protocol.ConsensusVersion, protoParams config.ConsensusParams, netName string, schemaVersionModifier string,
allocation []genesisAllocation, firstWalletValid uint64, lastWalletValid uint64, partKeyDilution uint64, feeSink, rewardsPool basics.Address, comment string, devmode bool, verboseOut io.Writer) (err error) {
func generateGenesisFiles(protoVersion protocol.ConsensusVersion, protoParams config.ConsensusParams, allocation []genesisAllocation, genData GenesisData, outDir string, verboseOut io.Writer) (err error) {

genesisAddrs := make(map[string]basics.Address)
records := make(map[string]basics.AccountData)
var (
netName = genData.NetworkName
schemaVersionModifier = genData.VersionModifier
firstWalletValid = genData.FirstPartKeyRound
lastWalletValid = genData.LastPartKeyRound
partKeyDilution = genData.PartKeyDilution
feeSink = genData.FeeSink
rewardsPool = genData.RewardsPool
devmode = genData.DevMode
rewardsBalance = genData.RewardsPoolBalance
comment = genData.Comment

genesisAddrs = make(map[string]basics.Address)
records = make(map[string]basics.AccountData)
)

if partKeyDilution == 0 {
partKeyDilution = protoParams.DefaultKeyDilution
Expand Down Expand Up @@ -326,24 +340,27 @@ func generateGenesisFiles(outDir string, protoVersion protocol.ConsensusVersion,
fmt.Fprintln(verboseOut, protoVersion, protoParams.MinBalance)
}

if rewardsBalance < protoParams.MinBalance {
// Needs to at least have min balance
rewardsBalance = protoParams.MinBalance
}

records["FeeSink"] = basics.AccountData{
Status: basics.NotParticipating,
MicroAlgos: basics.MicroAlgos{Raw: protoParams.MinBalance},
}

records["RewardsPool"] = basics.AccountData{
Status: basics.NotParticipating,
MicroAlgos: basics.MicroAlgos{Raw: defaultIncentivePoolBalanceAtInception},
MicroAlgos: basics.MicroAlgos{Raw: rewardsBalance},
}

// Add FeeSink and RewardsPool to allocation slice to be handled with other allocations.
sinkAcct := genesisAllocation{
Name: "FeeSink",
Stake: protoParams.MinBalance,
Online: basics.NotParticipating,
Name: "FeeSink",
}
poolAcct := genesisAllocation{
Name: "RewardsPool",
Stake: defaultIncentivePoolBalanceAtInception,
Online: basics.NotParticipating,
Name: "RewardsPool",
}

alloc2 := make([]genesisAllocation, 0, len(allocation)+2)
Expand Down
125 changes: 122 additions & 3 deletions gen/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,25 @@
package gen

import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/util/db"

"github.com/algorand/go-algorand/test/partitiontest"
"github.com/stretchr/testify/require"
"github.com/algorand/go-algorand/util/db"
)

func TestLoadMultiRootKeyConcurrent(t *testing.T) {
Expand Down Expand Up @@ -114,3 +120,116 @@ func TestGenesisRoundoff(t *testing.T) {
require.NoError(t, err)
require.True(t, strings.Contains(verbosity.String(), "roundoff"))
}

// `TestGenesisJsonCreation` defends against regressions to `genesis.json` generation by comparing a known, valid `genesis.json` against a version generated during test invocation.
//
// * For each `testCase`, there is a corresponding `genesis.json` in `gen/resources` representing the known, valid output.
// * When adding test cases, it's assumed folks peer review new artifacts in `gen/resources`.
// * Since _some_ `genesis.json` values are non-deterministic, the test replaces these values with static values to facilitate equality checks.
func TestGenesisJsonCreation(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

type testCase struct {
name string
gd GenesisData
}

// `base` is a canonical test confirming `devnet.json` generates the intended `genesis.json`.
base := func() testCase {
jsonBytes, err := os.ReadFile("devnet.json")
require.NoError(t, err)

gd := DefaultGenesis
err = json.Unmarshal(jsonBytes, &gd)
require.NoError(t, err)

return testCase{"base", gd}
}

// `balance` extends `base` to confirm overriding the rewards pool balance works.
balance := func() testCase {
gd := base().gd
gd.RewardsPoolBalance = 0 // Expect generated balance == MinBalance
return testCase{"balance", gd}
}

// `blotOutRandomValues` replaces random values with static values to support equality checks.
blotOutRandomValues := func(as []bookkeeping.GenesisAllocation) {
deterministicAddresses := []string{"FeeSink", "RewardsPool"}

isNondeterministicAddress := func(name string) bool {
for _, address := range deterministicAddresses {
if name == address {
return false
}
}
return true
}

for i := range as {
require.Len(t, as[i].State.VoteID, 32)
as[i].State.VoteID = crypto.OneTimeSignatureVerifier{}
require.Len(t, as[i].State.VoteID, 32)
as[i].State.SelectionID = crypto.VRFVerifier{}

if isNondeterministicAddress(as[i].Comment) {
require.Len(t, as[i].Address, 58)
as[i].Address = ""
}
}
}

saveGeneratedGenesisJSON := func(filename, artifactName string) {
src, err := os.Open(filename)
require.NoError(t, err)
defer src.Close()

dst, err := os.CreateTemp("", "*-"+artifactName)
require.NoError(t, err)
defer dst.Close()

_, err = io.Copy(dst, src)
require.NoError(t, err)

t.Log("generated genesis.json = " + dst.Name())
}

// Since `t.TempDir` deletes the generated dir, retain generated `genesis.json` on test failure.
saveOnFailure := func(result bool, generatedFilename, artifactName string) {
if !result {
saveGeneratedGenesisJSON(generatedFilename, artifactName)
t.FailNow()
}
}

for _, tc := range []testCase{
base(),
balance(),
} {
t.Run(fmt.Sprintf("name=%v", tc.name), func(t *testing.T) {
gd := tc.gd
gd.LastPartKeyRound = 10 // Ensure quick test execution by reducing rounds.

outDir := t.TempDir()
err := GenerateGenesisFiles(gd, config.Consensus, outDir, nil)
require.NoError(t, err)

artifactName := fmt.Sprintf("genesis-%v.json", tc.name)
generatedFilename := fmt.Sprintf("%v/genesis.json", outDir)
saveOnFailure := func(result bool) {
saveOnFailure(result, generatedFilename, artifactName)
}

roundtrip, err := bookkeeping.LoadGenesisFromFile(generatedFilename)
require.NoError(t, err)

expected, err := bookkeeping.LoadGenesisFromFile("resources/" + artifactName)
saveOnFailure(assert.NoError(t, err))

blotOutRandomValues(expected.Allocation)
blotOutRandomValues(roundtrip.Allocation)
saveOnFailure(assert.Equal(t, expected, roundtrip))
})
}
}
Loading

0 comments on commit a8f1b4a

Please sign in to comment.