This commit is contained in:
StephenButtolph 2020-03-30 22:06:29 -04:00
commit 4d34dc042d
21 changed files with 973 additions and 664 deletions

View File

@ -93,6 +93,7 @@ type manager struct {
// That is, [chainID].String() is an alias for the chain, too
ids.Aliaser
stakingEnabled bool // True iff the network has staking enabled
log logging.Logger
logFactory logging.Factory
vmManager vms.Manager // Manage mappings from vm ID --> vm
@ -122,6 +123,7 @@ type manager struct {
// <validators> validate this chain
// TODO: Make this function take less arguments
func New(
stakingEnabled bool,
log logging.Logger,
logFactory logging.Factory,
vmManager vms.Manager,
@ -146,6 +148,7 @@ func New(
router.Initialize(log, &timeoutManager)
m := &manager{
stakingEnabled: stakingEnabled,
log: log,
logFactory: logFactory,
vmManager: vmManager,
@ -261,7 +264,13 @@ func (m *manager) ForceCreateChain(chain ChainParameters) {
}
// The validators of this blockchain
validators, ok := m.validators.GetValidatorSet(ids.Empty) // TODO: Change argument to chain.SubnetID
var validators validators.Set // Validators validating this blockchain
var ok bool
if m.stakingEnabled {
validators, ok = m.validators.GetValidatorSet(chain.SubnetID)
} else { // Staking is disabled. Every peer validates every subnet.
validators, ok = m.validators.GetValidatorSet(ids.Empty) // ids.Empty is the default subnet ID. TODO: Move to const package so we can use it here.
}
if !ok {
m.log.Error("couldn't get validator set of subnet with ID %s. The subnet may not exist", chain.SubnetID)
return
@ -358,7 +367,7 @@ func (m *manager) createAvalancheChain(
msgChan := make(chan common.Message, defaultChannelSize)
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, fxs); err != nil {
return err
return fmt.Errorf("error during vm's Initialize: %w", err)
}
// Handles serialization/deserialization of vertices and also the

37
chains/mock_manager.go Normal file
View File

@ -0,0 +1,37 @@
package chains
import (
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/networking/router"
)
// MockManager implements Manager but does nothing. Always returns nil error.
// To be used only in tests (namely in package platformvm)
type MockManager struct{}
// Router ...
func (mm MockManager) Router() router.Router { return nil }
// CreateChain ...
func (mm MockManager) CreateChain(ChainParameters) {}
// ForceCreateChain ...
func (mm MockManager) ForceCreateChain(ChainParameters) {}
// AddRegistrant ...
func (mm MockManager) AddRegistrant(Registrant) {}
// Lookup ...
func (mm MockManager) Lookup(string) (ids.ID, error) { return ids.ID{}, nil }
// LookupVM ...
func (mm MockManager) LookupVM(string) (ids.ID, error) { return ids.ID{}, nil }
// Aliases ...
func (mm MockManager) Aliases(ids.ID) []string { return nil }
// Alias ...
func (mm MockManager) Alias(ids.ID, string) error { return nil }
// Shutdown ...
func (mm MockManager) Shutdown() {}

View File

@ -3,16 +3,25 @@
package genesis
// TODO: Move this to a separate repo and leave only a byte array
import (
"errors"
"fmt"
"math"
"math/big"
"regexp"
"strconv"
"strings"
"time"
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/go-ethereum/common"
"github.com/ava-labs/go-ethereum/params"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/units"
"github.com/ava-labs/gecko/vms/avm"
"github.com/ava-labs/gecko/vms/components/codec"
"github.com/ava-labs/gecko/vms/evm"
@ -149,9 +158,10 @@ func Aliases(networkID uint32) (generalAliases map[string][]string, chainAliases
spdagvm.ID.Key(): []string{"spdag"},
spchainvm.ID.Key(): []string{"spchain"},
timestampvm.ID.Key(): []string{"timestamp"},
secp256k1fx.ID.Key(): []string{"secp256k1fx"},
}
genesisBytes := Genesis(networkID)
genesisBytes, _ := Genesis(networkID)
genesis := &platformvm.Genesis{} // TODO let's not re-create genesis to do aliasing
platformvm.Codec.Unmarshal(genesisBytes, genesis) // TODO check for error
genesis.Initialize()
@ -182,328 +192,205 @@ func Aliases(networkID uint32) (generalAliases map[string][]string, chainAliases
// Since the Platform Chain causes the creation of all other
// chains, this function returns the genesis data of the entire network.
// The ID of the new network is [networkID].
func Genesis(networkID uint32) []byte {
if networkID != LocalID {
panic("unknown network ID provided")
func Genesis(networkID uint32) ([]byte, error) {
// Specify the genesis state of the AVM
avmArgs := avm.BuildGenesisArgs{}
{
holders := []interface{}(nil)
for _, addr := range Addresses {
holders = append(holders, avm.Holder{
Amount: json.Uint64(45 * units.MegaAva),
Address: addr,
})
}
avmArgs.GenesisData = map[string]avm.AssetDefinition{
// The AVM starts out with one asset, $AVA
"AVA": avm.AssetDefinition{
Name: "AVA",
Symbol: "AVA",
Denomination: 9,
InitialState: map[string][]interface{}{
"fixedCap": holders,
},
},
}
}
avmReply := avm.BuildGenesisReply{}
avmSS := avm.StaticService{}
err := avmSS.BuildGenesis(nil, &avmArgs, &avmReply)
if err != nil {
panic(err)
}
return []byte{
0x00, 0x00, 0x00, 0x01, 0x3c, 0xb7, 0xd3, 0x84,
0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1,
0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x0b, 0xde, 0x31, 0xb4, 0xd8, 0xb2, 0x29, 0x91,
0xd5, 0x1a, 0xa6, 0xaa, 0x1f, 0xc7, 0x33, 0xf2,
0x3a, 0x85, 0x1a, 0x8c, 0x94, 0x00, 0x00, 0x12,
0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x5d, 0xbb, 0x75, 0x80, 0x00, 0x00, 0x00,
0x00, 0x5f, 0x9c, 0xa9, 0x00, 0x00, 0x00, 0x30,
0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xaa, 0x18,
0xd3, 0x99, 0x1c, 0xf6, 0x37, 0xaa, 0x6c, 0x16,
0x2f, 0x5e, 0x95, 0xcf, 0x16, 0x3f, 0x69, 0xcd,
0x82, 0x91, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbb,
0x75, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9c,
0xa9, 0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb7,
0xd3, 0x84, 0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd,
0x09, 0xf1, 0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1,
0xb2, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b, 0xe9, 0x09, 0x4f, 0x73, 0x69,
0x80, 0x02, 0xfd, 0x52, 0xc9, 0x08, 0x19, 0xb4,
0x57, 0xb9, 0xfb, 0xc8, 0x66, 0xab, 0x80, 0x00,
0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x5d, 0xbb, 0x75, 0x80, 0x00,
0x00, 0x00, 0x00, 0x5f, 0x9c, 0xa9, 0x00, 0x00,
0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0xb7, 0xd3, 0x84, 0x2e,
0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe,
0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
0x47, 0x9f, 0x66, 0xc8, 0xbe, 0x89, 0x58, 0x30,
0x54, 0x7e, 0x70, 0xb4, 0xb2, 0x98, 0xca, 0xfd,
0x43, 0x3d, 0xba, 0x6e, 0x00, 0x00, 0x12, 0x30,
0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5d, 0xbb, 0x75, 0x80, 0x00, 0x00, 0x00, 0x00,
0x5f, 0x9c, 0xa9, 0x00, 0x00, 0x00, 0x30, 0x39,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee, 0x6a,
0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f, 0x68,
0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0b, 0xf2, 0x9b, 0xce,
0x5f, 0x34, 0xa7, 0x43, 0x01, 0xeb, 0x0d, 0xe7,
0x16, 0xd5, 0x19, 0x4e, 0x4a, 0x4a, 0xea, 0x5d,
0x7a, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbb, 0x75,
0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9c, 0xa9,
0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb7, 0xd3,
0x84, 0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09,
0xf1, 0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2,
0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x41, 0x56, 0x4d, 0x61, 0x76, 0x6d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x73,
0x65, 0x63, 0x70, 0x32, 0x35, 0x36, 0x6b, 0x31,
0x66, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x01, 0x00,
0x03, 0x41, 0x56, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x41, 0x56, 0x41, 0x00, 0x03, 0x41,
0x56, 0x41, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x06, 0x00, 0x9f, 0xdf, 0x42, 0xf6,
0xe4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e,
0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe,
0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x41, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x65, 0x76,
0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0xc9, 0x7b, 0x22,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x3a,
0x7b, 0x22, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49,
0x64, 0x22, 0x3a, 0x34, 0x33, 0x31, 0x31, 0x30,
0x2c, 0x22, 0x68, 0x6f, 0x6d, 0x65, 0x73, 0x74,
0x65, 0x61, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x22, 0x3a, 0x30, 0x2c, 0x22, 0x64, 0x61, 0x6f,
0x46, 0x6f, 0x72, 0x6b, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x64, 0x61,
0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x75, 0x70,
0x70, 0x6f, 0x72, 0x74, 0x22, 0x3a, 0x74, 0x72,
0x75, 0x65, 0x2c, 0x22, 0x65, 0x69, 0x70, 0x31,
0x35, 0x30, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22,
0x3a, 0x30, 0x2c, 0x22, 0x65, 0x69, 0x70, 0x31,
0x35, 0x30, 0x48, 0x61, 0x73, 0x68, 0x22, 0x3a,
0x22, 0x30, 0x78, 0x32, 0x30, 0x38, 0x36, 0x37,
0x39, 0x39, 0x61, 0x65, 0x65, 0x62, 0x65, 0x61,
0x65, 0x31, 0x33, 0x35, 0x63, 0x32, 0x34, 0x36,
0x63, 0x36, 0x35, 0x30, 0x32, 0x31, 0x63, 0x38,
0x32, 0x62, 0x34, 0x65, 0x31, 0x35, 0x61, 0x32,
0x63, 0x34, 0x35, 0x31, 0x33, 0x34, 0x30, 0x39,
0x39, 0x33, 0x61, 0x61, 0x63, 0x66, 0x64, 0x32,
0x37, 0x35, 0x31, 0x38, 0x38, 0x36, 0x35, 0x31,
0x34, 0x66, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x69,
0x70, 0x31, 0x35, 0x35, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x65, 0x69,
0x70, 0x31, 0x35, 0x38, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x62, 0x79,
0x7a, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x3a, 0x30, 0x2c,
0x22, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e,
0x74, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x65, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x3a, 0x30, 0x2c,
0x22, 0x70, 0x65, 0x74, 0x65, 0x72, 0x73, 0x62,
0x75, 0x72, 0x67, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x6e, 0x6f,
0x6e, 0x63, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x78,
0x30, 0x22, 0x2c, 0x22, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x78,
0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x22,
0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x22, 0x2c,
0x22, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69,
0x74, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x35, 0x66,
0x35, 0x65, 0x31, 0x30, 0x30, 0x22, 0x2c, 0x22,
0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c,
0x74, 0x79, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x22, 0x2c, 0x22, 0x6d, 0x69, 0x78, 0x48, 0x61,
0x73, 0x68, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22,
0x2c, 0x22, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61,
0x73, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22,
0x2c, 0x22, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x22,
0x3a, 0x7b, 0x22, 0x37, 0x35, 0x31, 0x61, 0x30,
0x62, 0x39, 0x36, 0x65, 0x31, 0x30, 0x34, 0x32,
0x62, 0x65, 0x65, 0x37, 0x38, 0x39, 0x34, 0x35,
0x32, 0x65, 0x63, 0x62, 0x32, 0x30, 0x32, 0x35,
0x33, 0x66, 0x62, 0x61, 0x34, 0x30, 0x64, 0x62,
0x65, 0x38, 0x35, 0x22, 0x3a, 0x7b, 0x22, 0x62,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a,
0x22, 0x30, 0x78, 0x33, 0x33, 0x62, 0x32, 0x65,
0x33, 0x63, 0x39, 0x66, 0x64, 0x30, 0x38, 0x30,
0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x6e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x67, 0x61,
0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68,
0x22, 0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x7d, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x53, 0x69,
0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x41, 0x47,
0x20, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74,
0x73, 0x73, 0x70, 0x64, 0x61, 0x67, 0x76, 0x6d,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15,
0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43,
0x68, 0x61, 0x69, 0x6e, 0x20, 0x50, 0x61, 0x79,
0x6d, 0x65, 0x6e, 0x74, 0x73, 0x73, 0x70, 0x63,
0x68, 0x61, 0x69, 0x6e, 0x76, 0x6d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x17, 0x53, 0x69, 0x6d, 0x70,
0x6c, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5d, 0xbb, 0x75, 0x80,
// Specify the genesis state of Athereum (the built-in instance of the EVM)
evmBalance, success := new(big.Int).SetString("33b2e3c9fd0804000000000", 16)
if success != true {
return nil, errors.New("problem creating evm genesis state")
}
evmArgs := core.Genesis{
Config: &params.ChainConfig{
ChainID: big.NewInt(43110),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
},
Nonce: 0,
Timestamp: 0,
ExtraData: []byte{0},
GasLimit: 100000000,
Difficulty: big.NewInt(0),
Mixhash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
Coinbase: common.HexToAddress("0x0000000000000000000000000000000000000000"),
Alloc: core.GenesisAlloc{
common.HexToAddress(evm.GenesisTestAddr): core.GenesisAccount{
Balance: evmBalance,
},
},
Number: 0,
GasUsed: 0,
ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
}
evmSS := evm.StaticService{}
evmReply, err := evmSS.BuildGenesis(nil, &evmArgs)
if err != nil {
return nil, err
}
// Specify the genesis state of the simple payments DAG
spdagvmArgs := spdagvm.BuildGenesisArgs{}
for _, addr := range ParsedAddresses {
spdagvmArgs.Outputs = append(spdagvmArgs.Outputs,
spdagvm.APIOutput{
Amount: json.Uint64(20 * units.KiloAva),
Threshold: 1,
Addresses: []ids.ShortID{addr},
},
)
}
spdagvmReply := spdagvm.BuildGenesisReply{}
spdagvmSS := spdagvm.StaticService{}
if err := spdagvmSS.BuildGenesis(nil, &spdagvmArgs, &spdagvmReply); err != nil {
return nil, fmt.Errorf("problem creating simple payments DAG: %w", err)
}
// Specify the genesis state of the simple payments chain
spchainvmArgs := spchainvm.BuildGenesisArgs{}
for _, addr := range ParsedAddresses {
spchainvmArgs.Accounts = append(spchainvmArgs.Accounts,
spchainvm.APIAccount{
Address: addr,
Balance: json.Uint64(20 * units.KiloAva),
},
)
}
spchainvmReply := spchainvm.BuildGenesisReply{}
spchainvmSS := spchainvm.StaticService{}
if err := spchainvmSS.BuildGenesis(nil, &spchainvmArgs, &spchainvmReply); err != nil {
return nil, fmt.Errorf("problem creating simple payments chain: %w", err)
}
// Specify the initial state of the Platform Chain
platformvmArgs := platformvm.BuildGenesisArgs{
NetworkID: json.Uint32(networkID),
}
for _, addr := range ParsedAddresses {
platformvmArgs.Accounts = append(platformvmArgs.Accounts,
platformvm.APIAccount{
Address: addr,
Balance: json.Uint64(20 * units.KiloAva),
},
)
}
genesisTime := time.Date(
/*year=*/ 2019,
/*month=*/ time.November,
/*day=*/ 1,
/*hour=*/ 0,
/*minute=*/ 0,
/*second=*/ 0,
/*nano-second=*/ 0,
/*location=*/ time.UTC,
)
stakingDuration := 365 * 24 * time.Hour // ~ 1 year
endStakingTime := genesisTime.Add(stakingDuration)
for i, validatorID := range ParsedStakerIDs {
weight := json.Uint64(20 * units.KiloAva)
platformvmArgs.Validators = append(platformvmArgs.Validators,
platformvm.APIDefaultSubnetValidator{
APIValidator: platformvm.APIValidator{
StartTime: json.Uint64(genesisTime.Unix()),
EndTime: json.Uint64(endStakingTime.Unix()),
Weight: &weight,
ID: validatorID,
},
Destination: ParsedAddresses[i%len(ParsedAddresses)],
},
)
}
// Specify the chains that exist upon this network's creation
platformvmArgs.Chains = []platformvm.APIChain{
platformvm.APIChain{
GenesisData: avmReply.Bytes,
SubnetID: platformvm.DefaultSubnetID,
VMID: avm.ID,
FxIDs: []ids.ID{
secp256k1fx.ID,
},
Name: "X-Chain",
},
platformvm.APIChain{
GenesisData: evmReply,
SubnetID: platformvm.DefaultSubnetID,
VMID: evm.ID,
Name: "C-Chain",
},
platformvm.APIChain{
GenesisData: spdagvmReply.Bytes,
SubnetID: platformvm.DefaultSubnetID,
VMID: spdagvm.ID,
Name: "Simple DAG Payments",
},
platformvm.APIChain{
GenesisData: spchainvmReply.Bytes,
SubnetID: platformvm.DefaultSubnetID,
VMID: spchainvm.ID,
Name: "Simple Chain Payments",
},
platformvm.APIChain{
GenesisData: formatting.CB58{Bytes: []byte{}}, // There is no genesis data
SubnetID: platformvm.DefaultSubnetID,
VMID: timestampvm.ID,
Name: "Simple Timestamp Server",
},
}
platformvmArgs.Time = json.Uint64(genesisTime.Unix())
platformvmReply := platformvm.BuildGenesisReply{}
platformvmSS := platformvm.StaticService{}
if err := platformvmSS.BuildGenesis(nil, &platformvmArgs, &platformvmReply); err != nil {
return nil, fmt.Errorf("problem while building platform chain's genesis state: %w", err)
}
return platformvmReply.Bytes.Bytes, nil
}
// VMGenesis ...
func VMGenesis(networkID uint32, vmID ids.ID) *platformvm.CreateChainTx {
genesisBytes := Genesis(networkID)
genesisBytes, _ := Genesis(networkID)
genesis := platformvm.Genesis{}
platformvm.Codec.Unmarshal(genesisBytes, &genesis)
if err := genesis.Initialize(); err != nil {

View File

@ -106,7 +106,7 @@ func TestAliases(t *testing.T) {
}
func TestGenesis(t *testing.T) {
genesisBytes := Genesis(LocalID)
genesisBytes, _ := Genesis(LocalID)
genesis := platformvm.Genesis{}
if err := platformvm.Codec.Unmarshal(genesisBytes, &genesis); err != nil {
t.Fatal(err)

View File

@ -120,10 +120,6 @@ func init() {
networkID, err := genesis.NetworkID(*networkName)
errs.Add(err)
if networkID != genesis.LocalID {
errs.Add(fmt.Errorf("the only supported networkID is: %s", genesis.LocalName))
}
Config.NetworkID = networkID
// DB:

View File

@ -354,8 +354,13 @@ func (n *Node) initChains() {
n.Log.Info("initializing chains")
vdrs := n.vdrs
// If staking is disabled, ignore updates to Subnets' validator sets
// Instead of updating node's validator manager, platform chain makes changes
// to its own local validator manager (which isn't used for sampling)
if !n.Config.EnableStaking {
defaultSubnetValidators := validators.NewSet()
defaultSubnetValidators.Add(validators.NewValidator(n.ID, 1))
vdrs = validators.NewManager()
vdrs.PutValidatorSet(platformvm.DefaultSubnetID, defaultSubnetValidators)
}
@ -363,10 +368,11 @@ func (n *Node) initChains() {
n.vmManager.RegisterVMFactory(
/*vmID=*/ platformvm.ID,
/*vmFactory=*/ &platformvm.Factory{
ChainManager: n.chainManager,
Validators: vdrs,
AVA: genesis.AVAAssetID(n.Config.NetworkID),
AVM: genesis.VMGenesis(n.Config.NetworkID, avm.ID).ID(),
ChainManager: n.chainManager,
Validators: vdrs,
StakingEnabled: n.Config.EnableStaking,
AVA: genesis.AVAAssetID(n.Config.NetworkID),
AVM: genesis.VMGenesis(n.Config.NetworkID, avm.ID).ID(),
},
)
@ -375,11 +381,12 @@ func (n *Node) initChains() {
beacons.Add(validators.NewValidator(peer.ID, 1))
}
genesisBytes := genesis.Genesis(n.Config.NetworkID)
genesisBytes, _ := genesis.Genesis(n.Config.NetworkID)
// Create the Platform Chain
n.chainManager.ForceCreateChain(chains.ChainParameters{
ID: ids.Empty,
SubnetID: platformvm.DefaultSubnetID,
GenesisData: genesisBytes, // Specifies other chains to create
VMAlias: platformvm.ID.String(),
CustomBeacons: beacons,
@ -409,6 +416,7 @@ func (n *Node) initAPIServer() {
// Assumes n.DB, n.vdrs all initialized (non-nil)
func (n *Node) initChainManager() {
n.chainManager = chains.New(
n.Config.EnableStaking,
n.Log,
n.LogFactory,
n.vmManager,

View File

@ -7,6 +7,8 @@ import (
"fmt"
"time"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/versiondb"
)
@ -86,7 +88,7 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
return nil, nil, nil, nil, err
}
current, pending, err := tx.vm.calculateValidators(db, tx.Timestamp(), DefaultSubnetID)
current, pending, _, _, err := tx.vm.calculateValidators(db, tx.Timestamp(), DefaultSubnetID)
if err != nil {
return nil, nil, nil, nil, err
}
@ -98,29 +100,35 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
return nil, nil, nil, nil, err
}
// For each subnet, calculate what current and pending validator sets should be
// For each Subnet, calculate what current and pending validator sets should be
// given new timestamp
// Key: Subnet ID
// Value: IDs of validators that will have started validating this Subnet when
// timestamp is advanced to tx.Timestamp()
startedValidating := make(map[ids.ID]ids.ShortSet, 0)
subnets, err := tx.vm.getSubnets(db)
if err != nil {
return nil, nil, nil, nil, err
}
for _, subnet := range subnets {
current, pending, err := tx.vm.calculateValidators(db, tx.Timestamp(), subnet.id)
current, pending, started, _, err := tx.vm.calculateValidators(db, tx.Timestamp(), subnet.id)
if err != nil {
return nil, nil, nil, nil, err
}
if err := tx.vm.putCurrentValidators(onCommitDB, current, subnet.id); err != nil {
return nil, nil, nil, nil, err
}
if err := tx.vm.putPendingValidators(onCommitDB, pending, subnet.id); err != nil {
return nil, nil, nil, nil, err
}
startedValidating[subnet.ID()] = started
}
// If this block is committed, update the validator sets
// onAbortDB or onCommitDB should commit (flush to vm.DB) before this is called
updateValidators := func() {
onCommitFunc := func() {
// For each Subnet, update the node's validator manager to reflect current Subnet membership
subnets, err := tx.vm.getSubnets(tx.vm.DB)
if err != nil {
tx.vm.Ctx.Log.Error("failed to get subnets: %s", err)
@ -128,18 +136,35 @@ func (tx *advanceTimeTx) SemanticVerify(db database.Database) (*versiondb.Databa
}
for _, subnet := range subnets {
if err := tx.vm.updateValidators(subnet.id); err != nil {
tx.vm.Ctx.Log.Debug("failed to update validators on the default subnet: %s", err)
tx.vm.Ctx.Log.Debug("failed to update Subnet %s: %s", subnet.id, err)
}
}
if err := tx.vm.updateValidators(DefaultSubnetID); err != nil {
tx.vm.Ctx.Log.Fatal("failed to update validators on the default subnet: %s", err)
tx.vm.Ctx.Log.Fatal("failed to update Default Subnet: %s", err)
}
// If this node started validating a Subnet, create the blockchains that the Subnet validates
chains, err := tx.vm.getChains(tx.vm.DB) // all blockchains
if err != nil {
tx.vm.Ctx.Log.Error("couldn't get blockchains: %s", err)
return
}
for subnetID, validatorIDs := range startedValidating {
if !validatorIDs.Contains(tx.vm.Ctx.NodeID) {
continue
}
for _, chain := range chains {
if chain.SubnetID.Equals(subnetID) {
tx.vm.createChain(chain)
}
}
}
}
// Specify what the state of the chain will be if this proposal is aborted
onAbortDB := versiondb.New(db) // state doesn't change
return onCommitDB, onAbortDB, updateValidators, nil, nil
return onCommitDB, onAbortDB, onCommitFunc, nil, nil
}
// InitiallyPrefersCommit returns true if the proposed time isn't after the

View File

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"github.com/ava-labs/gecko/chains"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto"
@ -15,8 +14,9 @@ import (
)
var (
errInvalidVMID = errors.New("invalid VM ID")
errFxIDsNotSortedAndUnique = errors.New("feature extensions IDs must be sorted and unique")
errInvalidVMID = errors.New("invalid VM ID")
errFxIDsNotSortedAndUnique = errors.New("feature extensions IDs must be sorted and unique")
errControlSigsNotSortedAndUnique = errors.New("control signatures must be sorted and unique")
)
// UnsignedCreateChainTx is an unsigned CreateChainTx
@ -24,6 +24,9 @@ type UnsignedCreateChainTx struct {
// ID of the network this blockchain exists on
NetworkID uint32 `serialize:"true"`
// ID of the Subnet that validates this blockchain
SubnetID ids.ID `serialize:"true"`
// Next unused nonce of account paying the transaction fee for this transaction.
// Currently unused, as there are no tx fees.
Nonce uint64 `serialize:"true"`
@ -37,7 +40,7 @@ type UnsignedCreateChainTx struct {
// IDs of the feature extensions running on the new chain
FxIDs []ids.ID `serialize:"true"`
// Byte representation of state of the new chain
// Byte representation of genesis state of the new chain
GenesisData []byte `serialize:"true"`
}
@ -45,11 +48,19 @@ type UnsignedCreateChainTx struct {
type CreateChainTx struct {
UnsignedCreateChainTx `serialize:"true"`
Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
// Address of the account that provides the transaction fee
// Set in SemanticVerify
PayerAddress ids.ShortID
// Signature of key whose account provides the transaction fee
PayerSig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
// Signatures from Subnet's control keys
// Should not empty slice, not nil, if there are no control sigs
ControlSigs [][crypto.SECP256K1RSigLen]byte `serialize:"true"`
vm *VM
id ids.ID
key crypto.PublicKey // public key of transaction signer
bytes []byte
}
@ -64,10 +75,6 @@ func (tx *CreateChainTx) initialize(vm *VM) error {
// ID of this transaction
func (tx *CreateChainTx) ID() ids.ID { return tx.id }
// Key returns the public key of the signer of this transaction
// Precondition: tx.Verify() has been called and returned nil
func (tx *CreateChainTx) Key() crypto.PublicKey { return tx.key }
// Bytes returns the byte representation of a CreateChainTx
func (tx *CreateChainTx) Bytes() []byte { return tx.bytes }
@ -77,8 +84,8 @@ func (tx *CreateChainTx) SyntacticVerify() error {
switch {
case tx == nil:
return errNilTx
case tx.key != nil:
return nil // Only verify the transaction once
case !tx.PayerAddress.IsZero(): // Only verify the transaction once
return nil
case tx.NetworkID != tx.vm.Ctx.NetworkID: // verify the transaction is on this network
return errWrongNetworkID
case tx.id.IsZero():
@ -87,6 +94,8 @@ func (tx *CreateChainTx) SyntacticVerify() error {
return errInvalidVMID
case !ids.IsSortedAndUniqueIDs(tx.FxIDs):
return errFxIDsNotSortedAndUnique
case !crypto.IsSortedAndUniqueSECP2561RSigs(tx.ControlSigs):
return errControlSigsNotSortedAndUnique
}
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
@ -95,11 +104,11 @@ func (tx *CreateChainTx) SyntacticVerify() error {
return err
}
key, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.Sig[:])
payerKey, err := tx.vm.factory.RecoverPublicKey(unsignedBytes, tx.PayerSig[:])
if err != nil {
return err
}
tx.key = key
tx.PayerAddress = payerKey.Address()
return nil
}
@ -125,10 +134,12 @@ func (tx *CreateChainTx) SemanticVerify(db database.Database) (func(), error) {
}
// Deduct tx fee from payer's account
account, err := tx.vm.getAccount(db, tx.Key().Address())
account, err := tx.vm.getAccount(db, tx.PayerAddress)
if err != nil {
return nil, err
}
// txFee is removed in account.Remove
// TODO: Consider changing Remove to be parameterized on total amount (inc. tx fee) to remove
account, err = account.Remove(0, tx.Nonce)
if err != nil {
return nil, err
@ -137,20 +148,55 @@ func (tx *CreateChainTx) SemanticVerify(db database.Database) (func(), error) {
return nil, err
}
// If this proposal is committed, create the new blockchain using the chain manager
// Verify that this transaction has sufficient control signatures
subnets, err := tx.vm.getSubnets(db) // all subnets that exist
if err != nil {
return nil, err
}
var subnet *CreateSubnetTx // the subnet that will validate the new chain
for _, sn := range subnets {
if sn.id.Equals(tx.SubnetID) {
subnet = sn
break
}
}
if subnet == nil {
return nil, fmt.Errorf("there is no subnet with ID %s", tx.SubnetID)
}
if len(tx.ControlSigs) != int(subnet.Threshold) {
return nil, fmt.Errorf("expected tx to have %d control sigs but has %d", subnet.Threshold, len(tx.ControlSigs))
}
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte representation of the unsigned transaction
if err != nil {
return nil, err
}
unsignedBytesHash := hashing.ComputeHash256(unsignedBytes)
// Each element is ID of key that signed this tx
controlIDs := make([]ids.ShortID, len(tx.ControlSigs))
for i, sig := range tx.ControlSigs {
key, err := tx.vm.factory.RecoverHashPublicKey(unsignedBytesHash, sig[:])
if err != nil {
return nil, err
}
controlIDs[i] = key.Address()
}
// Verify each control signature on this tx is from a control key
controlKeys := ids.ShortSet{}
controlKeys.Add(subnet.ControlKeys...)
for _, controlID := range controlIDs {
if !controlKeys.Contains(controlID) {
return nil, errors.New("tx has control signature from key not in subnet's ControlKeys")
}
}
// If this proposal is committed and this node is a member of the
// subnet that validates the blockchain, create the blockchain
onAccept := func() {
chainParams := chains.ChainParameters{
ID: tx.ID(),
GenesisData: tx.GenesisData,
VMAlias: tx.VMID.String(),
}
for _, fxID := range tx.FxIDs {
chainParams.FxAliases = append(chainParams.FxAliases, fxID.String())
}
// TODO: Not sure how else to make this not nil pointer error during tests
if tx.vm.chainManager != nil {
tx.vm.chainManager.CreateChain(chainParams)
}
tx.vm.createChain(tx)
}
return onAccept, nil
@ -166,10 +212,14 @@ func (chains createChainList) Bytes() []byte {
return bytes
}
func (vm *VM) newCreateChainTx(nonce uint64, genesisData []byte, vmID ids.ID, fxIDs []ids.ID, chainName string, networkID uint32, key *crypto.PrivateKeySECP256K1R) (*CreateChainTx, error) {
func (vm *VM) newCreateChainTx(nonce uint64, subnetID ids.ID, genesisData []byte,
vmID ids.ID, fxIDs []ids.ID, chainName string, networkID uint32,
controlKeys []*crypto.PrivateKeySECP256K1R,
payerKey *crypto.PrivateKeySECP256K1R) (*CreateChainTx, error) {
tx := &CreateChainTx{
UnsignedCreateChainTx: UnsignedCreateChainTx{
NetworkID: networkID,
SubnetID: subnetID,
Nonce: nonce,
GenesisData: genesisData,
VMID: vmID,
@ -178,17 +228,33 @@ func (vm *VM) newCreateChainTx(nonce uint64, genesisData []byte, vmID ids.ID, fx
},
}
// Generate byte repr. of unsigned transaction
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf) // Byte repr. of unsigned transaction
unsignedBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, err
}
unsignedBytesHash := hashing.ComputeHash256(unsignedBytes)
// Sign the tx with control keys
tx.ControlSigs = make([][crypto.SECP256K1RSigLen]byte, len(controlKeys))
for i, key := range controlKeys {
sig, err := key.SignHash(unsignedBytesHash)
if err != nil {
return nil, err
}
copy(tx.ControlSigs[i][:], sig)
}
sig, err := key.Sign(unsignedBytes)
// Sort the control signatures
crypto.SortSECP2561RSigs(tx.ControlSigs)
// Sign with the payer key
payerSig, err := payerKey.Sign(unsignedBytes)
if err != nil {
return nil, err
}
copy(tx.Sig[:], sig)
copy(tx.PayerSig[:], payerSig)
return tx, tx.initialize(vm)
}

View File

@ -6,8 +6,8 @@ package platformvm
import (
"testing"
"github.com/ava-labs/gecko/database/versiondb"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/vms/avm"
)
@ -24,18 +24,19 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
// Case 2: network ID is wrong
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID+1,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
err = tx.SyntacticVerify()
t.Log(err)
if err == nil {
t.Fatal("should've errored because network ID is wrong")
}
@ -43,11 +44,13 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
// case 3: tx ID is empty
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
@ -61,11 +64,13 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
// Case 4: vm ID is empty
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
@ -75,62 +80,189 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
if err := tx.SyntacticVerify(); err == nil {
t.Fatal("should've errored because tx ID is empty")
}
}
func TestSemanticVerify(t *testing.T) {
vm := defaultVM()
// create a tx
tx, err := vm.newCreateChainTx(
// Case 5: Control sigs not sorted
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
// Reverse signature order
tx.ControlSigs[0], tx.ControlSigs[1] = tx.ControlSigs[1], tx.ControlSigs[0]
if err := tx.SyntacticVerify(); err == nil {
t.Fatal("should've errored because control sigs not sorted")
}
// Case 6: Control sigs not unique
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
tx.ControlSigs[0] = tx.ControlSigs[1]
if err := tx.SyntacticVerify(); err == nil {
t.Fatal("should've errored because control sigs not unique")
}
// Case 7: Valid tx passes syntactic verification
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatalf("should have passed verification but got %v", err)
}
}
// Ensure SemanticVerify fails when there are not enough control sigs
func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
vm := defaultVM()
// Case 1: No control sigs (2 are needed)
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
nil,
defaultKey,
)
if err != nil {
t.Fatal(err)
}
newDB := versiondb.New(vm.DB)
_, err = tx.SemanticVerify(newDB)
if err != nil {
t.Fatal(err)
_, err = tx.SemanticVerify(vm.DB)
if err == nil {
t.Fatal("should have errored because there are no control sigs")
}
chains, err := vm.getChains(newDB)
if err != nil {
t.Fatal(err)
}
for _, c := range chains {
if c.ID().Equals(tx.ID()) {
return
}
}
t.Fatalf("Should have added the chain to the set of chains")
}
func TestSemanticVerifyAlreadyExisting(t *testing.T) {
vm := defaultVM()
// create a tx
tx, err := vm.newCreateChainTx(
// Case 2: 1 control sig (2 are needed)
tx, err = vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
// put the chain in existing chain
_, err = tx.SemanticVerify(vm.DB)
if err == nil {
t.Fatal("should have errored because there are no control sigs")
}
}
// Ensure SemanticVerify fails when an incorrect control signature is given
func TestCreateChainTxWrongControlSig(t *testing.T) {
vm := defaultVM()
// Generate new, random key to sign tx with
factory := crypto.FactorySECP256K1R{}
key, err := factory.NewPrivateKey()
if err != nil {
t.Fatal(err)
}
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], key.(*crypto.PrivateKeySECP256K1R)},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
_, err = tx.SemanticVerify(vm.DB)
if err == nil {
t.Fatal("should have errored because incorrect control sig given")
}
}
// Ensure SemanticVerify fails when the Subnet the blockchain specifies as
// its validator set doesn't exist
func TestCreateChainTxNoSuchSubnet(t *testing.T) {
vm := defaultVM()
tx, err := vm.newCreateChainTx(
defaultNonce+1,
ids.NewID([32]byte{1, 9, 124, 11, 20}), // pick some random ID for subnet
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
_, err = tx.SemanticVerify(vm.DB)
if err == nil {
t.Fatal("should have errored because Subnet doesn't exist")
}
}
func TestCreateChainTxAlreadyExists(t *testing.T) {
vm := defaultVM()
// create a tx
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
// put the chain in existing chain list
if err := vm.putChains(vm.DB, []*CreateChainTx{tx}); err != nil {
t.Fatal(err)
}
@ -140,3 +272,29 @@ func TestSemanticVerifyAlreadyExisting(t *testing.T) {
t.Fatalf("should have failed because there is already a chain with ID %s", tx.id)
}
}
// Ensure valid tx passes semanticVerify
func TestCreateChainTxValid(t *testing.T) {
vm := defaultVM()
// create a valid tx
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
avm.ID,
nil,
"chain name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
defaultKey,
)
if err != nil {
t.Fatal(err)
}
_, err = tx.SemanticVerify(vm.DB)
if err != nil {
t.Fatalf("expected tx to pass verification but got error: %v", err)
}
}

View File

@ -8,8 +8,8 @@ import (
"fmt"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/validators"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/hashing"
)
@ -17,8 +17,10 @@ import (
const maxThreshold = 25
var (
errThresholdExceedsKeysLen = errors.New("threshold must be no more than number of control keys")
errThresholdTooHigh = fmt.Errorf("threshold can't be greater than %d", maxThreshold)
errThresholdExceedsKeysLen = errors.New("threshold must be no more than number of control keys")
errThresholdTooHigh = fmt.Errorf("threshold can't be greater than %d", maxThreshold)
errControlKeysNotSortedAndUnique = errors.New("control keys must be sorted and unique")
errUnneededKeys = errors.New("subnets shouldn't have keys if the threshold is 0")
)
// UnsignedCreateSubnetTx is an unsigned proposal to create a new subnet
@ -41,11 +43,8 @@ type UnsignedCreateSubnetTx struct {
type CreateSubnetTx struct {
UnsignedCreateSubnetTx `serialize:"true"`
// The VM this tx exists within
vm *VM
// ID is this transaction's ID
id ids.ID
// Signature on the UnsignedCreateSubnetTx's byte repr
Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
// The public key that signed this transaction
// The transaction fee will be paid from the corresponding account
@ -53,14 +52,17 @@ type CreateSubnetTx struct {
// [key] is non-nil iff this tx is valid
key crypto.PublicKey
// Signature on the UnsignedCreateSubnetTx's byte repr
Sig [crypto.SECP256K1RSigLen]byte `serialize:"true"`
// The VM this tx exists within
vm *VM
// ID is this transaction's ID
id ids.ID
// Byte representation of this transaction (including signature)
bytes []byte
}
// ID returns the ID of this tx
// ID returns the ID of this transaction
func (tx *CreateSubnetTx) ID() ids.ID { return tx.id }
// SyntacticVerify nil iff [tx] is syntactically valid.
@ -77,6 +79,12 @@ func (tx *CreateSubnetTx) SyntacticVerify() error {
return errWrongNetworkID
case tx.Threshold > uint16(len(tx.ControlKeys)):
return errThresholdExceedsKeysLen
case tx.Threshold > maxThreshold:
return errThresholdTooHigh
case tx.Threshold == 0 && len(tx.ControlKeys) > 0:
return errUnneededKeys
case !ids.IsSortedAndUniqueShortIDs(tx.ControlKeys):
return errControlKeysNotSortedAndUnique
}
// Byte representation of the unsigned transaction
@ -107,12 +115,6 @@ func (tx *CreateSubnetTx) SemanticVerify(db database.Database) (func(), error) {
if err != nil {
return nil, err
}
for _, subnet := range subnets {
if subnet.id.Equals(tx.id) {
return nil, fmt.Errorf("there is already a subnet with ID %s", tx.id)
}
}
subnets = append(subnets, tx) // add new subnet
if err := tx.vm.putSubnets(db, subnets); err != nil {
return nil, err
@ -131,7 +133,12 @@ func (tx *CreateSubnetTx) SemanticVerify(db database.Database) (func(), error) {
return nil, err
}
return nil, nil
// Register new subnet in validator manager
onAccept := func() {
tx.vm.validators.PutValidatorSet(tx.id, validators.NewSet())
}
return onAccept, nil
}
// Bytes returns the byte representation of [tx]
@ -159,10 +166,11 @@ func (tx *CreateSubnetTx) initialize(vm *VM) error {
return nil
}
// [controlKeys] must be unique. They will be sorted by this method.
// If [controlKeys] is nil, [tx.Controlkeys] will be an empty list.
func (vm *VM) newCreateSubnetTx(networkID uint32, nonce uint64, controlKeys []ids.ShortID,
threshold uint16, payerKey *crypto.PrivateKeySECP256K1R,
) (*CreateSubnetTx, error) {
tx := &CreateSubnetTx{UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{
NetworkID: networkID,
Nonce: nonce,
@ -170,6 +178,17 @@ func (vm *VM) newCreateSubnetTx(networkID uint32, nonce uint64, controlKeys []id
Threshold: threshold,
}}
if threshold == 0 && len(tx.ControlKeys) > 0 {
return nil, errUnneededKeys
}
// Sort control keys
ids.SortShortIDs(tx.ControlKeys)
// Ensure control keys are unique
if !ids.IsSortedAndUniqueShortIDs(tx.ControlKeys) {
return nil, errControlKeysNotSortedAndUnique
}
unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx)
unsignedBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {

View File

@ -183,7 +183,7 @@ func (vm *VM) newExportTx(nonce uint64, networkID uint32, outs []*ava.Transferab
return nil, err
}
sig, err := key.Sign(unsignedBytes)
sig, err := from.Sign(unsignedBytes)
if err != nil {
return nil, err
}

View File

@ -16,18 +16,20 @@ var (
// Factory can create new instances of the Platform Chain
type Factory struct {
ChainManager chains.Manager
Validators validators.Manager
AVA ids.ID
AVM ids.ID
ChainManager chains.Manager
Validators validators.Manager
StakingEnabled bool
AVA ids.ID
AVM ids.ID
}
// New returns a new instance of the Platform Chain
func (f *Factory) New() interface{} {
return &VM{
chainManager: f.ChainManager,
validators: f.Validators,
ava: f.AVA,
avm: f.AVM,
chainManager: f.ChainManager,
validators: f.Validators,
stakingEnabled: f.StakingEnabled,
ava: f.AVA,
avm: f.AVM,
}
}

View File

@ -231,7 +231,7 @@ func (vm *VM) newImportTx(nonce uint64, networkID uint32, ins []*ava.Transferabl
tx := &ImportTx{UnsignedImportTx: UnsignedImportTx{
NetworkID: networkID,
Nonce: nonce,
Account: key.PublicKey().Address(),
Account: to.PublicKey().Address(),
Ins: ins,
}}
@ -258,7 +258,7 @@ func (vm *VM) newImportTx(nonce uint64, networkID uint32, ins []*ava.Transferabl
tx.Creds = append(tx.Creds, cred)
}
sig, err := key.SignHash(hash)
sig, err := to.SignHash(hash)
if err != nil {
return nil, err
}

View File

@ -8,9 +8,6 @@ import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"github.com/gorilla/rpc/v2/json2"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids"
@ -19,39 +16,26 @@ import (
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/math"
"github.com/ava-labs/gecko/vms/avm"
"github.com/ava-labs/gecko/vms/components/ava"
"github.com/ava-labs/gecko/vms/secp256k1fx"
)
var (
errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks")
errParsingID = errors.New("error parsing ID")
errGetAccount = errors.New("error retrieving account information")
errGetAccounts = errors.New("error getting accounts controlled by specified user")
errGetUser = errors.New("error while getting user. Does user exist?")
errNoMethodWithGenesis = errors.New("no method was provided but genesis data was provided")
errCreatingTransaction = errors.New("problem while creating transaction")
errNoDestination = errors.New("call is missing field 'stakeDestination'")
errNoSource = errors.New("call is missing field 'stakeSource'")
errGetStakeSource = errors.New("couldn't get account specified in 'stakeSource'")
errMissingDecisionBlock = errors.New("should have a decision block within the past two blocks")
errParsingID = errors.New("error parsing ID")
errGetAccount = errors.New("error retrieving account information")
errGetAccounts = errors.New("error getting accounts controlled by specified user")
errGetUser = errors.New("error while getting user. Does user exist?")
errNoMethodWithGenesis = errors.New("no method was provided but genesis data was provided")
errCreatingTransaction = errors.New("problem while creating transaction")
errNoDestination = errors.New("call is missing field 'stakeDestination'")
errNoSource = errors.New("call is missing field 'stakeSource'")
errGetStakeSource = errors.New("couldn't get account specified in 'stakeSource'")
errNoBlockchainWithAlias = errors.New("there is no blockchain with the specified alias")
errDSCantValidate = errors.New("new blockchain can't be validated by default Subnet")
)
var key *crypto.PrivateKeySECP256K1R
func init() {
cb58 := formatting.CB58{}
err := cb58.FromString("24jUJ9vZexUM6expyMcT48LBx27k1m7xpraoV62oSQAHdziao5")
if err != nil {
panic(err)
}
factory := crypto.FactorySECP256K1R{}
pk, err := factory.ToPrivateKey(cb58.Bytes)
if err != nil {
panic(err)
}
key = pk.(*crypto.PrivateKeySECP256K1R)
}
// Service defines the API calls that can be made to the platform chain
type Service struct{ vm *VM }
@ -319,7 +303,7 @@ type ListAccountsReply struct {
// ListAccounts lists all of the accounts controlled by [args.Username]
func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, reply *ListAccountsReply) error {
service.vm.Ctx.Log.Debug("platform.listAccounts called for user '%s'", args.Username)
service.vm.Ctx.Log.Debug("listAccounts called for user '%s'", args.Username)
// db holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
@ -382,7 +366,7 @@ type CreateAccountReply struct {
// The account's ID is [privKey].PublicKey().Address(), where [privKey] is a
// private key controlled by the user.
func (service *Service) CreateAccount(_ *http.Request, args *CreateAccountArgs, reply *CreateAccountReply) error {
service.vm.Ctx.Log.Debug("platform.createAccount called for user '%s'", args.Username)
service.vm.Ctx.Log.Debug("createAccount called for user '%s'", args.Username)
// userDB holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
@ -452,7 +436,7 @@ type AddDefaultSubnetValidatorArgs struct {
// AddDefaultSubnetValidator returns an unsigned transaction to add a validator to the default subnet
// The returned unsigned transaction should be signed using Sign()
func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetValidator called")
service.vm.Ctx.Log.Debug("AddDefaultSubnetValidator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
args.ID = service.vm.Ctx.NodeID
@ -497,7 +481,7 @@ type AddDefaultSubnetDelegatorArgs struct {
// to the default subnet
// The returned unsigned transaction should be signed using Sign()
func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.AddDefaultSubnetDelegator called")
service.vm.Ctx.Log.Debug("AddDefaultSubnetDelegator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
args.ID = service.vm.Ctx.NodeID
@ -679,7 +663,7 @@ type SignResponse struct {
// Sign [args.bytes]
func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignResponse) error {
service.vm.Ctx.Log.Debug("platform.sign called")
service.vm.Ctx.Log.Debug("sign called")
// Get the key of the Signer
db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
@ -710,10 +694,12 @@ func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignRespons
genTx.Tx, err = service.signAddNonDefaultSubnetValidatorTx(tx, key)
case *CreateSubnetTx:
genTx.Tx, err = service.signCreateSubnetTx(tx, key)
case *CreateChainTx:
genTx.Tx, err = service.signCreateChainTx(tx, key)
case *ExportTx:
genTx.Tx, err = service.signExportTx(tx, key)
default:
err = errors.New("Could not parse given tx. Must be one of: addDefaultSubnetValidatorTx, addNonDefaultSubnetValidatorTx, createSubnetTx")
err = errors.New("Could not parse given tx")
}
if err != nil {
return err
@ -725,7 +711,7 @@ func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignRespons
// Sign [unsigned] with [key]
func (service *Service) signAddDefaultSubnetValidatorTx(tx *addDefaultSubnetValidatorTx, key *crypto.PrivateKeySECP256K1R) (*addDefaultSubnetValidatorTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddDefaultSubnetValidatorTx called")
service.vm.Ctx.Log.Debug("signAddDefaultSubnetValidatorTx called")
// TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetValidatorTx)
@ -748,7 +734,7 @@ func (service *Service) signAddDefaultSubnetValidatorTx(tx *addDefaultSubnetVali
// Sign [unsigned] with [key]
func (service *Service) signAddDefaultSubnetDelegatorTx(tx *addDefaultSubnetDelegatorTx, key *crypto.PrivateKeySECP256K1R) (*addDefaultSubnetDelegatorTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddDefaultSubnetValidatorTx called")
service.vm.Ctx.Log.Debug("signAddDefaultSubnetValidatorTx called")
// TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetDelegatorTx)
@ -771,7 +757,7 @@ func (service *Service) signAddDefaultSubnetDelegatorTx(tx *addDefaultSubnetDele
// Sign [xt] with [key]
func (service *Service) signCreateSubnetTx(tx *CreateSubnetTx, key *crypto.PrivateKeySECP256K1R) (*CreateSubnetTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddDefaultSubnetValidatorTx called")
service.vm.Ctx.Log.Debug("signAddDefaultSubnetValidatorTx called")
// TODO: Should we check if tx is already signed?
unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx)
@ -822,7 +808,7 @@ func (service *Service) signExportTx(tx *ExportTx, key *crypto.PrivateKeySECP256
// Sorts tx.ControlSigs before returning
// Assumes each element of tx.ControlSigs is actually a signature, not just empty bytes
func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubnetValidatorTx, key *crypto.PrivateKeySECP256K1R) (*addNonDefaultSubnetValidatorTx, error) {
service.vm.Ctx.Log.Debug("platform.signAddNonDefaultSubnetValidatorTx called")
service.vm.Ctx.Log.Debug("signAddNonDefaultSubnetValidatorTx called")
// Compute the byte repr. of the unsigned tx and the signature of [key] over it
unsignedIntf := interface{}(&tx.UnsignedAddNonDefaultSubnetValidatorTx)
@ -1003,6 +989,59 @@ func (service *Service) CreateImportTx(_ *http.Request, args *CreateImportTxArgs
return nil
}
// Signs an unsigned or partially signed CreateChainTx with [key]
// If [key] is a control key for the subnet and there is an empty spot in tx.ControlSigs, signs there
// If [key] is a control key for the subnet and there is no empty spot in tx.ControlSigs, signs as payer
// If [key] is not a control key, sign as payer (account controlled by [key] pays the tx fee)
// Sorts tx.ControlSigs before returning
// Assumes each element of tx.ControlSigs is actually a signature, not just empty bytes
func (service *Service) signCreateChainTx(tx *CreateChainTx, key *crypto.PrivateKeySECP256K1R) (*CreateChainTx, error) {
service.vm.Ctx.Log.Debug("signCreateChainTx called")
// Compute the byte repr. of the unsigned tx and the signature of [key] over it
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
}
sig, err := key.Sign(unsignedTxBytes)
if err != nil {
return nil, errors.New("error while signing")
}
if len(sig) != crypto.SECP256K1RSigLen {
return nil, fmt.Errorf("expected signature to be length %d but was length %d", crypto.SECP256K1RSigLen, len(sig))
}
// Get information about the subnet
subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID)
if err != nil {
return nil, fmt.Errorf("problem getting subnet information: %v", err)
}
// Find the location at which [key] should put its signature.
// If [key] is a control key for this subnet and there is an empty spot in tx.ControlSigs, sign there
// If [key] is a control key for this subnet and there is no empty spot in tx.ControlSigs, sign as payer
// If [key] is not a control key, sign as payer (account controlled by [key] pays the tx fee)
controlKeySet := ids.ShortSet{}
controlKeySet.Add(subnet.ControlKeys...)
isControlKey := controlKeySet.Contains(key.PublicKey().Address())
payerSigEmpty := tx.PayerSig == [crypto.SECP256K1RSigLen]byte{} // true if no key has signed to pay the tx fee
if isControlKey && len(tx.ControlSigs) != int(subnet.Threshold) { // Sign as controlSig
tx.ControlSigs = append(tx.ControlSigs, [crypto.SECP256K1RSigLen]byte{})
copy(tx.ControlSigs[len(tx.ControlSigs)-1][:], sig)
} else if payerSigEmpty { // sign as payer
copy(tx.PayerSig[:], sig)
} else {
return nil, errors.New("no place for key to sign")
}
crypto.SortSECP2561RSigs(tx.ControlSigs)
return tx, nil
}
// IssueTxArgs are the arguments to IssueTx
type IssueTxArgs struct {
// Tx being sent to the network
@ -1017,6 +1056,8 @@ type IssueTxResponse struct {
// IssueTx issues the transaction [args.Tx] to the network
func (service *Service) IssueTx(_ *http.Request, args *IssueTxArgs, response *IssueTxResponse) error {
service.vm.Ctx.Log.Debug("issueTx called")
genTx := genericTx{}
if err := Codec.Unmarshal(args.Tx.Bytes, &genTx); err != nil {
return err
@ -1057,6 +1098,9 @@ func (service *Service) IssueTx(_ *http.Request, args *IssueTxArgs, response *Is
// CreateBlockchainArgs is the arguments for calling CreateBlockchain
type CreateBlockchainArgs struct {
// ID of Subnet that validates the new blockchain
SubnetID ids.ID `json:"subnetID"`
// ID of the VM the new blockchain is running
VMID string `json:"vmID"`
@ -1066,26 +1110,18 @@ type CreateBlockchainArgs struct {
// Human-readable name for the new blockchain, not necessarily unique
Name string `json:"name"`
// To generate the byte representation of the genesis data for this blockchain,
// a POST request with body [GenesisData] is made to the API method whose name is [Method], whose
// endpoint is [Endpoint]. See Platform Chain documentation for more info and examples.
Method string `json:"method"`
Endpoint string `json:"endpoint"`
GenesisData interface{} `json:"genesisData"`
// Next unused nonce of the account paying the transaction fee
PayerNonce json.Uint64 `json:"payerNonce"`
// Genesis state of the blockchain being created
GenesisData formatting.CB58 `json:"genesisData"`
}
// CreateGenesisReply is the reply from a call to CreateGenesis
type CreateGenesisReply struct {
Bytes formatting.CB58 `json:"bytes"`
}
// CreateBlockchain returns an unsigned transaction to create a new blockchain
// Must be signed with the Subnet's control keys and with a key that pays the transaction fee before issuance
func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchainArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("createBlockchain called")
// CreateBlockchainReply is the reply from calling CreateBlockchain
type CreateBlockchainReply struct {
BlockchainID ids.ID `json:"blockchainID"`
}
// CreateBlockchain issues a transaction to the network to create a new blockchain
func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchainArgs, reply *CreateBlockchainReply) error {
vmID, err := service.vm.chainManager.LookupVM(args.VMID)
if err != nil {
return fmt.Errorf("no VM with ID '%s' found", args.VMID)
@ -1099,48 +1135,43 @@ func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchain
}
fxIDs = append(fxIDs, fxID)
}
genesisBytes := []byte(nil)
if args.Method != "" {
buf, err := json2.EncodeClientRequest(args.Method, args.GenesisData)
if err != nil {
return fmt.Errorf("problem building blockchain genesis state: %w", err)
}
writer := httptest.NewRecorder()
service.vm.Ctx.HTTP.Call(
/*writer=*/ writer,
/*method=*/ "POST",
/*base=*/ args.VMID,
/*endpoint=*/ args.Endpoint,
/*body=*/ bytes.NewBuffer(buf),
/*headers=*/ map[string]string{
"Content-Type": "application/json",
},
)
result := CreateGenesisReply{}
if err := json2.DecodeClientResponse(writer.Body, &result); err != nil {
return fmt.Errorf("problem building blockchain genesis state: %w", err)
}
genesisBytes = result.Bytes.Bytes
} else if args.GenesisData != nil {
return errNoMethodWithGenesis
// If creating AVM instance, use secp256k1fx
// TODO: Document FXs and have user specify them in API call
fxIDsSet := ids.Set{}
fxIDsSet.Add(fxIDs...)
if vmID.Equals(avm.ID) && !fxIDsSet.Contains(secp256k1fx.ID) {
fxIDs = append(fxIDs, secp256k1fx.ID)
}
// TODO: Should use the key store to sign this transaction.
// TODO: Nonce shouldn't always be 0
tx, err := service.vm.newCreateChainTx(0, genesisBytes, vmID, fxIDs, args.Name, service.vm.Ctx.NetworkID, key)
if args.SubnetID.Equals(DefaultSubnetID) {
return errDSCantValidate
}
tx := CreateChainTx{
UnsignedCreateChainTx: UnsignedCreateChainTx{
NetworkID: service.vm.Ctx.NetworkID,
SubnetID: args.SubnetID,
Nonce: uint64(args.PayerNonce),
ChainName: args.Name,
VMID: vmID,
FxIDs: fxIDs,
GenesisData: args.GenesisData.Bytes,
},
PayerAddress: ids.ShortID{},
PayerSig: [crypto.SECP256K1RSigLen]byte{},
ControlSigs: nil,
vm: nil,
id: ids.ID{},
bytes: nil,
}
txBytes, err := Codec.Marshal(genericTx{Tx: &tx})
if err != nil {
return fmt.Errorf("problem creating transaction: %w", err)
service.vm.Ctx.Log.Error("problem marshaling createChainTx: %v", err)
return errCreatingTransaction
}
// Add this tx to the set of unissued txs
service.vm.unissuedDecisionTxs = append(service.vm.unissuedDecisionTxs, tx)
service.vm.resetTimer()
reply.BlockchainID = tx.ID()
response.UnsignedTx.Bytes = txBytes
return nil
}
@ -1158,6 +1189,8 @@ type GetBlockchainStatusReply struct {
// GetBlockchainStatus gets the status of a blockchain with the ID [args.BlockchainID].
func (service *Service) GetBlockchainStatus(_ *http.Request, args *GetBlockchainStatusArgs, reply *GetBlockchainStatusReply) error {
service.vm.Ctx.Log.Debug("getBlockchainStatus called")
_, err := service.vm.chainManager.Lookup(args.BlockchainID)
if err == nil {
reply.Status = Validating
@ -1212,3 +1245,100 @@ func (service *Service) chainExists(blockID ids.ID, chainID ids.ID) (bool, error
return false, nil
}
// ValidatedByArgs is the arguments for calling ValidatedBy
type ValidatedByArgs struct {
// ValidatedBy returns the ID of the Subnet validating the blockchain with this ID
BlockchainID ids.ID `json:"blockchainID"`
}
// ValidatedByResponse is the reply from calling ValidatedBy
type ValidatedByResponse struct {
// ID of the Subnet validating the specified blockchain
SubnetID ids.ID `json:"subnetID"`
}
// ValidatedBy returns the ID of the Subnet that validates [args.BlockchainID]
func (service *Service) ValidatedBy(_ *http.Request, args *ValidatedByArgs, response *ValidatedByResponse) error {
service.vm.Ctx.Log.Debug("validatedBy called")
chain, err := service.vm.getChain(service.vm.DB, args.BlockchainID)
if err != nil {
return err
}
response.SubnetID = chain.SubnetID
return nil
}
// ValidatesArgs are the arguments to Validates
type ValidatesArgs struct {
SubnetID ids.ID `json:"subnetID"`
}
// ValidatesResponse is the response from calling Validates
type ValidatesResponse struct {
BlockchainIDs []ids.ID `json:"blockchainIDs"`
}
// Validates returns the IDs of the blockchains validated by [args.SubnetID]
func (service *Service) Validates(_ *http.Request, args *ValidatesArgs, response *ValidatesResponse) error {
service.vm.Ctx.Log.Debug("validates called")
// Verify that the Subnet exists
if _, err := service.vm.getSubnet(service.vm.DB, args.SubnetID); err != nil {
return err
}
// Get the chains that exist
chains, err := service.vm.getChains(service.vm.DB)
if err != nil {
return err
}
// Filter to get the chains validated by the specified Subnet
for _, chain := range chains {
if chain.SubnetID.Equals(args.SubnetID) {
response.BlockchainIDs = append(response.BlockchainIDs, chain.ID())
}
}
return nil
}
// APIBlockchain is the representation of a blockchain used in API calls
type APIBlockchain struct {
// Blockchain's ID
ID ids.ID `json:"id"`
// Blockchain's (non-unique) human-readable name
Name string `json:"name"`
// Subnet that validates the blockchain
SubnetID ids.ID `json:"subnetID"`
// Virtual Machine the blockchain runs
VMID ids.ID `json:"vmID"`
}
// GetBlockchainsResponse is the response from a call to GetBlockchains
type GetBlockchainsResponse struct {
// blockchains that exist
Blockchains []APIBlockchain `json:"blockchains"`
}
// GetBlockchains returns all of the blockchains that exist
func (service *Service) GetBlockchains(_ *http.Request, args *struct{}, response *GetBlockchainsResponse) error {
service.vm.Ctx.Log.Debug("getBlockchains called")
chains, err := service.vm.getChains(service.vm.DB)
if err != nil {
return fmt.Errorf("couldn't retrieve blockchains: %v", err)
}
for _, chain := range chains {
response.Blockchains = append(response.Blockchains, APIBlockchain{
ID: chain.ID(),
Name: chain.ChainName,
SubnetID: chain.SubnetID,
VMID: chain.VMID,
})
}
return nil
}

View File

@ -22,7 +22,7 @@ func TestAddDefaultSubnetValidator(t *testing.T) {
}
func TestCreateBlockchainArgsParsing(t *testing.T) {
jsonString := `{"vmID":"lol","chainName":"awesome","genesisData":{"key":"value"}}`
jsonString := `{"vmID":"lol","fxIDs":["secp256k1"], "name":"awesome", "payerNonce":5, "genesisData":"SkB92YpWm4Q2iPnLGCuDPZPgUQMxajqQQuz91oi3xD984f8r"}`
args := CreateBlockchainArgs{}
err := json.Unmarshal([]byte(jsonString), &args)
if err != nil {

View File

@ -17,6 +17,7 @@ import (
var (
errEmptyAccountAddress = errors.New("account has empty address")
errNoSuchBlockchain = errors.New("there is no blockchain with the specified ID")
)
// TODO: Cache prefixed IDs or use different way of keying into database
@ -146,7 +147,7 @@ func (vm *VM) putAccount(db database.Database, account Account) error {
return nil
}
// get the blockchains that exist
// get all the blockchains that exist
func (vm *VM) getChains(db database.Database) ([]*CreateChainTx, error) {
chainsInterface, err := vm.State.Get(db, chainsTypeID, chainsKey)
if err != nil {
@ -154,12 +155,26 @@ func (vm *VM) getChains(db database.Database) ([]*CreateChainTx, error) {
}
chains, ok := chainsInterface.([]*CreateChainTx)
if !ok {
vm.Ctx.Log.Warn("expected to retrieve []*CreateChainTx from database but got different type")
vm.Ctx.Log.Error("expected to retrieve []*CreateChainTx from database but got different type")
return nil, errDBChains
}
return chains, nil
}
// get a blockchain by its ID
func (vm *VM) getChain(db database.Database, ID ids.ID) (*CreateChainTx, error) {
chains, err := vm.getChains(db)
if err != nil {
return nil, err
}
for _, chain := range chains {
if chain.ID().Equals(ID) {
return chain, nil
}
}
return nil, errNoSuchBlockchain
}
// put the list of blockchains that exist to database
func (vm *VM) putChains(db database.Database, chains createChainList) error {
if err := vm.State.Put(db, chainsTypeID, chainsKey, chains); err != nil {

View File

@ -9,6 +9,7 @@ import (
"net/http"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/json"
)
@ -74,11 +75,13 @@ type APIDefaultSubnetValidator struct {
// [VMID] is the ID of the VM this chain runs.
// [FxIDs] are the IDs of the Fxs the chain supports.
// [Name] is a human-readable, non-unique name for the chain.
// [SubnetID] is the ID of the subnet that validates the chain
type APIChain struct {
GenesisData formatting.CB58 `json:"genesisData"`
VMID ids.ID `json:"vmID"`
FxIDs []ids.ID `json:"fxIDs"`
Name string `json:"name"`
SubnetID ids.ID `json:"subnetID"`
}
// BuildGenesisArgs are the arguments used to create
@ -134,8 +137,8 @@ func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, repl
return errAccountHasNoValue
}
accounts = append(accounts, newAccount(
account.Address, // ID
0, // nonce
account.Address, // ID
0, // nonce
uint64(account.Balance), // balance
))
}
@ -182,12 +185,15 @@ func (*StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, repl
tx := &CreateChainTx{
UnsignedCreateChainTx: UnsignedCreateChainTx{
NetworkID: uint32(args.NetworkID),
SubnetID: chain.SubnetID,
Nonce: 0,
ChainName: chain.Name,
VMID: chain.VMID,
FxIDs: chain.FxIDs,
GenesisData: chain.GenesisData.Bytes,
},
ControlSigs: [][crypto.SECP256K1RSigLen]byte{},
PayerSig: [crypto.SECP256K1RSigLen]byte{},
}
if err := tx.initialize(nil); err != nil {
return err

View File

@ -4,114 +4,12 @@
package platformvm
import (
"bytes"
"testing"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/json"
)
func TestBuildGenesis(t *testing.T) {
expected := []byte{
0x00, 0x00, 0x00, 0x01, 0x01, 0x5c, 0xce, 0x6c,
0x55, 0xd6, 0xb5, 0x09, 0x84, 0x5c, 0x8c, 0x4e,
0x30, 0xbe, 0xd9, 0x8d, 0x39, 0x1a, 0xe7, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x5b, 0xcd, 0x15,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0b, 0x01, 0x5c, 0xce, 0x6c, 0x55, 0xd6, 0xb5,
0x09, 0x84, 0x5c, 0x8c, 0x4e, 0x30, 0xbe, 0xd9,
0x8d, 0x39, 0x1a, 0xe7, 0xf0, 0x00, 0x00, 0x00,
0x00, 0x3a, 0xde, 0x68, 0xb1, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x5c, 0xce, 0x6c, 0x55, 0xd6, 0xb5,
0x09, 0x84, 0x5c, 0x8c, 0x4e, 0x30, 0xbe, 0xd9,
0x8d, 0x39, 0x1a, 0xe7, 0xf0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x13, 0x4d, 0x79, 0x20, 0x46,
0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x20,
0x45, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x53,
0x6f, 0x75, 0x74, 0x68, 0x20, 0x50, 0x61, 0x72,
0x6b, 0x20, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64,
0x65, 0x20, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x53,
0x63, 0x6f, 0x74, 0x74, 0x20, 0x54, 0x65, 0x6e,
0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x20, 0x6d, 0x75,
0x73, 0x74, 0x20, 0x64, 0x69, 0x65, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
}
addr, _ := ids.ShortFromString("8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z")
genesisData := formatting.CB58{}
genesisData.FromString("CGgRrQ3nws7RRMGyDV59cetJBAwmsmDyCSgku")
vmID, _ := ids.FromString("dkFD29iYU9e9jah2nrnksTWJUy2VVpg5Lnqd7nQqvCJgR26H4")
account := APIAccount{
Address: addr,
Balance: 123456789,
}
weight := json.Uint64(987654321)
validator := APIDefaultSubnetValidator{
APIValidator: APIValidator{
EndTime: 15,
Weight: &weight,
ID: addr,
},
Destination: addr,
}
chains := APIChain{
GenesisData: genesisData,
VMID: vmID,
Name: "My Favorite Episode",
}
args := BuildGenesisArgs{
Accounts: []APIAccount{
account,
},
Validators: []APIDefaultSubnetValidator{
validator,
},
Chains: []APIChain{
chains,
},
Time: 5,
}
reply := BuildGenesisReply{}
ss := StaticService{}
if err := ss.BuildGenesis(nil, &args, &reply); err != nil {
t.Fatal(err)
}
if !bytes.Equal(reply.Bytes.Bytes, expected) {
t.Fatalf("StaticService.BuildGenesis:\nReturned:\n%s\nExpected:\n%s",
formatting.DumpBytes{Bytes: reply.Bytes.Bytes},
formatting.DumpBytes{Bytes: expected})
}
}
func TestBuildGenesisInvalidAccountBalance(t *testing.T) {
id, _ := ids.ShortFromString("8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z")
account := APIAccount{

View File

@ -151,8 +151,13 @@ func init() {
type VM struct {
*core.SnowmanVM
// Node's validator manager
// Maps Subnets --> nodes in the Subnet HEAD
validators validators.Manager
// true if the node is being run with staking enabled
stakingEnabled bool
// The node's chain manager
chainManager chains.Manager
@ -296,8 +301,8 @@ func (vm *VM) Initialize(
})
go ctx.Log.RecoverAndPanic(vm.timer.Dispatch)
if err := vm.updateValidators(DefaultSubnetID); err != nil {
ctx.Log.Error("failed to initialize the current validator set: %s", err)
if err := vm.initSubnets(); err != nil {
ctx.Log.Error("failed to initialize Subnets: %s", err)
return err
}
@ -313,27 +318,67 @@ func (vm *VM) Initialize(
return nil
}
// Create all of the chains that the database says should exist
// Create all chains that exist that this node validates
// Can only be called after initSubnets()
func (vm *VM) initBlockchains() error {
vm.Ctx.Log.Verbo("platform chain initializing existing blockchains")
existingChains, err := vm.getChains(vm.DB)
vm.Ctx.Log.Info("initializing blockchains")
blockchains, err := vm.getChains(vm.DB) // get blockchains that exist
if err != nil {
return err
}
for _, chain := range existingChains { // Create each blockchain
chainParams := chains.ChainParameters{
ID: chain.ID(),
GenesisData: chain.GenesisData,
VMAlias: chain.VMID.String(),
}
for _, fxID := range chain.FxIDs {
chainParams.FxAliases = append(chainParams.FxAliases, fxID.String())
}
vm.chainManager.CreateChain(chainParams)
for _, chain := range blockchains {
vm.createChain(chain)
}
return nil
}
// Set the node's validator manager to be up to date
func (vm *VM) initSubnets() error {
vm.Ctx.Log.Info("initializing Subnets")
subnets, err := vm.getSubnets(vm.DB)
if err != nil {
return err
}
if err := vm.updateValidators(DefaultSubnetID); err != nil {
return err
}
for _, subnet := range subnets {
if err := vm.updateValidators(subnet.id); err != nil {
return err
}
}
return nil
}
// Create the blockchain described in [tx], but only if this node is a member of
// the Subnet that validates the chain
func (vm *VM) createChain(tx *CreateChainTx) {
// The validators that compose the Subnet that validates this chain
validators, subnetExists := vm.validators.GetValidatorSet(tx.SubnetID)
if !subnetExists {
vm.Ctx.Log.Error("blockchain %s validated by Subnet %s but couldn't get that Subnet. Blockchain not created")
return
}
if !validators.Contains(vm.Ctx.NodeID) && vm.stakingEnabled { // This node doesn't validate this blockchain
return
}
chainParams := chains.ChainParameters{
ID: tx.ID(),
SubnetID: tx.SubnetID,
GenesisData: tx.GenesisData,
VMAlias: tx.VMID.String(),
}
for _, fxID := range tx.FxIDs {
chainParams.FxAliases = append(chainParams.FxAliases, fxID.String())
}
vm.chainManager.CreateChain(chainParams)
}
// Shutdown this blockchain
func (vm *VM) Shutdown() {
vm.timer.Stop()
@ -663,13 +708,16 @@ func (vm *VM) nextSubnetValidatorChangeTime(db database.Database, subnetID ids.I
// Returns:
// 1) The validator set of subnet with ID [subnetID] when timestamp is advanced to [timestamp]
// 2) The pending validator set of subnet with ID [subnetID] when timestamp is advanced to [timestamp]
// 3) The IDs of the validators that start validating [subnetID] between now and [timestamp]
// 4) The IDs of the validators that stop validating [subnetID] between now and [timestamp]
// Note that this method will not remove validators from the current validator set of the default subnet.
// That happens in reward blocks.
func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, subnetID ids.ID) (current, pending *EventHeap, err error) {
func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, subnetID ids.ID) (current,
pending *EventHeap, started, stopped ids.ShortSet, err error) {
// remove validators whose end time <= [timestamp]
current, err = vm.getCurrentValidators(db, subnetID)
if err != nil {
return nil, nil, err
return nil, nil, nil, nil, err
}
if !subnetID.Equals(DefaultSubnetID) { // validators of default subnet removed in rewardValidatorTxs, not here
for current.Len() > 0 {
@ -678,11 +726,12 @@ func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, sub
break
}
current.Remove()
stopped.Add(next.Vdr().ID())
}
}
pending, err = vm.getPendingValidators(db, subnetID)
if err != nil {
return nil, nil, err
return nil, nil, nil, nil, err
}
for pending.Len() > 0 {
nextTx := pending.Peek() // pending staker with earliest start time
@ -691,8 +740,9 @@ func (vm *VM) calculateValidators(db database.Database, timestamp time.Time, sub
}
heap.Push(current, nextTx)
heap.Pop(pending)
started.Add(nextTx.Vdr().ID())
}
return current, pending, nil
return current, pending, started, stopped, nil
}
func (vm *VM) getValidators(validatorEvents *EventHeap) []validators.Validator {
@ -720,10 +770,12 @@ func (vm *VM) getValidators(validatorEvents *EventHeap) []validators.Validator {
return vdrList
}
// update the node's validator manager to contain the current validator set of the given Subnet
func (vm *VM) updateValidators(subnetID ids.ID) error {
validatorSet, ok := vm.validators.GetValidatorSet(subnetID)
if !ok {
return fmt.Errorf("couldn't get the validator sampler of the %s subnet", subnetID)
validatorSet, subnetInitialized := vm.validators.GetValidatorSet(subnetID)
if !subnetInitialized { // validator manager doesn't know about this subnet yet
validatorSet = validators.NewSet()
vm.validators.PutValidatorSet(subnetID, validatorSet)
}
currentValidators, err := vm.getCurrentValidators(vm.DB, subnetID)

View File

@ -10,6 +10,7 @@ import (
"testing"
"time"
"github.com/ava-labs/gecko/chains"
"github.com/ava-labs/gecko/chains/atomic"
"github.com/ava-labs/gecko/database/memdb"
"github.com/ava-labs/gecko/ids"
@ -39,16 +40,17 @@ var (
// each key corresponds to an account that has $AVA and a genesis validator
keys []*crypto.PrivateKeySECP256K1R
// amount all genesis validators stake
// amount all genesis validators stake in defaultVM
defaultStakeAmount uint64
// balance of accounts that exist at genesis
// balance of accounts that exist at genesis in defaultVM
defaultBalance = 100 * MinimumStakeAmount
// At genesis this account has AVA and is validating the default subnet
defaultKey *crypto.PrivateKeySECP256K1R
// non-default subnet that exists at genesis in defaultVM
// non-default Subnet that exists at genesis in defaultVM
// Its controlKeys are keys[0], keys[1], keys[2]
testSubnet1 *CreateSubnetTx
testSubnet1ControlKeys []*crypto.PrivateKeySECP256K1R
)
@ -116,7 +118,8 @@ func defaultVM() *VM {
}
vm := &VM{
SnowmanVM: &core.SnowmanVM{},
SnowmanVM: &core.SnowmanVM{},
chainManager: chains.MockManager{},
}
defaultSubnet := validators.NewSet()
@ -136,7 +139,7 @@ func defaultVM() *VM {
testNetworkID,
0,
[]ids.ShortID{keys[0].PublicKey().Address(), keys[1].PublicKey().Address(), keys[2].PublicKey().Address()}, // control keys are keys[0], keys[1], keys[2]
2, // 2 sigs from keys[0], keys[1], keys[2] needed to add validator to this subnet
2, // threshold; 2 sigs from keys[0], keys[1], keys[2] needed to add validator to this subnet
keys[0],
)
if err != nil {
@ -765,11 +768,13 @@ func TestCreateChain(t *testing.T) {
tx, err := vm.newCreateChainTx(
defaultNonce+1,
testSubnet1.id,
nil,
timestampvm.ID,
nil,
"name ",
"name",
testNetworkID,
[]*crypto.PrivateKeySECP256K1R{testSubnet1ControlKeys[0], testSubnet1ControlKeys[1]},
keys[0],
)
if err != nil {
@ -806,7 +811,7 @@ func TestCreateChain(t *testing.T) {
}
// Verify tx fee was deducted
account, err := vm.getAccount(vm.DB, tx.Key().Address())
account, err := vm.getAccount(vm.DB, tx.PayerAddress)
if err != nil {
t.Fatal(err)
}

View File

@ -74,10 +74,6 @@ func init() {
networkID, err := genesis.NetworkID(*networkName)
errs.Add(err)
if networkID != genesis.LocalID {
errs.Add(fmt.Errorf("the only supported networkID is: %s", genesis.LocalName))
}
config.NetworkID = networkID
// Remote: