mirror of https://github.com/poanetwork/gecko.git
Merge branch 'master' into list-assets
This commit is contained in:
commit
aedf398e05
|
@ -29,6 +29,7 @@ awscpu
|
||||||
*.ava
|
*.ava
|
||||||
|
|
||||||
db*
|
db*
|
||||||
|
|
||||||
*cpu[0-9]*
|
*cpu[0-9]*
|
||||||
*mem[0-9]*
|
*mem[0-9]*
|
||||||
*lock[0-9]*
|
*lock[0-9]*
|
||||||
|
@ -46,3 +47,8 @@ bin/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
keys/staker.*
|
keys/staker.*
|
||||||
|
|
||||||
|
!*.go
|
||||||
|
!*.proto
|
||||||
|
|
||||||
|
plugins/
|
|
@ -6,10 +6,6 @@ RUN apt-get update && apt-get install -y libssl-dev libuv1-dev curl cmake
|
||||||
|
|
||||||
RUN mkdir -p /go/src/github.com/ava-labs
|
RUN mkdir -p /go/src/github.com/ava-labs
|
||||||
|
|
||||||
# Because downloading ethereum takes long it is done separately, so that the docker
|
|
||||||
# layer, when cached can be re-used
|
|
||||||
RUN go get -t -v github.com/ava-labs/go-ethereum
|
|
||||||
|
|
||||||
WORKDIR $GOPATH/src/github.com/ava-labs/
|
WORKDIR $GOPATH/src/github.com/ava-labs/
|
||||||
COPY . gecko
|
COPY . gecko
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,8 @@ sudo apt-get install libssl-dev libuv1-dev cmake make curl g++
|
||||||
Clone the Gecko repository:
|
Clone the Gecko repository:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd $GOPATH
|
go get -v -d github.com/ava-labs/gecko/...
|
||||||
mkdir -p src/github.com/ava-labs
|
cd $GOPATH/src/github.com/ava-labs/gecko
|
||||||
cd src/github.com/ava-labs
|
|
||||||
git clone https://github.com/ava-labs/gecko.git
|
|
||||||
cd gecko
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Building the Gecko Executable
|
#### Building the Gecko Executable
|
||||||
|
|
|
@ -10,14 +10,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Peerable can return a group of peers
|
// Peerable can return a group of peers
|
||||||
type Peerable interface{ Peers() []utils.IPDesc }
|
type Peerable interface{ IPs() []utils.IPDesc }
|
||||||
|
|
||||||
// Networking provides helper methods for tracking the current network state
|
// Networking provides helper methods for tracking the current network state
|
||||||
type Networking struct{ peers Peerable }
|
type Networking struct{ peers Peerable }
|
||||||
|
|
||||||
// Peers returns the current peers
|
// Peers returns the current peers
|
||||||
func (n *Networking) Peers() ([]string, error) {
|
func (n *Networking) Peers() ([]string, error) {
|
||||||
ipDescs := n.peers.Peers()
|
ipDescs := n.peers.IPs()
|
||||||
ips := make([]string, len(ipDescs))
|
ips := make([]string, len(ipDescs))
|
||||||
for i, ipDesc := range ipDescs {
|
for i, ipDesc := range ipDescs {
|
||||||
ips[i] = ipDesc.String()
|
ips[i] = ipDesc.String()
|
||||||
|
|
|
@ -214,7 +214,12 @@ func (m *manager) ForceCreateChain(chain ChainParameters) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the chain
|
// Create the chain
|
||||||
vm := vmFactory.New()
|
vm, err := vmFactory.New()
|
||||||
|
if err != nil {
|
||||||
|
m.log.Error("error while creating vm: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: Shutdown VM if an error occurs
|
||||||
|
|
||||||
fxs := make([]*common.Fx, len(chain.FxAliases))
|
fxs := make([]*common.Fx, len(chain.FxAliases))
|
||||||
for i, fxAlias := range chain.FxAliases {
|
for i, fxAlias := range chain.FxAliases {
|
||||||
|
@ -231,10 +236,16 @@ func (m *manager) ForceCreateChain(chain ChainParameters) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fx, err := fxFactory.New()
|
||||||
|
if err != nil {
|
||||||
|
m.log.Error("error while creating fx: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Create the fx
|
// Create the fx
|
||||||
fxs[i] = &common.Fx{
|
fxs[i] = &common.Fx{
|
||||||
ID: fxID,
|
ID: fxID,
|
||||||
Fx: fxFactory.New(),
|
Fx: fx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package rpcdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/database"
|
||||||
|
"github.com/ava-labs/gecko/database/nodb"
|
||||||
|
"github.com/ava-labs/gecko/database/rpcdb/rpcdbproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errClosed = fmt.Sprintf("rpc error: code = Unknown desc = %s", database.ErrClosed)
|
||||||
|
errNotFound = fmt.Sprintf("rpc error: code = Unknown desc = %s", database.ErrNotFound)
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatabaseClient is an implementation of database that talks over RPC.
|
||||||
|
type DatabaseClient struct{ client rpcdbproto.DatabaseClient }
|
||||||
|
|
||||||
|
// NewClient returns a database instance connected to a remote database instance
|
||||||
|
func NewClient(client rpcdbproto.DatabaseClient) *DatabaseClient {
|
||||||
|
return &DatabaseClient{client: client}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has returns false, nil
|
||||||
|
func (db *DatabaseClient) Has(key []byte) (bool, error) {
|
||||||
|
resp, err := db.client.Has(context.Background(), &rpcdbproto.HasRequest{
|
||||||
|
Key: key,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, updateError(err)
|
||||||
|
}
|
||||||
|
return resp.Has, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns nil, error
|
||||||
|
func (db *DatabaseClient) Get(key []byte) ([]byte, error) {
|
||||||
|
resp, err := db.client.Get(context.Background(), &rpcdbproto.GetRequest{
|
||||||
|
Key: key,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, updateError(err)
|
||||||
|
}
|
||||||
|
return resp.Value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put returns nil
|
||||||
|
func (db *DatabaseClient) Put(key, value []byte) error {
|
||||||
|
_, err := db.client.Put(context.Background(), &rpcdbproto.PutRequest{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
return updateError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete returns nil
|
||||||
|
func (db *DatabaseClient) Delete(key []byte) error {
|
||||||
|
_, err := db.client.Delete(context.Background(), &rpcdbproto.DeleteRequest{
|
||||||
|
Key: key,
|
||||||
|
})
|
||||||
|
return updateError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBatch returns a new batch
|
||||||
|
func (db *DatabaseClient) NewBatch() database.Batch { return &batch{db: db} }
|
||||||
|
|
||||||
|
// NewIterator implements the Database interface
|
||||||
|
func (db *DatabaseClient) NewIterator() database.Iterator {
|
||||||
|
return db.NewIteratorWithStartAndPrefix(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIteratorWithStart implements the Database interface
|
||||||
|
func (db *DatabaseClient) NewIteratorWithStart(start []byte) database.Iterator {
|
||||||
|
return db.NewIteratorWithStartAndPrefix(start, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIteratorWithPrefix implements the Database interface
|
||||||
|
func (db *DatabaseClient) NewIteratorWithPrefix(prefix []byte) database.Iterator {
|
||||||
|
return db.NewIteratorWithStartAndPrefix(nil, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIteratorWithStartAndPrefix returns a new empty iterator
|
||||||
|
func (db *DatabaseClient) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
|
||||||
|
resp, err := db.client.NewIteratorWithStartAndPrefix(context.Background(), &rpcdbproto.NewIteratorWithStartAndPrefixRequest{
|
||||||
|
Start: start,
|
||||||
|
Prefix: prefix,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return &nodb.Iterator{Err: updateError(err)}
|
||||||
|
}
|
||||||
|
return &iterator{
|
||||||
|
db: db,
|
||||||
|
id: resp.Id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns an error
|
||||||
|
func (db *DatabaseClient) Stat(property string) (string, error) {
|
||||||
|
resp, err := db.client.Stat(context.Background(), &rpcdbproto.StatRequest{
|
||||||
|
Property: property,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", updateError(err)
|
||||||
|
}
|
||||||
|
return resp.Stat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact returns nil
|
||||||
|
func (db *DatabaseClient) Compact(start, limit []byte) error {
|
||||||
|
_, err := db.client.Compact(context.Background(), &rpcdbproto.CompactRequest{
|
||||||
|
Start: start,
|
||||||
|
Limit: limit,
|
||||||
|
})
|
||||||
|
return updateError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close returns nil
|
||||||
|
func (db *DatabaseClient) Close() error {
|
||||||
|
_, err := db.client.Close(context.Background(), &rpcdbproto.CloseRequest{})
|
||||||
|
return updateError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyValue struct {
|
||||||
|
key []byte
|
||||||
|
value []byte
|
||||||
|
delete bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type batch struct {
|
||||||
|
db *DatabaseClient
|
||||||
|
writes []keyValue
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *batch) Put(key, value []byte) error {
|
||||||
|
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
|
||||||
|
b.size += len(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *batch) Delete(key []byte) error {
|
||||||
|
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
|
||||||
|
b.size++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *batch) ValueSize() int { return b.size }
|
||||||
|
|
||||||
|
func (b *batch) Write() error {
|
||||||
|
request := &rpcdbproto.WriteBatchRequest{}
|
||||||
|
|
||||||
|
keySet := make(map[string]struct{}, len(b.writes))
|
||||||
|
for i := len(b.writes) - 1; i >= 0; i-- {
|
||||||
|
kv := b.writes[i]
|
||||||
|
key := string(kv.key)
|
||||||
|
if _, overwritten := keySet[key]; overwritten {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keySet[key] = struct{}{}
|
||||||
|
|
||||||
|
if kv.delete {
|
||||||
|
request.Deletes = append(request.Deletes, &rpcdbproto.DeleteRequest{
|
||||||
|
Key: kv.key,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
request.Puts = append(request.Puts, &rpcdbproto.PutRequest{
|
||||||
|
Key: kv.key,
|
||||||
|
Value: kv.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := b.db.client.WriteBatch(context.Background(), request)
|
||||||
|
return updateError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *batch) Reset() {
|
||||||
|
b.writes = b.writes[:0]
|
||||||
|
b.size = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *batch) Replay(w database.KeyValueWriter) error {
|
||||||
|
for _, keyvalue := range b.writes {
|
||||||
|
if keyvalue.delete {
|
||||||
|
if err := w.Delete(keyvalue.key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if err := w.Put(keyvalue.key, keyvalue.value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *batch) Inner() database.Batch { return b }
|
||||||
|
|
||||||
|
type iterator struct {
|
||||||
|
db *DatabaseClient
|
||||||
|
id uint64
|
||||||
|
key []byte
|
||||||
|
value []byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns false
|
||||||
|
func (it *iterator) Next() bool {
|
||||||
|
resp, err := it.db.client.IteratorNext(context.Background(), &rpcdbproto.IteratorNextRequest{
|
||||||
|
Id: it.id,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
it.err = err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.key = resp.Key
|
||||||
|
it.value = resp.Value
|
||||||
|
return resp.FoundNext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns any errors
|
||||||
|
func (it *iterator) Error() error {
|
||||||
|
if it.err != nil {
|
||||||
|
return it.err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := it.db.client.IteratorError(context.Background(), &rpcdbproto.IteratorErrorRequest{
|
||||||
|
Id: it.id,
|
||||||
|
})
|
||||||
|
it.err = updateError(err)
|
||||||
|
return it.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns nil
|
||||||
|
func (it *iterator) Key() []byte { return it.key }
|
||||||
|
|
||||||
|
// Value returns nil
|
||||||
|
func (it *iterator) Value() []byte { return it.value }
|
||||||
|
|
||||||
|
// Release does nothing
|
||||||
|
func (it *iterator) Release() {
|
||||||
|
it.db.client.IteratorRelease(context.Background(), &rpcdbproto.IteratorReleaseRequest{
|
||||||
|
Id: it.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyBytes(bytes []byte) []byte {
|
||||||
|
copiedBytes := make([]byte, len(bytes))
|
||||||
|
copy(copiedBytes, bytes)
|
||||||
|
return copiedBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateError(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch err.Error() {
|
||||||
|
case errClosed:
|
||||||
|
return database.ErrClosed
|
||||||
|
case errNotFound:
|
||||||
|
return database.ErrNotFound
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package rpcdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/database"
|
||||||
|
"github.com/ava-labs/gecko/database/rpcdb/rpcdbproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errUnknownIterator = errors.New("unknown iterator")
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatabaseServer is a database that is managed over RPC.
|
||||||
|
type DatabaseServer struct {
|
||||||
|
db database.Database
|
||||||
|
batch database.Batch
|
||||||
|
|
||||||
|
nextIteratorID uint64
|
||||||
|
iterators map[uint64]database.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer returns a database instance that is managed remotely
|
||||||
|
func NewServer(db database.Database) *DatabaseServer {
|
||||||
|
return &DatabaseServer{
|
||||||
|
db: db,
|
||||||
|
batch: db.NewBatch(),
|
||||||
|
iterators: make(map[uint64]database.Iterator),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has ...
|
||||||
|
func (db *DatabaseServer) Has(_ context.Context, req *rpcdbproto.HasRequest) (*rpcdbproto.HasResponse, error) {
|
||||||
|
has, err := db.db.Has(req.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &rpcdbproto.HasResponse{Has: has}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ...
|
||||||
|
func (db *DatabaseServer) Get(_ context.Context, req *rpcdbproto.GetRequest) (*rpcdbproto.GetResponse, error) {
|
||||||
|
value, err := db.db.Get(req.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &rpcdbproto.GetResponse{Value: value}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put ...
|
||||||
|
func (db *DatabaseServer) Put(_ context.Context, req *rpcdbproto.PutRequest) (*rpcdbproto.PutResponse, error) {
|
||||||
|
return &rpcdbproto.PutResponse{}, db.db.Put(req.Key, req.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete ...
|
||||||
|
func (db *DatabaseServer) Delete(_ context.Context, req *rpcdbproto.DeleteRequest) (*rpcdbproto.DeleteResponse, error) {
|
||||||
|
return &rpcdbproto.DeleteResponse{}, db.db.Delete(req.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat ...
|
||||||
|
func (db *DatabaseServer) Stat(_ context.Context, req *rpcdbproto.StatRequest) (*rpcdbproto.StatResponse, error) {
|
||||||
|
stat, err := db.db.Stat(req.Property)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &rpcdbproto.StatResponse{Stat: stat}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact ...
|
||||||
|
func (db *DatabaseServer) Compact(_ context.Context, req *rpcdbproto.CompactRequest) (*rpcdbproto.CompactResponse, error) {
|
||||||
|
return &rpcdbproto.CompactResponse{}, db.db.Compact(req.Start, req.Limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close ...
|
||||||
|
func (db *DatabaseServer) Close(_ context.Context, _ *rpcdbproto.CloseRequest) (*rpcdbproto.CloseResponse, error) {
|
||||||
|
return &rpcdbproto.CloseResponse{}, db.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteBatch ...
|
||||||
|
func (db *DatabaseServer) WriteBatch(_ context.Context, req *rpcdbproto.WriteBatchRequest) (*rpcdbproto.WriteBatchResponse, error) {
|
||||||
|
db.batch.Reset()
|
||||||
|
|
||||||
|
for _, put := range req.Puts {
|
||||||
|
if err := db.batch.Put(put.Key, put.Value); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, del := range req.Deletes {
|
||||||
|
if err := db.batch.Delete(del.Key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rpcdbproto.WriteBatchResponse{}, db.batch.Write()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIteratorWithStartAndPrefix ...
|
||||||
|
func (db *DatabaseServer) NewIteratorWithStartAndPrefix(_ context.Context, req *rpcdbproto.NewIteratorWithStartAndPrefixRequest) (*rpcdbproto.NewIteratorWithStartAndPrefixResponse, error) {
|
||||||
|
id := db.nextIteratorID
|
||||||
|
it := db.db.NewIteratorWithStartAndPrefix(req.Start, req.Prefix)
|
||||||
|
db.iterators[id] = it
|
||||||
|
|
||||||
|
db.nextIteratorID++
|
||||||
|
return &rpcdbproto.NewIteratorWithStartAndPrefixResponse{Id: id}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IteratorNext ...
|
||||||
|
func (db *DatabaseServer) IteratorNext(_ context.Context, req *rpcdbproto.IteratorNextRequest) (*rpcdbproto.IteratorNextResponse, error) {
|
||||||
|
it, exists := db.iterators[req.Id]
|
||||||
|
if !exists {
|
||||||
|
return nil, errUnknownIterator
|
||||||
|
}
|
||||||
|
return &rpcdbproto.IteratorNextResponse{
|
||||||
|
FoundNext: it.Next(),
|
||||||
|
Key: it.Key(),
|
||||||
|
Value: it.Value(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IteratorError ...
|
||||||
|
func (db *DatabaseServer) IteratorError(_ context.Context, req *rpcdbproto.IteratorErrorRequest) (*rpcdbproto.IteratorErrorResponse, error) {
|
||||||
|
it, exists := db.iterators[req.Id]
|
||||||
|
if !exists {
|
||||||
|
return nil, errUnknownIterator
|
||||||
|
}
|
||||||
|
return &rpcdbproto.IteratorErrorResponse{}, it.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IteratorRelease ...
|
||||||
|
func (db *DatabaseServer) IteratorRelease(_ context.Context, req *rpcdbproto.IteratorReleaseRequest) (*rpcdbproto.IteratorReleaseResponse, error) {
|
||||||
|
it, exists := db.iterators[req.Id]
|
||||||
|
if exists {
|
||||||
|
delete(db.iterators, req.Id)
|
||||||
|
it.Release()
|
||||||
|
}
|
||||||
|
return &rpcdbproto.IteratorReleaseResponse{}, nil
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package rpcdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/test/bufconn"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/database"
|
||||||
|
"github.com/ava-labs/gecko/database/memdb"
|
||||||
|
"github.com/ava-labs/gecko/database/rpcdb/rpcdbproto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bufSize = 1 << 20
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInterface(t *testing.T) {
|
||||||
|
for _, test := range database.Tests {
|
||||||
|
listener := bufconn.Listen(bufSize)
|
||||||
|
server := grpc.NewServer()
|
||||||
|
rpcdbproto.RegisterDatabaseServer(server, NewServer(memdb.New()))
|
||||||
|
go func() {
|
||||||
|
if err := server.Serve(listener); err != nil {
|
||||||
|
log.Fatalf("Server exited with error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
dialer := grpc.WithContextDialer(
|
||||||
|
func(context.Context, string) (net.Conn, error) {
|
||||||
|
return listener.Dial()
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
conn, err := grpc.DialContext(ctx, "", dialer, grpc.WithInsecure())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to dial: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := NewClient(rpcdbproto.NewDatabaseClient(conn))
|
||||||
|
test(t, db)
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,108 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package rpcdbproto;
|
||||||
|
|
||||||
|
message HasRequest {
|
||||||
|
bytes key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HasResponse {
|
||||||
|
bool has = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRequest {
|
||||||
|
bytes key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetResponse {
|
||||||
|
bytes value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PutRequest {
|
||||||
|
bytes key = 1;
|
||||||
|
bytes value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PutResponse {}
|
||||||
|
|
||||||
|
message DeleteRequest {
|
||||||
|
bytes key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteResponse {}
|
||||||
|
|
||||||
|
message StatRequest {
|
||||||
|
string property = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatResponse {
|
||||||
|
string stat = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CompactRequest {
|
||||||
|
bytes start = 1;
|
||||||
|
bytes limit = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CompactResponse {}
|
||||||
|
|
||||||
|
message CloseRequest {}
|
||||||
|
|
||||||
|
message CloseResponse {}
|
||||||
|
|
||||||
|
message WriteBatchRequest {
|
||||||
|
repeated PutRequest puts = 1;
|
||||||
|
repeated DeleteRequest deletes = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WriteBatchResponse {}
|
||||||
|
|
||||||
|
message NewIteratorRequest {}
|
||||||
|
|
||||||
|
message NewIteratorWithStartAndPrefixRequest {
|
||||||
|
bytes start = 1;
|
||||||
|
bytes prefix = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NewIteratorWithStartAndPrefixResponse {
|
||||||
|
uint64 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IteratorNextRequest {
|
||||||
|
uint64 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IteratorNextResponse {
|
||||||
|
bool foundNext = 1;
|
||||||
|
bytes key = 2;
|
||||||
|
bytes value = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IteratorErrorRequest {
|
||||||
|
uint64 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IteratorErrorResponse {}
|
||||||
|
|
||||||
|
message IteratorReleaseRequest {
|
||||||
|
uint64 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IteratorReleaseResponse {}
|
||||||
|
|
||||||
|
service Database {
|
||||||
|
rpc Has(HasRequest) returns (HasResponse);
|
||||||
|
rpc Get(GetRequest) returns (GetResponse);
|
||||||
|
rpc Put(PutRequest) returns (PutResponse);
|
||||||
|
rpc Delete(DeleteRequest) returns (DeleteResponse);
|
||||||
|
rpc Stat(StatRequest) returns (StatResponse);
|
||||||
|
rpc Compact(CompactRequest) returns (CompactResponse);
|
||||||
|
rpc Close(CloseRequest) returns (CloseResponse);
|
||||||
|
|
||||||
|
rpc WriteBatch(WriteBatchRequest) returns (WriteBatchResponse);
|
||||||
|
|
||||||
|
rpc NewIteratorWithStartAndPrefix(NewIteratorWithStartAndPrefixRequest) returns (NewIteratorWithStartAndPrefixResponse);
|
||||||
|
|
||||||
|
rpc IteratorNext(IteratorNextRequest) returns (IteratorNextResponse);
|
||||||
|
rpc IteratorError(IteratorErrorRequest) returns (IteratorErrorResponse);
|
||||||
|
rpc IteratorRelease(IteratorReleaseRequest) returns (IteratorReleaseResponse);
|
||||||
|
}
|
|
@ -16,6 +16,8 @@ var (
|
||||||
TestBatchPut,
|
TestBatchPut,
|
||||||
TestBatchDelete,
|
TestBatchDelete,
|
||||||
TestBatchReset,
|
TestBatchReset,
|
||||||
|
TestBatchReuse,
|
||||||
|
TestBatchRewrite,
|
||||||
TestBatchReplay,
|
TestBatchReplay,
|
||||||
TestBatchInner,
|
TestBatchInner,
|
||||||
TestIterator,
|
TestIterator,
|
||||||
|
@ -236,6 +238,105 @@ func TestBatchReset(t *testing.T, db Database) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBatchReuse ...
|
||||||
|
func TestBatchReuse(t *testing.T, db Database) {
|
||||||
|
key1 := []byte("hello1")
|
||||||
|
value1 := []byte("world1")
|
||||||
|
|
||||||
|
key2 := []byte("hello2")
|
||||||
|
value2 := []byte("world2")
|
||||||
|
|
||||||
|
batch := db.NewBatch()
|
||||||
|
if batch == nil {
|
||||||
|
t.Fatalf("db.NewBatch returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := batch.Put(key1, value1); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on batch.Put: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on batch.Write: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Delete(key1); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on database.Delete: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has, err := db.Has(key1); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on db.Has: %s", err)
|
||||||
|
} else if has {
|
||||||
|
t.Fatalf("db.Has unexpectedly returned true on key %s", key1)
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.Reset()
|
||||||
|
|
||||||
|
if err := batch.Put(key2, value2); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on batch.Put: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on batch.Write: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has, err := db.Has(key1); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on db.Has: %s", err)
|
||||||
|
} else if has {
|
||||||
|
t.Fatalf("db.Has unexpectedly returned true on key %s", key1)
|
||||||
|
} else if has, err := db.Has(key2); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on db.Has: %s", err)
|
||||||
|
} else if !has {
|
||||||
|
t.Fatalf("db.Has unexpectedly returned false on key %s", key2)
|
||||||
|
} else if v, err := db.Get(key2); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on db.Get: %s", err)
|
||||||
|
} else if !bytes.Equal(value2, v) {
|
||||||
|
t.Fatalf("db.Get: Returned: 0x%x ; Expected: 0x%x", v, value2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBatchRewrite ...
|
||||||
|
func TestBatchRewrite(t *testing.T, db Database) {
|
||||||
|
key := []byte("hello1")
|
||||||
|
value := []byte("world1")
|
||||||
|
|
||||||
|
batch := db.NewBatch()
|
||||||
|
if batch == nil {
|
||||||
|
t.Fatalf("db.NewBatch returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := batch.Put(key, value); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on batch.Put: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on batch.Write: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Delete(key); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on database.Delete: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has, err := db.Has(key); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on db.Has: %s", err)
|
||||||
|
} else if has {
|
||||||
|
t.Fatalf("db.Has unexpectedly returned true on key %s", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := batch.Write(); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on batch.Write: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has, err := db.Has(key); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on db.Has: %s", err)
|
||||||
|
} else if !has {
|
||||||
|
t.Fatalf("db.Has unexpectedly returned false on key %s", key)
|
||||||
|
} else if v, err := db.Get(key); err != nil {
|
||||||
|
t.Fatalf("Unexpected error on db.Get: %s", err)
|
||||||
|
} else if !bytes.Equal(value, v) {
|
||||||
|
t.Fatalf("db.Get: Returned: 0x%x ; Expected: 0x%x", v, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestBatchReplay ...
|
// TestBatchReplay ...
|
||||||
func TestBatchReplay(t *testing.T, db Database) {
|
func TestBatchReplay(t *testing.T, db Database) {
|
||||||
key1 := []byte("hello1")
|
key1 := []byte("hello1")
|
||||||
|
|
|
@ -6,7 +6,6 @@ package genesis
|
||||||
import (
|
import (
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/vms/avm"
|
"github.com/ava-labs/gecko/vms/avm"
|
||||||
"github.com/ava-labs/gecko/vms/evm"
|
|
||||||
"github.com/ava-labs/gecko/vms/nftfx"
|
"github.com/ava-labs/gecko/vms/nftfx"
|
||||||
"github.com/ava-labs/gecko/vms/platformvm"
|
"github.com/ava-labs/gecko/vms/platformvm"
|
||||||
"github.com/ava-labs/gecko/vms/propertyfx"
|
"github.com/ava-labs/gecko/vms/propertyfx"
|
||||||
|
@ -21,7 +20,7 @@ func Aliases(networkID uint32) (map[string][]string, map[[32]byte][]string, map[
|
||||||
generalAliases := map[string][]string{
|
generalAliases := map[string][]string{
|
||||||
"vm/" + platformvm.ID.String(): []string{"vm/platform"},
|
"vm/" + platformvm.ID.String(): []string{"vm/platform"},
|
||||||
"vm/" + avm.ID.String(): []string{"vm/avm"},
|
"vm/" + avm.ID.String(): []string{"vm/avm"},
|
||||||
"vm/" + evm.ID.String(): []string{"vm/evm"},
|
"vm/" + EVMID.String(): []string{"vm/evm"},
|
||||||
"vm/" + spdagvm.ID.String(): []string{"vm/spdag"},
|
"vm/" + spdagvm.ID.String(): []string{"vm/spdag"},
|
||||||
"vm/" + spchainvm.ID.String(): []string{"vm/spchain"},
|
"vm/" + spchainvm.ID.String(): []string{"vm/spchain"},
|
||||||
"vm/" + timestampvm.ID.String(): []string{"vm/timestamp"},
|
"vm/" + timestampvm.ID.String(): []string{"vm/timestamp"},
|
||||||
|
@ -33,7 +32,7 @@ func Aliases(networkID uint32) (map[string][]string, map[[32]byte][]string, map[
|
||||||
vmAliases := map[[32]byte][]string{
|
vmAliases := map[[32]byte][]string{
|
||||||
platformvm.ID.Key(): []string{"platform"},
|
platformvm.ID.Key(): []string{"platform"},
|
||||||
avm.ID.Key(): []string{"avm"},
|
avm.ID.Key(): []string{"avm"},
|
||||||
evm.ID.Key(): []string{"evm"},
|
EVMID.Key(): []string{"evm"},
|
||||||
spdagvm.ID.Key(): []string{"spdag"},
|
spdagvm.ID.Key(): []string{"spdag"},
|
||||||
spchainvm.ID.Key(): []string{"spchain"},
|
spchainvm.ID.Key(): []string{"spchain"},
|
||||||
timestampvm.ID.Key(): []string{"timestamp"},
|
timestampvm.ID.Key(): []string{"timestamp"},
|
||||||
|
@ -60,7 +59,7 @@ func Aliases(networkID uint32) (map[string][]string, map[[32]byte][]string, map[
|
||||||
case avm.ID.Equals(chain.VMID):
|
case avm.ID.Equals(chain.VMID):
|
||||||
generalAliases["bc/"+chain.ID().String()] = []string{"X", "avm", "bc/X", "bc/avm"}
|
generalAliases["bc/"+chain.ID().String()] = []string{"X", "avm", "bc/X", "bc/avm"}
|
||||||
chainAliases[chain.ID().Key()] = []string{"X", "avm"}
|
chainAliases[chain.ID().Key()] = []string{"X", "avm"}
|
||||||
case evm.ID.Equals(chain.VMID):
|
case EVMID.Equals(chain.VMID):
|
||||||
generalAliases["bc/"+chain.ID().String()] = []string{"C", "evm", "bc/C", "bc/evm"}
|
generalAliases["bc/"+chain.ID().String()] = []string{"C", "evm", "bc/C", "bc/evm"}
|
||||||
chainAliases[chain.ID().Key()] = []string{"C", "evm"}
|
chainAliases[chain.ID().Key()] = []string{"C", "evm"}
|
||||||
case spdagvm.ID.Equals(chain.VMID):
|
case spdagvm.ID.Equals(chain.VMID):
|
||||||
|
|
|
@ -5,7 +5,6 @@ package genesis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/vms/evm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Note that since an AVA network has exactly one Platform Chain,
|
// Note that since an AVA network has exactly one Platform Chain,
|
||||||
|
@ -16,8 +15,9 @@ import (
|
||||||
|
|
||||||
// Config contains the genesis addresses used to construct a genesis
|
// Config contains the genesis addresses used to construct a genesis
|
||||||
type Config struct {
|
type Config struct {
|
||||||
MintAddresses, FundedAddresses, FundedEVMAddresses, StakerIDs []string
|
MintAddresses, FundedAddresses, StakerIDs []string
|
||||||
ParsedMintAddresses, ParsedFundedAddresses, ParsedStakerIDs []ids.ShortID
|
ParsedMintAddresses, ParsedFundedAddresses, ParsedStakerIDs []ids.ShortID
|
||||||
|
EVMBytes []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) init() error {
|
func (c *Config) init() error {
|
||||||
|
@ -66,9 +66,6 @@ var (
|
||||||
"ZdhZv6oZrmXLyFDy6ovXAu6VxmbTsT2h",
|
"ZdhZv6oZrmXLyFDy6ovXAu6VxmbTsT2h",
|
||||||
"6cesTteH62Y5mLoDBUASaBvCXuL2AthL",
|
"6cesTteH62Y5mLoDBUASaBvCXuL2AthL",
|
||||||
},
|
},
|
||||||
FundedEVMAddresses: []string{
|
|
||||||
"0x572f4D80f10f663B5049F789546f25f70Bb62a7F",
|
|
||||||
},
|
|
||||||
StakerIDs: []string{
|
StakerIDs: []string{
|
||||||
"NX4zVkuiRJZYe6Nzzav7GXN3TakUet3Co",
|
"NX4zVkuiRJZYe6Nzzav7GXN3TakUet3Co",
|
||||||
"CMsa8cMw4eib1Hb8GG4xiUKAq5eE1BwUX",
|
"CMsa8cMw4eib1Hb8GG4xiUKAq5eE1BwUX",
|
||||||
|
@ -76,6 +73,98 @@ var (
|
||||||
"N86eodVZja3GEyZJTo3DFUPGpxEEvjGHs",
|
"N86eodVZja3GEyZJTo3DFUPGpxEEvjGHs",
|
||||||
"EkKeGSLUbHrrtuayBtbwgWDRUiAziC3ao",
|
"EkKeGSLUbHrrtuayBtbwgWDRUiAziC3ao",
|
||||||
},
|
},
|
||||||
|
EVMBytes: []byte{
|
||||||
|
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, 0x35, 0x37, 0x32,
|
||||||
|
0x66, 0x34, 0x64, 0x38, 0x30, 0x66, 0x31, 0x30,
|
||||||
|
0x66, 0x36, 0x36, 0x33, 0x62, 0x35, 0x30, 0x34,
|
||||||
|
0x39, 0x66, 0x37, 0x38, 0x39, 0x35, 0x34, 0x36,
|
||||||
|
0x66, 0x32, 0x35, 0x66, 0x37, 0x30, 0x62, 0x62,
|
||||||
|
0x36, 0x32, 0x61, 0x37, 0x66, 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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
DefaultConfig = Config{
|
DefaultConfig = Config{
|
||||||
MintAddresses: []string{},
|
MintAddresses: []string{},
|
||||||
|
@ -83,10 +172,6 @@ var (
|
||||||
// Private key: ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN
|
// Private key: ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN
|
||||||
"6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV",
|
"6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV",
|
||||||
},
|
},
|
||||||
FundedEVMAddresses: []string{
|
|
||||||
// Private key: evm.GenesisTestKey
|
|
||||||
evm.GenesisTestAddr,
|
|
||||||
},
|
|
||||||
StakerIDs: []string{
|
StakerIDs: []string{
|
||||||
"7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg",
|
"7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg",
|
||||||
"MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ",
|
"MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ",
|
||||||
|
@ -94,6 +179,98 @@ var (
|
||||||
"GWPcbFJZFfZreETSoWjPimr846mXEKCtu",
|
"GWPcbFJZFfZreETSoWjPimr846mXEKCtu",
|
||||||
"P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
|
"P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
|
||||||
},
|
},
|
||||||
|
EVMBytes: []byte{
|
||||||
|
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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,8 @@ package genesis
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"time"
|
"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/ids"
|
||||||
"github.com/ava-labs/gecko/utils/formatting"
|
"github.com/ava-labs/gecko/utils/formatting"
|
||||||
"github.com/ava-labs/gecko/utils/json"
|
"github.com/ava-labs/gecko/utils/json"
|
||||||
|
@ -21,7 +15,6 @@ import (
|
||||||
"github.com/ava-labs/gecko/utils/wrappers"
|
"github.com/ava-labs/gecko/utils/wrappers"
|
||||||
"github.com/ava-labs/gecko/vms/avm"
|
"github.com/ava-labs/gecko/vms/avm"
|
||||||
"github.com/ava-labs/gecko/vms/components/codec"
|
"github.com/ava-labs/gecko/vms/components/codec"
|
||||||
"github.com/ava-labs/gecko/vms/evm"
|
|
||||||
"github.com/ava-labs/gecko/vms/nftfx"
|
"github.com/ava-labs/gecko/vms/nftfx"
|
||||||
"github.com/ava-labs/gecko/vms/platformvm"
|
"github.com/ava-labs/gecko/vms/platformvm"
|
||||||
"github.com/ava-labs/gecko/vms/propertyfx"
|
"github.com/ava-labs/gecko/vms/propertyfx"
|
||||||
|
@ -31,6 +24,11 @@ import (
|
||||||
"github.com/ava-labs/gecko/vms/timestampvm"
|
"github.com/ava-labs/gecko/vms/timestampvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ID of the EVM VM
|
||||||
|
var (
|
||||||
|
EVMID = ids.NewID([32]byte{'e', 'v', 'm'})
|
||||||
|
)
|
||||||
|
|
||||||
// Genesis returns the genesis data of the Platform Chain.
|
// Genesis returns the genesis data of the Platform Chain.
|
||||||
// Since an AVA network has exactly one Platform Chain, and the Platform Chain
|
// Since an AVA network has exactly one Platform Chain, and the Platform Chain
|
||||||
// defines the genesis state of the network (who is staking, which chains exist,
|
// defines the genesis state of the network (who is staking, which chains exist,
|
||||||
|
@ -76,50 +74,6 @@ func FromConfig(networkID uint32, config *Config) ([]byte, error) {
|
||||||
|
|
||||||
avmSS := avm.StaticService{}
|
avmSS := avm.StaticService{}
|
||||||
err := avmSS.BuildGenesis(nil, &avmArgs, &avmReply)
|
err := avmSS.BuildGenesis(nil, &avmArgs, &avmReply)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
alloc := core.GenesisAlloc{}
|
|
||||||
for _, addr := range config.FundedEVMAddresses {
|
|
||||||
alloc[common.HexToAddress(addr)] = core.GenesisAccount{
|
|
||||||
Balance: evmBalance,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evmArgs := core.Genesis{
|
|
||||||
Config: ¶ms.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: alloc,
|
|
||||||
Number: 0,
|
|
||||||
GasUsed: 0,
|
|
||||||
ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
}
|
|
||||||
evmSS := evm.StaticService{}
|
|
||||||
evmReply, err := evmSS.BuildGenesis(nil, &evmArgs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -214,9 +168,9 @@ func FromConfig(networkID uint32, config *Config) ([]byte, error) {
|
||||||
Name: "X-Chain",
|
Name: "X-Chain",
|
||||||
},
|
},
|
||||||
platformvm.APIChain{
|
platformvm.APIChain{
|
||||||
GenesisData: evmReply,
|
GenesisData: formatting.CB58{Bytes: config.EVMBytes},
|
||||||
SubnetID: platformvm.DefaultSubnetID,
|
SubnetID: platformvm.DefaultSubnetID,
|
||||||
VMID: evm.ID,
|
VMID: EVMID,
|
||||||
Name: "C-Chain",
|
Name: "C-Chain",
|
||||||
},
|
},
|
||||||
platformvm.APIChain{
|
platformvm.APIChain{
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/vms/avm"
|
"github.com/ava-labs/gecko/vms/avm"
|
||||||
"github.com/ava-labs/gecko/vms/evm"
|
|
||||||
"github.com/ava-labs/gecko/vms/platformvm"
|
"github.com/ava-labs/gecko/vms/platformvm"
|
||||||
"github.com/ava-labs/gecko/vms/spchainvm"
|
"github.com/ava-labs/gecko/vms/spchainvm"
|
||||||
"github.com/ava-labs/gecko/vms/spdagvm"
|
"github.com/ava-labs/gecko/vms/spdagvm"
|
||||||
|
@ -97,7 +96,7 @@ func TestAliases(t *testing.T) {
|
||||||
t.Fatalf("Should have a custom alias from the vm")
|
t.Fatalf("Should have a custom alias from the vm")
|
||||||
} else if _, exists := generalAliases["vm/"+avm.ID.String()]; !exists {
|
} else if _, exists := generalAliases["vm/"+avm.ID.String()]; !exists {
|
||||||
t.Fatalf("Should have a custom alias from the vm")
|
t.Fatalf("Should have a custom alias from the vm")
|
||||||
} else if _, exists := generalAliases["vm/"+evm.ID.String()]; !exists {
|
} else if _, exists := generalAliases["vm/"+EVMID.String()]; !exists {
|
||||||
t.Fatalf("Should have a custom alias from the vm")
|
t.Fatalf("Should have a custom alias from the vm")
|
||||||
} else if _, exists := generalAliases["vm/"+spdagvm.ID.String()]; !exists {
|
} else if _, exists := generalAliases["vm/"+spdagvm.ID.String()]; !exists {
|
||||||
t.Fatalf("Should have a custom alias from the vm")
|
t.Fatalf("Should have a custom alias from the vm")
|
||||||
|
@ -135,12 +134,12 @@ func TestVMGenesis(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
networkID: CascadeID,
|
networkID: CascadeID,
|
||||||
vmID: evm.ID,
|
vmID: EVMID,
|
||||||
expectedID: "2mUYSXfLrDtigwbzj1LxKVsHwELghc5sisoXrzJwLqAAQHF4i",
|
expectedID: "2mUYSXfLrDtigwbzj1LxKVsHwELghc5sisoXrzJwLqAAQHF4i",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
networkID: LocalID,
|
networkID: LocalID,
|
||||||
vmID: evm.ID,
|
vmID: EVMID,
|
||||||
expectedID: "tZGm6RCkeGpVETUTp11DW3UYFZmm69zfqxchpHrSF7wgy8rmw",
|
expectedID: "tZGm6RCkeGpVETUTp11DW3UYFZmm69zfqxchpHrSF7wgy8rmw",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,9 +150,9 @@ func TestIDUnmarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestIDHex(t *testing.T) {
|
func TestIDHex(t *testing.T) {
|
||||||
id := NewID([32]byte{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'})
|
id := NewID([32]byte{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'})
|
||||||
expected := "617661206c61627300000000000000000000000000000000000000000000000000"
|
expected := "617661206c616273000000000000000000000000000000000000000000000000"
|
||||||
actual := id.Hex()
|
actual := id.Hex()
|
||||||
if actual != actual {
|
if actual != expected {
|
||||||
t.Fatalf("got %s, expected %s", actual, expected)
|
t.Fatalf("got %s, expected %s", actual, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
main/main.go
29
main/main.go
|
@ -7,10 +7,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/nat"
|
||||||
"github.com/ava-labs/gecko/node"
|
"github.com/ava-labs/gecko/node"
|
||||||
"github.com/ava-labs/gecko/utils/crypto"
|
"github.com/ava-labs/gecko/utils/crypto"
|
||||||
"github.com/ava-labs/gecko/utils/logging"
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
"github.com/ava-labs/go-ethereum/p2p/nat"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// main is the primary entry point to Ava. This can either create a CLI to an
|
// main is the primary entry point to Ava. This can either create a CLI to an
|
||||||
|
@ -40,6 +40,10 @@ func main() {
|
||||||
defer log.StopOnPanic()
|
defer log.StopOnPanic()
|
||||||
defer Config.DB.Close()
|
defer Config.DB.Close()
|
||||||
|
|
||||||
|
if Config.StakingIP.IsZero() {
|
||||||
|
log.Warn("NAT traversal has failed. If this node becomes a staker, it may lose its reward due to being unreachable.")
|
||||||
|
}
|
||||||
|
|
||||||
// Track if sybil control is enforced
|
// Track if sybil control is enforced
|
||||||
if !Config.EnableStaking {
|
if !Config.EnableStaking {
|
||||||
log.Warn("Staking and p2p encryption are disabled. Packet spoofing is possible.")
|
log.Warn("Staking and p2p encryption are disabled. Packet spoofing is possible.")
|
||||||
|
@ -61,26 +65,11 @@ func main() {
|
||||||
log.Warn("assertions are enabled. This may slow down execution")
|
log.Warn("assertions are enabled. This may slow down execution")
|
||||||
}
|
}
|
||||||
|
|
||||||
natChan := make(chan struct{})
|
mapper := nat.NewDefaultMapper(log, Config.Nat, nat.TCP, "gecko")
|
||||||
defer close(natChan)
|
defer mapper.UnmapAllPorts()
|
||||||
|
|
||||||
go nat.Map(
|
mapper.MapPort(Config.StakingIP.Port, Config.StakingIP.Port)
|
||||||
/*nat=*/ Config.Nat,
|
mapper.MapPort(Config.HTTPPort, Config.HTTPPort)
|
||||||
/*closeChannel=*/ natChan,
|
|
||||||
/*protocol=*/ "TCP",
|
|
||||||
/*internetPort=*/ int(Config.StakingIP.Port),
|
|
||||||
/*localPort=*/ int(Config.StakingIP.Port),
|
|
||||||
/*name=*/ "Gecko Staking Server",
|
|
||||||
)
|
|
||||||
|
|
||||||
go nat.Map(
|
|
||||||
/*nat=*/ Config.Nat,
|
|
||||||
/*closeChannel=*/ natChan,
|
|
||||||
/*protocol=*/ "TCP",
|
|
||||||
/*internetPort=*/ int(Config.HTTPPort),
|
|
||||||
/*localPort=*/ int(Config.HTTPPort),
|
|
||||||
/*name=*/ "Gecko HTTP Server",
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Debug("initializing node state")
|
log.Debug("initializing node state")
|
||||||
// MainNode is a global variable in the node.go file
|
// MainNode is a global variable in the node.go file
|
||||||
|
|
|
@ -12,12 +12,11 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/p2p/nat"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/database/leveldb"
|
"github.com/ava-labs/gecko/database/leveldb"
|
||||||
"github.com/ava-labs/gecko/database/memdb"
|
"github.com/ava-labs/gecko/database/memdb"
|
||||||
"github.com/ava-labs/gecko/genesis"
|
"github.com/ava-labs/gecko/genesis"
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
"github.com/ava-labs/gecko/nat"
|
||||||
"github.com/ava-labs/gecko/node"
|
"github.com/ava-labs/gecko/node"
|
||||||
"github.com/ava-labs/gecko/snow/networking/router"
|
"github.com/ava-labs/gecko/snow/networking/router"
|
||||||
"github.com/ava-labs/gecko/utils"
|
"github.com/ava-labs/gecko/utils"
|
||||||
|
@ -25,6 +24,12 @@ import (
|
||||||
"github.com/ava-labs/gecko/utils/hashing"
|
"github.com/ava-labs/gecko/utils/hashing"
|
||||||
"github.com/ava-labs/gecko/utils/logging"
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
"github.com/ava-labs/gecko/utils/wrappers"
|
"github.com/ava-labs/gecko/utils/wrappers"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dbVersion = "v0.2.0"
|
||||||
|
defaultDbDir = "~/.gecko/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Results of parsing the CLI
|
// Results of parsing the CLI
|
||||||
|
@ -36,6 +41,14 @@ var (
|
||||||
// GetIPs returns the default IPs for each network
|
// GetIPs returns the default IPs for each network
|
||||||
func GetIPs(networkID uint32) []string {
|
func GetIPs(networkID uint32) []string {
|
||||||
switch networkID {
|
switch networkID {
|
||||||
|
case genesis.CascadeID:
|
||||||
|
return []string{
|
||||||
|
"3.227.207.132:21001",
|
||||||
|
"34.207.133.167:21001",
|
||||||
|
"107.23.241.199:21001",
|
||||||
|
"54.197.215.186:21001",
|
||||||
|
"18.234.153.22:21001",
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -56,7 +69,7 @@ func init() {
|
||||||
fs := flag.NewFlagSet("gecko", flag.ContinueOnError)
|
fs := flag.NewFlagSet("gecko", flag.ContinueOnError)
|
||||||
|
|
||||||
// NetworkID:
|
// NetworkID:
|
||||||
networkName := fs.String("network-id", genesis.LocalName, "Network ID this node will connect to")
|
networkName := fs.String("network-id", genesis.CascadeName, "Network ID this node will connect to")
|
||||||
|
|
||||||
// Ava fees:
|
// Ava fees:
|
||||||
fs.Uint64Var(&Config.AvaTxFee, "ava-tx-fee", 0, "Ava transaction fee, in $nAva")
|
fs.Uint64Var(&Config.AvaTxFee, "ava-tx-fee", 0, "Ava transaction fee, in $nAva")
|
||||||
|
@ -69,7 +82,7 @@ func init() {
|
||||||
|
|
||||||
// Database:
|
// Database:
|
||||||
db := fs.Bool("db-enabled", true, "Turn on persistent storage")
|
db := fs.Bool("db-enabled", true, "Turn on persistent storage")
|
||||||
dbDir := fs.String("db-dir", "db", "Database directory for Ava state")
|
dbDir := fs.String("db-dir", defaultDbDir, "Database directory for Ava state")
|
||||||
|
|
||||||
// IP:
|
// IP:
|
||||||
consensusIP := fs.String("public-ip", "", "Public IP of this node")
|
consensusIP := fs.String("public-ip", "", "Public IP of this node")
|
||||||
|
@ -90,6 +103,9 @@ func init() {
|
||||||
fs.StringVar(&Config.StakingKeyFile, "staking-tls-key-file", "keys/staker.key", "TLS private key file for staking connections")
|
fs.StringVar(&Config.StakingKeyFile, "staking-tls-key-file", "keys/staker.key", "TLS private key file for staking connections")
|
||||||
fs.StringVar(&Config.StakingCertFile, "staking-tls-cert-file", "keys/staker.crt", "TLS certificate file for staking connections")
|
fs.StringVar(&Config.StakingCertFile, "staking-tls-cert-file", "keys/staker.crt", "TLS certificate file for staking connections")
|
||||||
|
|
||||||
|
// Plugins:
|
||||||
|
fs.StringVar(&Config.PluginDir, "plugin-dir", "./build/plugins", "Plugin directory for Ava VMs")
|
||||||
|
|
||||||
// Logging:
|
// Logging:
|
||||||
logsDir := fs.String("log-dir", "", "Logging directory for Ava")
|
logsDir := fs.String("log-dir", "", "Logging directory for Ava")
|
||||||
logLevel := fs.String("log-level", "info", "The log level. Should be one of {verbo, debug, info, warn, error, fatal, off}")
|
logLevel := fs.String("log-level", "info", "The log level. Should be one of {verbo, debug, info, warn, error, fatal, off}")
|
||||||
|
@ -133,7 +149,12 @@ func init() {
|
||||||
// DB:
|
// DB:
|
||||||
if *db && err == nil {
|
if *db && err == nil {
|
||||||
// TODO: Add better params here
|
// TODO: Add better params here
|
||||||
dbPath := path.Join(*dbDir, genesis.NetworkName(Config.NetworkID))
|
if *dbDir == defaultDbDir {
|
||||||
|
if *dbDir, err = homedir.Expand(defaultDbDir); err != nil {
|
||||||
|
errs.Add(fmt.Errorf("couldn't resolve default db path: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dbPath := path.Join(*dbDir, genesis.NetworkName(Config.NetworkID), dbVersion)
|
||||||
db, err := leveldb.New(dbPath, 0, 0, 0)
|
db, err := leveldb.New(dbPath, 0, 0, 0)
|
||||||
Config.DB = db
|
Config.DB = db
|
||||||
errs.Add(err)
|
errs.Add(err)
|
||||||
|
@ -141,17 +162,15 @@ func init() {
|
||||||
Config.DB = memdb.New()
|
Config.DB = memdb.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.Nat = nat.Any()
|
Config.Nat = nat.NewRouter()
|
||||||
|
|
||||||
var ip net.IP
|
var ip net.IP
|
||||||
// If public IP is not specified, get it using shell command dig
|
// If public IP is not specified, get it using shell command dig
|
||||||
if *consensusIP == "" {
|
if *consensusIP == "" {
|
||||||
ip, err = Config.Nat.ExternalIP()
|
ip, err = Config.Nat.IP()
|
||||||
errs.Add(fmt.Errorf(
|
if err != nil {
|
||||||
"%s\n"+
|
ip = net.IPv4zero
|
||||||
"If you are trying to create a local network, try adding --public-ip=127.0.0.1\n"+
|
}
|
||||||
"If you are attempting to connect to a public network, you may need to manually report your IP and perform port forwarding",
|
|
||||||
err))
|
|
||||||
} else {
|
} else {
|
||||||
ip = net.ParseIP(*consensusIP)
|
ip = net.ParseIP(*consensusIP)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package nat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
|
"github.com/ava-labs/gecko/utils/wrappers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultMappingTimeout = 30 * time.Minute
|
||||||
|
defaultMappingUpdateInterval = 3 * defaultMappingTimeout / 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mapper maps port
|
||||||
|
type Mapper interface {
|
||||||
|
MapPort(newInternalPort, newExternalPort uint16) error
|
||||||
|
UnmapAllPorts() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapper struct {
|
||||||
|
log logging.Logger
|
||||||
|
router Router
|
||||||
|
networkProtocol NetworkProtocol
|
||||||
|
mappingNames string
|
||||||
|
mappingTimeout time.Duration
|
||||||
|
mappingUpdateInterval time.Duration
|
||||||
|
|
||||||
|
closer chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
|
errLock sync.Mutex
|
||||||
|
errs wrappers.Errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMapper returns a new mapper that can map ports on a router
|
||||||
|
func NewMapper(
|
||||||
|
log logging.Logger,
|
||||||
|
router Router,
|
||||||
|
networkProtocol NetworkProtocol,
|
||||||
|
mappingNames string,
|
||||||
|
mappingTimeout time.Duration,
|
||||||
|
mappingUpdateInterval time.Duration,
|
||||||
|
) Mapper {
|
||||||
|
return &mapper{
|
||||||
|
log: log,
|
||||||
|
router: router,
|
||||||
|
networkProtocol: networkProtocol,
|
||||||
|
mappingNames: mappingNames,
|
||||||
|
mappingTimeout: mappingTimeout,
|
||||||
|
mappingUpdateInterval: mappingUpdateInterval,
|
||||||
|
closer: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultMapper returns a new mapper that can map ports on a router with
|
||||||
|
// default settings
|
||||||
|
func NewDefaultMapper(
|
||||||
|
log logging.Logger,
|
||||||
|
router Router,
|
||||||
|
networkProtocol NetworkProtocol,
|
||||||
|
mappingNames string,
|
||||||
|
) Mapper {
|
||||||
|
return NewMapper(
|
||||||
|
log,
|
||||||
|
router,
|
||||||
|
networkProtocol,
|
||||||
|
mappingNames,
|
||||||
|
defaultMappingTimeout, // uses the default value
|
||||||
|
defaultMappingUpdateInterval, // uses the default value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapPort maps a local port to a port on the router until UnmapAllPorts is
|
||||||
|
// called.
|
||||||
|
func (m *mapper) MapPort(newInternalPort, newExternalPort uint16) error {
|
||||||
|
m.wg.Add(1)
|
||||||
|
go m.mapPort(newInternalPort, newExternalPort)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mapper) mapPort(newInternalPort, newExternalPort uint16) {
|
||||||
|
// duration is set to 0 here so that the select case will execute
|
||||||
|
// immediately
|
||||||
|
updateTimer := time.NewTimer(0)
|
||||||
|
defer func() {
|
||||||
|
updateTimer.Stop()
|
||||||
|
|
||||||
|
m.errLock.Lock()
|
||||||
|
m.errs.Add(m.router.UnmapPort(
|
||||||
|
m.networkProtocol,
|
||||||
|
newInternalPort,
|
||||||
|
newExternalPort))
|
||||||
|
m.errLock.Unlock()
|
||||||
|
|
||||||
|
m.log.Debug("Unmapped external port %d to internal port %d",
|
||||||
|
newExternalPort,
|
||||||
|
newInternalPort)
|
||||||
|
|
||||||
|
m.wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-updateTimer.C:
|
||||||
|
err := m.router.MapPort(
|
||||||
|
m.networkProtocol,
|
||||||
|
newInternalPort,
|
||||||
|
newExternalPort,
|
||||||
|
m.mappingNames,
|
||||||
|
m.mappingTimeout)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
m.errLock.Lock()
|
||||||
|
m.errs.Add(err)
|
||||||
|
m.errLock.Unlock()
|
||||||
|
|
||||||
|
m.log.Debug("Failed to add mapping from external port %d to internal port %d due to %s",
|
||||||
|
newExternalPort,
|
||||||
|
newInternalPort,
|
||||||
|
err)
|
||||||
|
} else {
|
||||||
|
m.log.Debug("Mapped external port %d to internal port %d",
|
||||||
|
newExternalPort,
|
||||||
|
newInternalPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remap the port in m.mappingUpdateInterval
|
||||||
|
updateTimer.Reset(m.mappingUpdateInterval)
|
||||||
|
case _, _ = <-m.closer:
|
||||||
|
return // only return when all ports are unmapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mapper) UnmapAllPorts() error {
|
||||||
|
close(m.closer)
|
||||||
|
m.wg.Wait()
|
||||||
|
return m.errs.Err
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package nat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoRouter = errors.New("no nat enabled router was discovered")
|
||||||
|
)
|
||||||
|
|
||||||
|
type noRouter struct{}
|
||||||
|
|
||||||
|
func (noRouter) MapPort(_ NetworkProtocol, _, _ uint16, _ string, _ time.Duration) error {
|
||||||
|
return errNoRouter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (noRouter) UnmapPort(_ NetworkProtocol, _, _ uint16) error {
|
||||||
|
return errNoRouter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (noRouter) IP() (net.IP, error) {
|
||||||
|
return nil, errNoRouter
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package nat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackpal/gateway"
|
||||||
|
"github.com/jackpal/go-nat-pmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pmpClientTimeout = 500 * time.Millisecond
|
||||||
|
)
|
||||||
|
|
||||||
|
// natPMPClient adapts the NAT-PMP protocol implementation so it conforms to
|
||||||
|
// the common interface.
|
||||||
|
type pmpClient struct {
|
||||||
|
client *natpmp.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pmp *pmpClient) MapPort(
|
||||||
|
networkProtocol NetworkProtocol,
|
||||||
|
newInternalPort uint16,
|
||||||
|
newExternalPort uint16,
|
||||||
|
mappingName string,
|
||||||
|
mappingDuration time.Duration) error {
|
||||||
|
protocol := string(networkProtocol)
|
||||||
|
internalPort := int(newInternalPort)
|
||||||
|
externalPort := int(newExternalPort)
|
||||||
|
// go-nat-pmp uses seconds to denote their lifetime
|
||||||
|
lifetime := int(mappingDuration / time.Second)
|
||||||
|
|
||||||
|
_, err := pmp.client.AddPortMapping(protocol, internalPort, externalPort, lifetime)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pmp *pmpClient) UnmapPort(
|
||||||
|
networkProtocol NetworkProtocol,
|
||||||
|
internalPort uint16,
|
||||||
|
_ uint16) error {
|
||||||
|
protocol := string(networkProtocol)
|
||||||
|
internalPortInt := int(internalPort)
|
||||||
|
|
||||||
|
_, err := pmp.client.AddPortMapping(protocol, internalPortInt, 0, 0)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pmp *pmpClient) IP() (net.IP, error) {
|
||||||
|
response, err := pmp.client.GetExternalAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return response.ExternalIPAddress[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPMPRouter() Router {
|
||||||
|
gatewayIP, err := gateway.DiscoverGateway()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pmp := &pmpClient{natpmp.NewClientWithTimeout(gatewayIP, pmpClientTimeout)}
|
||||||
|
if _, err := pmp.IP(); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pmp
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
// Package nat performs network address translation and provides helpers for
|
||||||
|
// routing ports.
|
||||||
|
package nat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetworkProtocol is a protocol that will be used through a port
|
||||||
|
type NetworkProtocol string
|
||||||
|
|
||||||
|
// Available protocol
|
||||||
|
const (
|
||||||
|
TCP NetworkProtocol = "TCP"
|
||||||
|
UDP NetworkProtocol = "UDP"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Router provides a standard NAT router functions. Specifically, allowing the
|
||||||
|
// fetching of public IPs and port forwarding to this computer.
|
||||||
|
type Router interface {
|
||||||
|
// mapPort creates a mapping between a port on the local computer to an
|
||||||
|
// external port on the router.
|
||||||
|
//
|
||||||
|
// The mappingName is something displayed on the router, so it is included
|
||||||
|
// for completeness.
|
||||||
|
MapPort(
|
||||||
|
networkProtocol NetworkProtocol,
|
||||||
|
newInternalPort uint16,
|
||||||
|
newExternalPort uint16,
|
||||||
|
mappingName string,
|
||||||
|
mappingDuration time.Duration) error
|
||||||
|
|
||||||
|
// UnmapPort clears a mapping that was previous made by a call to MapPort
|
||||||
|
UnmapPort(
|
||||||
|
networkProtocol NetworkProtocol,
|
||||||
|
internalPort uint16,
|
||||||
|
externalPort uint16) error
|
||||||
|
|
||||||
|
// Returns the routers IP address on the network the router considers
|
||||||
|
// external
|
||||||
|
IP() (net.IP, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRouter returns a new router discovered on the local network
|
||||||
|
func NewRouter() Router {
|
||||||
|
routers := make(chan Router)
|
||||||
|
// Because getting a router can take a noticeable amount of time to error,
|
||||||
|
// we run these requests in parallel
|
||||||
|
go func() {
|
||||||
|
routers <- getUPnPRouter()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
routers <- getPMPRouter()
|
||||||
|
}()
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
if router := <-routers; router != nil {
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noRouter{}
|
||||||
|
}
|
|
@ -0,0 +1,253 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package nat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/huin/goupnp"
|
||||||
|
"github.com/huin/goupnp/dcps/internetgateway1"
|
||||||
|
"github.com/huin/goupnp/dcps/internetgateway2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
soapTimeout = time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoGateway = errors.New("Failed to connect to any avaliable gateways")
|
||||||
|
)
|
||||||
|
|
||||||
|
// upnpClient is the interface used by goupnp for their client implementations
|
||||||
|
type upnpClient interface {
|
||||||
|
// attempts to map connection using the provided protocol from the external
|
||||||
|
// port to the internal port for the lease duration.
|
||||||
|
AddPortMapping(
|
||||||
|
newRemoteHost string,
|
||||||
|
newExternalPort uint16,
|
||||||
|
newProtocol string,
|
||||||
|
newInternalPort uint16,
|
||||||
|
newInternalClient string,
|
||||||
|
newEnabled bool,
|
||||||
|
newPortMappingDescription string,
|
||||||
|
newLeaseDuration uint32) error
|
||||||
|
|
||||||
|
// attempt to remove any mapping from the external port.
|
||||||
|
DeletePortMapping(
|
||||||
|
newRemoteHost string,
|
||||||
|
newExternalPort uint16,
|
||||||
|
newProtocol string) error
|
||||||
|
|
||||||
|
// attempts to return the external IP address, formatted as a string.
|
||||||
|
GetExternalIPAddress() (ip string, err error)
|
||||||
|
|
||||||
|
// returns if there is rsip available, nat enabled, or an unexpected error.
|
||||||
|
GetNATRSIPStatus() (newRSIPAvailable bool, natEnabled bool, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type upnpRouter struct {
|
||||||
|
root *goupnp.RootDevice
|
||||||
|
client upnpClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *upnpRouter) MapPort(
|
||||||
|
networkProtocol NetworkProtocol,
|
||||||
|
newInternalPort uint16,
|
||||||
|
newExternalPort uint16,
|
||||||
|
mappingName string,
|
||||||
|
mappingDuration time.Duration,
|
||||||
|
) error {
|
||||||
|
ip, err := n.localAddress()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol := string(networkProtocol)
|
||||||
|
// goupnp uses seconds to denote their lifetime
|
||||||
|
lifetime := uint32(mappingDuration / time.Second)
|
||||||
|
|
||||||
|
// UnmapPort's error is intentionally dropped, because the mapping may not
|
||||||
|
// exist.
|
||||||
|
n.UnmapPort(networkProtocol, newInternalPort, newExternalPort)
|
||||||
|
|
||||||
|
return n.client.AddPortMapping(
|
||||||
|
"", // newRemoteHost isn't used to limit the mapping to a host
|
||||||
|
newExternalPort,
|
||||||
|
protocol,
|
||||||
|
newInternalPort,
|
||||||
|
ip.String(), // newInternalClient is the client traffic should be sent to
|
||||||
|
true, // newEnabled enables port mappings
|
||||||
|
mappingName,
|
||||||
|
lifetime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *upnpRouter) UnmapPort(networkProtocol NetworkProtocol, _, externalPort uint16) error {
|
||||||
|
protocol := string(networkProtocol)
|
||||||
|
return n.client.DeletePortMapping(
|
||||||
|
"", // newRemoteHost isn't used to limit the mapping to a host
|
||||||
|
externalPort,
|
||||||
|
protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *upnpRouter) IP() (net.IP, error) {
|
||||||
|
ipStr, err := n.client.GetExternalIPAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(ipStr)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, fmt.Errorf("invalid IP %s", ipStr)
|
||||||
|
}
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *upnpRouter) localAddress() (net.IP, error) {
|
||||||
|
// attempt to get an address on the router
|
||||||
|
deviceAddr, err := net.ResolveUDPAddr("udp4", n.root.URLBase.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
deviceIP := deviceAddr.IP
|
||||||
|
|
||||||
|
netInterfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to find one of my ips that the router would know about
|
||||||
|
for _, netInterface := range netInterfaces {
|
||||||
|
addrs, err := netInterface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
// this is pretty janky, but it seems to be the best way to get the
|
||||||
|
// ip mask and properly check if the ip references the device we are
|
||||||
|
// connected to
|
||||||
|
ipNet, ok := addr.(*net.IPNet)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipNet.Contains(deviceIP) {
|
||||||
|
return ipNet.IP, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("couldn't find the local address in the same network as %s", deviceIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUPnPRouter searches for all Gateway Devices that have avaliable
|
||||||
|
// connections in the goupnp library and returns the first connection it can
|
||||||
|
// find.
|
||||||
|
func getUPnPRouter() Router {
|
||||||
|
routers := make(chan *upnpRouter)
|
||||||
|
// Because DiscoverDevices takes a noticeable amount of time to error, we
|
||||||
|
// run these requests in parallel
|
||||||
|
go func() {
|
||||||
|
routers <- connectToGateway(internetgateway1.URN_WANConnectionDevice_1, gateway1)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
routers <- connectToGateway(internetgateway2.URN_WANConnectionDevice_2, gateway2)
|
||||||
|
}()
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
if router := <-routers; router != nil {
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gateway1(client goupnp.ServiceClient) upnpClient {
|
||||||
|
switch client.Service.ServiceType {
|
||||||
|
case internetgateway1.URN_WANIPConnection_1:
|
||||||
|
return &internetgateway1.WANIPConnection1{ServiceClient: client}
|
||||||
|
case internetgateway1.URN_WANPPPConnection_1:
|
||||||
|
return &internetgateway1.WANPPPConnection1{ServiceClient: client}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gateway2(client goupnp.ServiceClient) upnpClient {
|
||||||
|
switch client.Service.ServiceType {
|
||||||
|
case internetgateway2.URN_WANIPConnection_1:
|
||||||
|
return &internetgateway2.WANIPConnection1{ServiceClient: client}
|
||||||
|
case internetgateway2.URN_WANIPConnection_2:
|
||||||
|
return &internetgateway2.WANIPConnection2{ServiceClient: client}
|
||||||
|
case internetgateway2.URN_WANPPPConnection_1:
|
||||||
|
return &internetgateway2.WANPPPConnection1{ServiceClient: client}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectToGateway(deviceType string, toClient func(goupnp.ServiceClient) upnpClient) *upnpRouter {
|
||||||
|
devs, err := goupnp.DiscoverDevices(deviceType)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// we are iterating over all the network devices, acting a possible roots
|
||||||
|
for i := range devs {
|
||||||
|
dev := &devs[i]
|
||||||
|
if dev.Root == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// the root device may be a router, so attempt to connect to that
|
||||||
|
rootDevice := &dev.Root.Device
|
||||||
|
if upnp := getRouter(dev, rootDevice, toClient); upnp != nil {
|
||||||
|
return upnp
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to connect to any sub devices
|
||||||
|
devices := rootDevice.Devices
|
||||||
|
for i := range devices {
|
||||||
|
if upnp := getRouter(dev, &devices[i], toClient); upnp != nil {
|
||||||
|
return upnp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRouter(rootDevice *goupnp.MaybeRootDevice, device *goupnp.Device, toClient func(goupnp.ServiceClient) upnpClient) *upnpRouter {
|
||||||
|
for i := range device.Services {
|
||||||
|
service := &device.Services[i]
|
||||||
|
|
||||||
|
soapClient := service.NewSOAPClient()
|
||||||
|
// make sure the client times out if needed
|
||||||
|
soapClient.HTTPClient.Timeout = soapTimeout
|
||||||
|
|
||||||
|
// attempt to create a client connection
|
||||||
|
serviceClient := goupnp.ServiceClient{
|
||||||
|
SOAPClient: soapClient,
|
||||||
|
RootDevice: rootDevice.Root,
|
||||||
|
Location: rootDevice.Location,
|
||||||
|
Service: service,
|
||||||
|
}
|
||||||
|
client := toClient(serviceClient)
|
||||||
|
if client == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether port mapping is enabled
|
||||||
|
if _, nat, err := client.GetNATRSIPStatus(); err != nil || !nat {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found a router!
|
||||||
|
return &upnpRouter{
|
||||||
|
root: rootDevice.Root,
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,288 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package networking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ava-labs/salticidae-go"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/ava-labs/gecko/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Connections provides an interface for what a group of connections will
|
|
||||||
// support.
|
|
||||||
type Connections interface {
|
|
||||||
Add(salticidae.NetAddr, ids.ShortID)
|
|
||||||
|
|
||||||
GetIP(ids.ShortID) (salticidae.NetAddr, bool)
|
|
||||||
GetID(salticidae.NetAddr) (ids.ShortID, bool)
|
|
||||||
|
|
||||||
ContainsIP(salticidae.NetAddr) bool
|
|
||||||
ContainsID(ids.ShortID) bool
|
|
||||||
|
|
||||||
Remove(salticidae.NetAddr, ids.ShortID)
|
|
||||||
RemoveIP(salticidae.NetAddr)
|
|
||||||
RemoveID(ids.ShortID)
|
|
||||||
|
|
||||||
Peers() []utils.IPDesc
|
|
||||||
IPs() []salticidae.NetAddr
|
|
||||||
IDs() ids.ShortSet
|
|
||||||
Conns() ([]utils.IPDesc, []ids.ShortID)
|
|
||||||
RawConns() ([]salticidae.NetAddr, []ids.ShortID)
|
|
||||||
|
|
||||||
Len() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddrCert implements the Connections interface
|
|
||||||
type AddrCert struct {
|
|
||||||
// ip -> id
|
|
||||||
ipToID map[uint64]ids.ShortID
|
|
||||||
// id -> ip
|
|
||||||
idToIP map[[20]byte]salticidae.NetAddr
|
|
||||||
mux sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Assumes that addr is garbage collected normally
|
|
||||||
func (ac *AddrCert) Add(ip salticidae.NetAddr, id ids.ShortID) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
ac.add(ip, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIP returns the ip mapped to the id that is provided if one exists.
|
|
||||||
func (ac *AddrCert) GetIP(id ids.ShortID) (salticidae.NetAddr, bool) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.getIP(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetID returns the id mapped to the ip that is provided if one exists.
|
|
||||||
func (ac *AddrCert) GetID(ip salticidae.NetAddr) (ids.ShortID, bool) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.getID(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainsIP returns true if the ip is contained in the connection pool
|
|
||||||
func (ac *AddrCert) ContainsIP(ip salticidae.NetAddr) bool {
|
|
||||||
_, exists := ac.GetID(ip)
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainsID returns true if the id is contained in the connection pool
|
|
||||||
func (ac *AddrCert) ContainsID(id ids.ShortID) bool {
|
|
||||||
_, exists := ac.GetIP(id)
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove ensures that no connection will have any mapping containing [ip] or
|
|
||||||
// [id].
|
|
||||||
func (ac *AddrCert) Remove(ip salticidae.NetAddr, id ids.ShortID) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
ac.remove(ip, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveIP ensures that no connection will have a mapping containing [ip]
|
|
||||||
func (ac *AddrCert) RemoveIP(ip salticidae.NetAddr) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
ac.removeIP(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveID ensures that no connection will have a mapping containing [id]
|
|
||||||
func (ac *AddrCert) RemoveID(id ids.ShortID) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
ac.removeID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Peers returns the full list of ips contained in this connection pool.
|
|
||||||
func (ac *AddrCert) Peers() []utils.IPDesc {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.peers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPs returns the full list of ips contained in this connection pool. This can
|
|
||||||
// be useful for gossiping a node's connections through the network.
|
|
||||||
func (ac *AddrCert) IPs() []salticidae.NetAddr {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.ips()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDs return the set of IDs that are mapping in this connection pool.
|
|
||||||
func (ac *AddrCert) IDs() ids.ShortSet {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.ids()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conns return the set of connections in this connection pool.
|
|
||||||
func (ac *AddrCert) Conns() ([]utils.IPDesc, []ids.ShortID) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.conns()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawConns return the set of connections in this connection pool.
|
|
||||||
func (ac *AddrCert) RawConns() ([]salticidae.NetAddr, []ids.ShortID) {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.rawConns()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of elements in the map
|
|
||||||
func (ac *AddrCert) Len() int {
|
|
||||||
ac.mux.Lock()
|
|
||||||
defer ac.mux.Unlock()
|
|
||||||
|
|
||||||
return ac.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) init() {
|
|
||||||
if ac.ipToID == nil {
|
|
||||||
ac.ipToID = make(map[uint64]ids.ShortID)
|
|
||||||
}
|
|
||||||
if ac.idToIP == nil {
|
|
||||||
ac.idToIP = make(map[[20]byte]salticidae.NetAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) add(ip salticidae.NetAddr, id ids.ShortID) {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ac.removeIP(ip)
|
|
||||||
ac.removeID(id)
|
|
||||||
|
|
||||||
ac.ipToID[addrToID(ip)] = id
|
|
||||||
ac.idToIP[id.Key()] = ip
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) getIP(id ids.ShortID) (salticidae.NetAddr, bool) {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ip, exists := ac.idToIP[id.Key()]
|
|
||||||
return ip, exists
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) getID(ip salticidae.NetAddr) (ids.ShortID, bool) {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
id, exists := ac.ipToID[addrToID(ip)]
|
|
||||||
return id, exists
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) remove(ip salticidae.NetAddr, id ids.ShortID) {
|
|
||||||
ac.removeIP(ip)
|
|
||||||
ac.removeID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) removeIP(ip salticidae.NetAddr) {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ipID := addrToID(ip)
|
|
||||||
if id, exists := ac.ipToID[ipID]; exists {
|
|
||||||
delete(ac.ipToID, ipID)
|
|
||||||
delete(ac.idToIP, id.Key())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) removeID(id ids.ShortID) {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
idKey := id.Key()
|
|
||||||
if ip, exists := ac.idToIP[idKey]; exists {
|
|
||||||
delete(ac.ipToID, addrToID(ip))
|
|
||||||
delete(ac.idToIP, idKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) peers() []utils.IPDesc {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ips := []utils.IPDesc(nil)
|
|
||||||
for _, ip := range ac.idToIP {
|
|
||||||
ips = append(ips, toIPDesc(ip))
|
|
||||||
}
|
|
||||||
return ips
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) ips() []salticidae.NetAddr {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ips := []salticidae.NetAddr(nil)
|
|
||||||
for _, ip := range ac.idToIP {
|
|
||||||
ips = append(ips, ip)
|
|
||||||
}
|
|
||||||
return ips
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) ids() ids.ShortSet {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ids := ids.ShortSet{}
|
|
||||||
for _, id := range ac.ipToID {
|
|
||||||
ids.Add(id)
|
|
||||||
}
|
|
||||||
return ids
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) conns() ([]utils.IPDesc, []ids.ShortID) {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ipList := []utils.IPDesc(nil)
|
|
||||||
idList := []ids.ShortID(nil)
|
|
||||||
for id, ip := range ac.idToIP {
|
|
||||||
ipList = append(ipList, toIPDesc(ip))
|
|
||||||
idList = append(idList, ids.NewShortID(id))
|
|
||||||
}
|
|
||||||
return ipList, idList
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) rawConns() ([]salticidae.NetAddr, []ids.ShortID) {
|
|
||||||
ac.init()
|
|
||||||
|
|
||||||
ipList := []salticidae.NetAddr(nil)
|
|
||||||
idList := []ids.ShortID(nil)
|
|
||||||
for id, ip := range ac.idToIP {
|
|
||||||
ipList = append(ipList, ip)
|
|
||||||
idList = append(idList, ids.NewShortID(id))
|
|
||||||
}
|
|
||||||
return ipList, idList
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AddrCert) len() int { return len(ac.ipToID) }
|
|
||||||
|
|
||||||
func toIPDesc(addr salticidae.NetAddr) utils.IPDesc {
|
|
||||||
ip, err := ToIPDesc(addr)
|
|
||||||
HandshakeNet.log.AssertNoError(err)
|
|
||||||
return ip
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToIPDesc converts an address to an IP
|
|
||||||
func ToIPDesc(addr salticidae.NetAddr) (utils.IPDesc, error) {
|
|
||||||
ip := salticidae.FromBigEndianU32(addr.GetIP())
|
|
||||||
port := salticidae.FromBigEndianU16(addr.GetPort())
|
|
||||||
return utils.ToIPDesc(fmt.Sprintf("%d.%d.%d.%d:%d", byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip), port))
|
|
||||||
}
|
|
||||||
|
|
||||||
func addrToID(addr salticidae.NetAddr) uint64 {
|
|
||||||
return uint64(addr.GetIP()) | (uint64(addr.GetPort()) << 32)
|
|
||||||
}
|
|
|
@ -16,10 +16,11 @@ type Builder struct{ Codec }
|
||||||
func (m Builder) GetVersion() (Msg, error) { return m.Pack(GetVersion, nil) }
|
func (m Builder) GetVersion() (Msg, error) { return m.Pack(GetVersion, nil) }
|
||||||
|
|
||||||
// Version message
|
// Version message
|
||||||
func (m Builder) Version(networkID uint32, myTime uint64, myVersion string) (Msg, error) {
|
func (m Builder) Version(networkID uint32, myTime uint64, ip utils.IPDesc, myVersion string) (Msg, error) {
|
||||||
return m.Pack(Version, map[Field]interface{}{
|
return m.Pack(Version, map[Field]interface{}{
|
||||||
NetworkID: networkID,
|
NetworkID: networkID,
|
||||||
MyTime: myTime,
|
MyTime: myTime,
|
||||||
|
IP: ip,
|
||||||
VersionStr: myVersion,
|
VersionStr: myVersion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ const (
|
||||||
VersionStr Field = iota // Used in handshake
|
VersionStr Field = iota // Used in handshake
|
||||||
NetworkID // Used in handshake
|
NetworkID // Used in handshake
|
||||||
MyTime // Used in handshake
|
MyTime // Used in handshake
|
||||||
|
IP // Used in handshake
|
||||||
Peers // Used in handshake
|
Peers // Used in handshake
|
||||||
ChainID // Used for dispatching
|
ChainID // Used for dispatching
|
||||||
RequestID // Used for all messages
|
RequestID // Used for all messages
|
||||||
|
@ -38,6 +39,8 @@ func (f Field) Packer() func(*wrappers.Packer, interface{}) {
|
||||||
return wrappers.TryPackInt
|
return wrappers.TryPackInt
|
||||||
case MyTime:
|
case MyTime:
|
||||||
return wrappers.TryPackLong
|
return wrappers.TryPackLong
|
||||||
|
case IP:
|
||||||
|
return wrappers.TryPackIP
|
||||||
case Peers:
|
case Peers:
|
||||||
return wrappers.TryPackIPList
|
return wrappers.TryPackIPList
|
||||||
case ChainID: // TODO: This will be shortened to use a modified varint spec
|
case ChainID: // TODO: This will be shortened to use a modified varint spec
|
||||||
|
@ -72,6 +75,8 @@ func (f Field) Unpacker() func(*wrappers.Packer) interface{} {
|
||||||
return wrappers.TryUnpackInt
|
return wrappers.TryUnpackInt
|
||||||
case MyTime:
|
case MyTime:
|
||||||
return wrappers.TryUnpackLong
|
return wrappers.TryUnpackLong
|
||||||
|
case IP:
|
||||||
|
return wrappers.TryUnpackIP
|
||||||
case Peers:
|
case Peers:
|
||||||
return wrappers.TryUnpackIPList
|
return wrappers.TryUnpackIPList
|
||||||
case ChainID: // TODO: This will be shortened to use a modified varint spec
|
case ChainID: // TODO: This will be shortened to use a modified varint spec
|
||||||
|
@ -105,6 +110,8 @@ func (f Field) String() string {
|
||||||
return "NetworkID"
|
return "NetworkID"
|
||||||
case MyTime:
|
case MyTime:
|
||||||
return "MyTime"
|
return "MyTime"
|
||||||
|
case IP:
|
||||||
|
return "IP"
|
||||||
case Peers:
|
case Peers:
|
||||||
return "Peers"
|
return "Peers"
|
||||||
case ChainID:
|
case ChainID:
|
||||||
|
@ -161,7 +168,7 @@ var (
|
||||||
Messages = map[salticidae.Opcode][]Field{
|
Messages = map[salticidae.Opcode][]Field{
|
||||||
// Handshake:
|
// Handshake:
|
||||||
GetVersion: []Field{},
|
GetVersion: []Field{},
|
||||||
Version: []Field{NetworkID, MyTime, VersionStr},
|
Version: []Field{NetworkID, MyTime, IP, VersionStr},
|
||||||
GetPeerList: []Field{},
|
GetPeerList: []Field{},
|
||||||
PeerList: []Field{Peers},
|
PeerList: []Field{Peers},
|
||||||
// Bootstrapping:
|
// Bootstrapping:
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||||
|
// See the file LICENSE for licensing terms.
|
||||||
|
|
||||||
|
package networking
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ava-labs/salticidae-go"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
"github.com/ava-labs/gecko/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Connections provides an interface for what a group of connections will
|
||||||
|
// support.
|
||||||
|
type Connections interface {
|
||||||
|
Add(salticidae.PeerID, ids.ShortID, utils.IPDesc)
|
||||||
|
|
||||||
|
GetPeerID(ids.ShortID) (salticidae.PeerID, bool)
|
||||||
|
GetID(salticidae.PeerID) (ids.ShortID, bool)
|
||||||
|
|
||||||
|
ContainsPeerID(salticidae.PeerID) bool
|
||||||
|
ContainsID(ids.ShortID) bool
|
||||||
|
ContainsIP(utils.IPDesc) bool
|
||||||
|
|
||||||
|
Remove(salticidae.PeerID, ids.ShortID)
|
||||||
|
RemovePeerID(salticidae.PeerID)
|
||||||
|
RemoveID(ids.ShortID)
|
||||||
|
|
||||||
|
PeerIDs() []salticidae.PeerID
|
||||||
|
IDs() ids.ShortSet
|
||||||
|
IPs() []utils.IPDesc
|
||||||
|
Conns() ([]salticidae.PeerID, []ids.ShortID, []utils.IPDesc)
|
||||||
|
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type connections struct {
|
||||||
|
mux sync.Mutex
|
||||||
|
// peerID -> id
|
||||||
|
peerIDToID map[[32]byte]ids.ShortID
|
||||||
|
// id -> peerID
|
||||||
|
idToPeerID map[[20]byte]salticidae.PeerID
|
||||||
|
// id -> ip
|
||||||
|
idToIP map[[20]byte]utils.IPDesc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnections returns a new and empty connections object
|
||||||
|
func NewConnections() Connections {
|
||||||
|
return &connections{
|
||||||
|
peerIDToID: make(map[[32]byte]ids.ShortID),
|
||||||
|
idToPeerID: make(map[[20]byte]salticidae.PeerID),
|
||||||
|
idToIP: make(map[[20]byte]utils.IPDesc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Assumes that peer is garbage collected normally
|
||||||
|
func (c *connections) Add(peer salticidae.PeerID, id ids.ShortID, ip utils.IPDesc) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
c.add(peer, id, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerID returns the peer mapped to the id that is provided if one exists.
|
||||||
|
func (c *connections) GetPeerID(id ids.ShortID) (salticidae.PeerID, bool) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return c.getPeerID(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID returns the id mapped to the peer that is provided if one exists.
|
||||||
|
func (c *connections) GetID(peer salticidae.PeerID) (ids.ShortID, bool) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return c.getID(peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsPeerID returns true if the peer is contained in the connection pool
|
||||||
|
func (c *connections) ContainsPeerID(peer salticidae.PeerID) bool {
|
||||||
|
_, exists := c.GetID(peer)
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsID returns true if the id is contained in the connection pool
|
||||||
|
func (c *connections) ContainsID(id ids.ShortID) bool {
|
||||||
|
_, exists := c.GetPeerID(id)
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsIP returns true if the ip is contained in the connection pool
|
||||||
|
func (c *connections) ContainsIP(ip utils.IPDesc) bool {
|
||||||
|
for _, otherIP := range c.IPs() {
|
||||||
|
if ip.Equal(otherIP) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove ensures that no connection will have any mapping containing [peer] or
|
||||||
|
// [id].
|
||||||
|
func (c *connections) Remove(peer salticidae.PeerID, id ids.ShortID) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
c.remove(peer, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePeerID ensures that no connection will have a mapping containing [peer]
|
||||||
|
func (c *connections) RemovePeerID(peer salticidae.PeerID) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
c.removePeerID(peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveID ensures that no connection will have a mapping containing [id]
|
||||||
|
func (c *connections) RemoveID(id ids.ShortID) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
c.removeID(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerIDs returns the full list of peers contained in this connection pool.
|
||||||
|
func (c *connections) PeerIDs() []salticidae.PeerID {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return c.peerIDs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs return the set of IDs that are mapping in this connection pool.
|
||||||
|
func (c *connections) IDs() ids.ShortSet {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return c.ids()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPs return the set of IPs that are mapped in this connection pool.
|
||||||
|
func (c *connections) IPs() []utils.IPDesc {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return c.ips()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conns return the set of connections in this connection pool.
|
||||||
|
func (c *connections) Conns() ([]salticidae.PeerID, []ids.ShortID, []utils.IPDesc) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return c.conns()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of elements in the map
|
||||||
|
func (c *connections) Len() int {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
return c.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) add(peer salticidae.PeerID, id ids.ShortID, ip utils.IPDesc) {
|
||||||
|
c.remove(peer, id)
|
||||||
|
|
||||||
|
key := id.Key()
|
||||||
|
c.peerIDToID[toID(peer)] = id
|
||||||
|
c.idToPeerID[key] = peer
|
||||||
|
c.idToIP[key] = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) getPeerID(id ids.ShortID) (salticidae.PeerID, bool) {
|
||||||
|
peer, exists := c.idToPeerID[id.Key()]
|
||||||
|
return peer, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) getID(peer salticidae.PeerID) (ids.ShortID, bool) {
|
||||||
|
id, exists := c.peerIDToID[toID(peer)]
|
||||||
|
return id, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) remove(peer salticidae.PeerID, id ids.ShortID) {
|
||||||
|
c.removePeerID(peer)
|
||||||
|
c.removeID(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) removePeerID(peer salticidae.PeerID) {
|
||||||
|
peerID := toID(peer)
|
||||||
|
if id, exists := c.peerIDToID[peerID]; exists {
|
||||||
|
idKey := id.Key()
|
||||||
|
|
||||||
|
delete(c.peerIDToID, peerID)
|
||||||
|
delete(c.idToPeerID, idKey)
|
||||||
|
delete(c.idToIP, idKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) removeID(id ids.ShortID) {
|
||||||
|
idKey := id.Key()
|
||||||
|
if peer, exists := c.idToPeerID[idKey]; exists {
|
||||||
|
delete(c.peerIDToID, toID(peer))
|
||||||
|
delete(c.idToPeerID, idKey)
|
||||||
|
delete(c.idToIP, idKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) peerIDs() []salticidae.PeerID {
|
||||||
|
peers := make([]salticidae.PeerID, 0, len(c.idToPeerID))
|
||||||
|
for _, peer := range c.idToPeerID {
|
||||||
|
peers = append(peers, peer)
|
||||||
|
}
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) ids() ids.ShortSet {
|
||||||
|
ids := ids.ShortSet{}
|
||||||
|
for _, id := range c.peerIDToID {
|
||||||
|
ids.Add(id)
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) ips() []utils.IPDesc {
|
||||||
|
ips := make([]utils.IPDesc, 0, len(c.idToIP))
|
||||||
|
for _, ip := range c.idToIP {
|
||||||
|
ips = append(ips, ip)
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) conns() ([]salticidae.PeerID, []ids.ShortID, []utils.IPDesc) {
|
||||||
|
peers := make([]salticidae.PeerID, 0, len(c.idToPeerID))
|
||||||
|
idList := make([]ids.ShortID, 0, len(c.idToPeerID))
|
||||||
|
ips := make([]utils.IPDesc, 0, len(c.idToPeerID))
|
||||||
|
for id, peer := range c.idToPeerID {
|
||||||
|
idList = append(idList, ids.NewShortID(id))
|
||||||
|
peers = append(peers, peer)
|
||||||
|
ips = append(ips, c.idToIP[id])
|
||||||
|
}
|
||||||
|
return peers, idList, ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connections) len() int { return len(c.idToPeerID) }
|
||||||
|
|
||||||
|
func toID(peer salticidae.PeerID) [32]byte {
|
||||||
|
ds := salticidae.NewDataStream(false)
|
||||||
|
|
||||||
|
peerInt := peer.AsUInt256()
|
||||||
|
peerInt.Serialize(ds)
|
||||||
|
|
||||||
|
size := ds.Size()
|
||||||
|
dsb := ds.GetDataInPlace(size)
|
||||||
|
idBytes := dsb.Get()
|
||||||
|
|
||||||
|
id := [32]byte{}
|
||||||
|
copy(id[:], idBytes)
|
||||||
|
|
||||||
|
ds.Free()
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func toIPDesc(addr salticidae.NetAddr) utils.IPDesc {
|
||||||
|
ip, err := ToIPDesc(addr)
|
||||||
|
HandshakeNet.log.AssertNoError(err)
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToIPDesc converts an address to an IP
|
||||||
|
func ToIPDesc(addr salticidae.NetAddr) (utils.IPDesc, error) {
|
||||||
|
ip := salticidae.FromBigEndianU32(addr.GetIP())
|
||||||
|
port := salticidae.FromBigEndianU16(addr.GetPort())
|
||||||
|
return utils.ToIPDesc(fmt.Sprintf("%d.%d.%d.%d:%d", byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip), port))
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
package networking
|
package networking
|
||||||
|
|
||||||
// #include "salticidae/network.h"
|
// #include "salticidae/network.h"
|
||||||
// bool checkPeerCertificate(msgnetwork_conn_t *, bool, void *);
|
// bool connHandler(msgnetwork_conn_t *, bool, void *);
|
||||||
// void unknownPeerHandler(netaddr_t *, x509_t *, void *);
|
// void unknownPeerHandler(netaddr_t *, x509_t *, void *);
|
||||||
// void peerHandler(peernetwork_conn_t *, bool, void *);
|
// void peerHandler(peernetwork_conn_t *, bool, void *);
|
||||||
// void ping(msg_t *, msgnetwork_conn_t *, void *);
|
// void ping(msg_t *, msgnetwork_conn_t *, void *);
|
||||||
|
@ -19,6 +19,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -61,9 +63,23 @@ Attempt reconnections
|
||||||
node isn't connected to after awhile delete the connection.
|
node isn't connected to after awhile delete the connection.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Version this avalanche instance is executing.
|
||||||
|
var (
|
||||||
|
VersionPrefix = "avalanche/"
|
||||||
|
VersionSeparator = "."
|
||||||
|
MajorVersion = 0
|
||||||
|
MinorVersion = 2
|
||||||
|
PatchVersion = 0
|
||||||
|
ClientVersion = fmt.Sprintf("%s%d%s%d%s%d",
|
||||||
|
VersionPrefix,
|
||||||
|
MajorVersion,
|
||||||
|
VersionSeparator,
|
||||||
|
MinorVersion,
|
||||||
|
VersionSeparator,
|
||||||
|
PatchVersion)
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CurrentVersion this avalanche instance is executing.
|
|
||||||
CurrentVersion = "avalanche/0.0.1"
|
|
||||||
// MaxClockDifference allowed between connected nodes.
|
// MaxClockDifference allowed between connected nodes.
|
||||||
MaxClockDifference = time.Minute
|
MaxClockDifference = time.Minute
|
||||||
// PeerListGossipSpacing is the amount of time to wait between pushing this
|
// PeerListGossipSpacing is the amount of time to wait between pushing this
|
||||||
|
@ -74,12 +90,16 @@ const (
|
||||||
// PeerListStakerGossipFraction calculates the fraction of stakers that are
|
// PeerListStakerGossipFraction calculates the fraction of stakers that are
|
||||||
// gossiped to. If set to 1, then only stakers will be gossiped to.
|
// gossiped to. If set to 1, then only stakers will be gossiped to.
|
||||||
PeerListStakerGossipFraction = 2
|
PeerListStakerGossipFraction = 2
|
||||||
|
|
||||||
|
// ConnectTimeout is the amount of time to wait before attempt to connect to
|
||||||
|
// an unknown peer
|
||||||
|
ConnectTimeout = 6 * time.Second
|
||||||
// GetVersionTimeout is the amount of time to wait before sending a
|
// GetVersionTimeout is the amount of time to wait before sending a
|
||||||
// getVersion message to a partially connected peer
|
// getVersion message to a partially connected peer
|
||||||
GetVersionTimeout = 2 * time.Second
|
GetVersionTimeout = 2 * time.Second
|
||||||
// ReconnectTimeout is the amount of time to wait to reconnect to a staker
|
// ReconnectTimeout is the amount of time to wait to reconnect to a staker
|
||||||
// before giving up
|
// before giving up
|
||||||
ReconnectTimeout = 1 * time.Minute
|
ReconnectTimeout = 10 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manager is the struct that will be accessed on event calls
|
// Manager is the struct that will be accessed on event calls
|
||||||
|
@ -96,23 +116,35 @@ var (
|
||||||
type Handshake struct {
|
type Handshake struct {
|
||||||
handshakeMetrics
|
handshakeMetrics
|
||||||
|
|
||||||
networkID uint32
|
networkID uint32 // ID of the network I'm running, used to prevent connecting to the wrong network
|
||||||
|
|
||||||
log logging.Logger
|
log logging.Logger
|
||||||
vdrs validators.Set
|
vdrs validators.Set // set of current validators in the AVAnet
|
||||||
myAddr salticidae.NetAddr
|
myAddr salticidae.NetAddr // IP I communicate to peers
|
||||||
myID ids.ShortID
|
myID ids.ShortID // ID that identifies myself as a staker or not
|
||||||
net salticidae.PeerNetwork
|
net salticidae.PeerNetwork // C messaging network
|
||||||
enableStaking bool // Should only be false for local tests
|
enableStaking bool // Should only be false for local tests
|
||||||
|
|
||||||
clock timer.Clock
|
clock timer.Clock
|
||||||
pending AddrCert // Connections that I haven't gotten version messages from
|
|
||||||
connections AddrCert // Connections that I think are connected
|
|
||||||
|
|
||||||
versionTimeout timer.TimeoutManager
|
// Connections that I have added by IP, but haven't gotten an ID from
|
||||||
reconnectTimeout timer.TimeoutManager
|
requestedLock sync.Mutex
|
||||||
|
requested map[string]struct{}
|
||||||
|
requestedTimeout timer.TimeoutManager // keys are hashes of the ip:port string
|
||||||
|
|
||||||
|
// Connections that I have added as a peer, but haven't gotten a version
|
||||||
|
// message from
|
||||||
|
pending Connections
|
||||||
|
versionTimeout timer.TimeoutManager // keys are the peer IDs
|
||||||
|
|
||||||
|
// Connections that I have gotten a valid version message from
|
||||||
|
connections Connections
|
||||||
|
reconnectTimeout timer.TimeoutManager // keys are the peer IDs
|
||||||
|
|
||||||
|
// IPs of nodes I'm connected to will be repeatedly gossiped throughout the network
|
||||||
peerListGossiper *timer.Repeater
|
peerListGossiper *timer.Repeater
|
||||||
|
|
||||||
|
// If any chain is blocked on connecting to peers, track these blockers here
|
||||||
awaitingLock sync.Mutex
|
awaitingLock sync.Mutex
|
||||||
awaiting []*networking.AwaitingConnections
|
awaiting []*networking.AwaitingConnections
|
||||||
}
|
}
|
||||||
|
@ -130,17 +162,37 @@ func (nm *Handshake) Initialize(
|
||||||
networkID uint32,
|
networkID uint32,
|
||||||
) {
|
) {
|
||||||
log.AssertTrue(nm.net == nil, "Should only register network handlers once")
|
log.AssertTrue(nm.net == nil, "Should only register network handlers once")
|
||||||
|
|
||||||
|
nm.handshakeMetrics.Initialize(log, registerer)
|
||||||
|
|
||||||
|
nm.networkID = networkID
|
||||||
|
|
||||||
nm.log = log
|
nm.log = log
|
||||||
nm.vdrs = vdrs
|
nm.vdrs = vdrs
|
||||||
nm.myAddr = myAddr
|
nm.myAddr = myAddr
|
||||||
nm.myID = myID
|
nm.myID = myID
|
||||||
nm.net = peerNet
|
nm.net = peerNet
|
||||||
nm.enableStaking = enableStaking
|
nm.enableStaking = enableStaking
|
||||||
nm.networkID = networkID
|
|
||||||
|
|
||||||
|
nm.requested = make(map[string]struct{})
|
||||||
|
nm.requestedTimeout.Initialize(ConnectTimeout)
|
||||||
|
go nm.log.RecoverAndPanic(nm.requestedTimeout.Dispatch)
|
||||||
|
|
||||||
|
nm.pending = NewConnections()
|
||||||
|
nm.versionTimeout.Initialize(GetVersionTimeout)
|
||||||
|
go nm.log.RecoverAndPanic(nm.versionTimeout.Dispatch)
|
||||||
|
|
||||||
|
nm.connections = NewConnections()
|
||||||
|
nm.reconnectTimeout.Initialize(ReconnectTimeout)
|
||||||
|
go nm.log.RecoverAndPanic(nm.reconnectTimeout.Dispatch)
|
||||||
|
|
||||||
|
nm.peerListGossiper = timer.NewRepeater(nm.gossipPeerList, PeerListGossipSpacing)
|
||||||
|
go nm.log.RecoverAndPanic(nm.peerListGossiper.Dispatch)
|
||||||
|
|
||||||
|
// register c message callbacks
|
||||||
net := peerNet.AsMsgNetwork()
|
net := peerNet.AsMsgNetwork()
|
||||||
|
|
||||||
net.RegConnHandler(salticidae.MsgNetworkConnCallback(C.checkPeerCertificate), nil)
|
net.RegConnHandler(salticidae.MsgNetworkConnCallback(C.connHandler), nil)
|
||||||
peerNet.RegPeerHandler(salticidae.PeerNetworkPeerCallback(C.peerHandler), nil)
|
peerNet.RegPeerHandler(salticidae.PeerNetworkPeerCallback(C.peerHandler), nil)
|
||||||
peerNet.RegUnknownPeerHandler(salticidae.PeerNetworkUnknownPeerCallback(C.unknownPeerHandler), nil)
|
peerNet.RegUnknownPeerHandler(salticidae.PeerNetworkUnknownPeerCallback(C.unknownPeerHandler), nil)
|
||||||
net.RegHandler(Ping, salticidae.MsgNetworkMsgCallback(C.ping), nil)
|
net.RegHandler(Ping, salticidae.MsgNetworkMsgCallback(C.ping), nil)
|
||||||
|
@ -149,17 +201,95 @@ func (nm *Handshake) Initialize(
|
||||||
net.RegHandler(Version, salticidae.MsgNetworkMsgCallback(C.version), nil)
|
net.RegHandler(Version, salticidae.MsgNetworkMsgCallback(C.version), nil)
|
||||||
net.RegHandler(GetPeerList, salticidae.MsgNetworkMsgCallback(C.getPeerList), nil)
|
net.RegHandler(GetPeerList, salticidae.MsgNetworkMsgCallback(C.getPeerList), nil)
|
||||||
net.RegHandler(PeerList, salticidae.MsgNetworkMsgCallback(C.peerList), nil)
|
net.RegHandler(PeerList, salticidae.MsgNetworkMsgCallback(C.peerList), nil)
|
||||||
|
}
|
||||||
|
|
||||||
nm.handshakeMetrics.Initialize(nm.log, registerer)
|
// ConnectTo add the peer as a connection and connects to them.
|
||||||
|
func (nm *Handshake) ConnectTo(peer salticidae.PeerID, stakerID ids.ShortID, addr salticidae.NetAddr) {
|
||||||
|
if nm.pending.ContainsPeerID(peer) || nm.connections.ContainsPeerID(peer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
nm.versionTimeout.Initialize(GetVersionTimeout)
|
nm.log.Info("Attempting to connect to %s", stakerID)
|
||||||
go nm.log.RecoverAndPanic(nm.versionTimeout.Dispatch)
|
|
||||||
|
|
||||||
nm.reconnectTimeout.Initialize(ReconnectTimeout)
|
nm.net.AddPeer(peer)
|
||||||
go nm.log.RecoverAndPanic(nm.reconnectTimeout.Dispatch)
|
nm.net.SetPeerAddr(peer, addr)
|
||||||
|
nm.net.ConnPeer(peer, 600, 1)
|
||||||
|
|
||||||
nm.peerListGossiper = timer.NewRepeater(nm.gossipPeerList, PeerListGossipSpacing)
|
ip := toIPDesc(addr)
|
||||||
go nm.log.RecoverAndPanic(nm.peerListGossiper.Dispatch)
|
nm.pending.Add(peer, stakerID, ip)
|
||||||
|
|
||||||
|
peerBytes := toID(peer)
|
||||||
|
peerID := ids.NewID(peerBytes)
|
||||||
|
|
||||||
|
nm.reconnectTimeout.Put(peerID, func() {
|
||||||
|
nm.pending.Remove(peer, stakerID)
|
||||||
|
nm.connections.Remove(peer, stakerID)
|
||||||
|
nm.net.DelPeer(peer)
|
||||||
|
|
||||||
|
nm.numPeers.Set(float64(nm.connections.Len()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect ...
|
||||||
|
func (nm *Handshake) Connect(addr salticidae.NetAddr) {
|
||||||
|
ip := toIPDesc(addr)
|
||||||
|
ipStr := ip.String()
|
||||||
|
if nm.pending.ContainsIP(ip) || nm.connections.ContainsIP(ip) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nm.enableStaking {
|
||||||
|
nm.log.Info("Adding peer %s", ip)
|
||||||
|
|
||||||
|
peer := salticidae.NewPeerIDFromNetAddr(addr, true)
|
||||||
|
nm.ConnectTo(peer, toShortID(ip), addr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nm.requestedLock.Lock()
|
||||||
|
_, exists := nm.requested[ipStr]
|
||||||
|
nm.requestedLock.Unlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nm.log.Info("Adding peer %s", ip)
|
||||||
|
|
||||||
|
count := new(int)
|
||||||
|
*count = 100
|
||||||
|
handler := new(func())
|
||||||
|
*handler = func() {
|
||||||
|
nm.requestedLock.Lock()
|
||||||
|
defer nm.requestedLock.Unlock()
|
||||||
|
|
||||||
|
if *count == 100 {
|
||||||
|
nm.requested[ipStr] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := nm.requested[ipStr]; !exists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *count <= 0 {
|
||||||
|
delete(nm.requested, ipStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*count--
|
||||||
|
|
||||||
|
if nm.pending.ContainsIP(ip) || nm.connections.ContainsIP(ip) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nm.log.Info("Attempting to discover peer at %s", ipStr)
|
||||||
|
|
||||||
|
msgNet := nm.net.AsMsgNetwork()
|
||||||
|
msgNet.Connect(addr)
|
||||||
|
|
||||||
|
ipID := ids.NewID(hashing.ComputeHash256Array([]byte(ipStr)))
|
||||||
|
nm.requestedTimeout.Put(ipID, *handler)
|
||||||
|
}
|
||||||
|
(*handler)()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AwaitConnections ...
|
// AwaitConnections ...
|
||||||
|
@ -209,19 +339,19 @@ func (nm *Handshake) gossipPeerList() {
|
||||||
idsToSend = append(idsToSend, nonStakers[sampler.Sample()])
|
idsToSend = append(idsToSend, nonStakers[sampler.Sample()])
|
||||||
}
|
}
|
||||||
|
|
||||||
ips := []salticidae.NetAddr{}
|
peers := []salticidae.PeerID{}
|
||||||
for _, id := range idsToSend {
|
for _, id := range idsToSend {
|
||||||
if ip, exists := nm.connections.GetIP(id); exists {
|
if peer, exists := nm.connections.GetPeerID(id); exists {
|
||||||
ips = append(ips, ip)
|
peers = append(peers, peer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nm.SendPeerList(ips...)
|
nm.SendPeerList(peers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connections returns the object that tracks the nodes that are currently
|
// Connections returns the object that tracks the nodes that are currently
|
||||||
// connected to this node.
|
// connected to this node.
|
||||||
func (nm *Handshake) Connections() Connections { return &nm.connections }
|
func (nm *Handshake) Connections() Connections { return nm.connections }
|
||||||
|
|
||||||
// Shutdown the network
|
// Shutdown the network
|
||||||
func (nm *Handshake) Shutdown() {
|
func (nm *Handshake) Shutdown() {
|
||||||
|
@ -230,59 +360,60 @@ func (nm *Handshake) Shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendGetVersion to the requested peer
|
// SendGetVersion to the requested peer
|
||||||
func (nm *Handshake) SendGetVersion(addr salticidae.NetAddr) {
|
func (nm *Handshake) SendGetVersion(peer salticidae.PeerID) {
|
||||||
build := Builder{}
|
build := Builder{}
|
||||||
gv, err := build.GetVersion()
|
gv, err := build.GetVersion()
|
||||||
nm.log.AssertNoError(err)
|
nm.log.AssertNoError(err)
|
||||||
nm.send(gv, addr)
|
nm.send(gv, peer)
|
||||||
|
|
||||||
nm.numGetVersionSent.Inc()
|
nm.numGetVersionSent.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendVersion to the requested peer
|
// SendVersion to the requested peer
|
||||||
func (nm *Handshake) SendVersion(addr salticidae.NetAddr) error {
|
func (nm *Handshake) SendVersion(peer salticidae.PeerID) error {
|
||||||
build := Builder{}
|
build := Builder{}
|
||||||
v, err := build.Version(nm.networkID, nm.clock.Unix(), CurrentVersion)
|
v, err := build.Version(nm.networkID, nm.clock.Unix(), toIPDesc(nm.myAddr), ClientVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("packing Version failed due to %s", err)
|
return fmt.Errorf("packing Version failed due to %s", err)
|
||||||
}
|
}
|
||||||
nm.send(v, addr)
|
nm.send(v, peer)
|
||||||
nm.numVersionSent.Inc()
|
nm.numVersionSent.Inc()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendPeerList to the requested peer
|
// SendPeerList to the requested peer
|
||||||
func (nm *Handshake) SendPeerList(addrs ...salticidae.NetAddr) error {
|
func (nm *Handshake) SendPeerList(peers ...salticidae.PeerID) error {
|
||||||
if len(addrs) == 0 {
|
if len(peers) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, ids := nm.connections.Conns()
|
_, ids, ips := nm.connections.Conns()
|
||||||
ipsToSend := []utils.IPDesc(nil)
|
ipsToSend := []utils.IPDesc(nil)
|
||||||
for i, id := range ids {
|
for i, id := range ids {
|
||||||
if nm.vdrs.Contains(id) {
|
ip := ips[i]
|
||||||
ipsToSend = append(ipsToSend, ips[i])
|
if !ip.IsZero() && nm.vdrs.Contains(id) {
|
||||||
|
ipsToSend = append(ipsToSend, ip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ipsToSend) == 0 {
|
if len(ipsToSend) == 0 {
|
||||||
nm.log.Debug("No IPs to send to %d peer(s)", len(addrs))
|
nm.log.Debug("No IPs to send to %d peer(s)", len(peers))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
nm.log.Verbo("Sending %d ips to %d peer(s)", len(ipsToSend), len(addrs))
|
nm.log.Verbo("Sending %d ips to %d peer(s)", len(ipsToSend), len(peers))
|
||||||
|
|
||||||
build := Builder{}
|
build := Builder{}
|
||||||
pl, err := build.PeerList(ipsToSend)
|
pl, err := build.PeerList(ipsToSend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Packing Peerlist failed due to %w", err)
|
return fmt.Errorf("Packing Peerlist failed due to %w", err)
|
||||||
}
|
}
|
||||||
nm.send(pl, addrs...)
|
nm.send(pl, peers...)
|
||||||
nm.numPeerlistSent.Add(float64(len(addrs)))
|
nm.numPeerlistSent.Add(float64(len(peers)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *Handshake) send(msg Msg, addrs ...salticidae.NetAddr) {
|
func (nm *Handshake) send(msg Msg, peers ...salticidae.PeerID) {
|
||||||
ds := msg.DataStream()
|
ds := msg.DataStream()
|
||||||
defer ds.Free()
|
defer ds.Free()
|
||||||
ba := salticidae.NewByteArrayMovedFromDataStream(ds, false)
|
ba := salticidae.NewByteArrayMovedFromDataStream(ds, false)
|
||||||
|
@ -290,81 +421,112 @@ func (nm *Handshake) send(msg Msg, addrs ...salticidae.NetAddr) {
|
||||||
cMsg := salticidae.NewMsgMovedFromByteArray(msg.Op(), ba, false)
|
cMsg := salticidae.NewMsgMovedFromByteArray(msg.Op(), ba, false)
|
||||||
defer cMsg.Free()
|
defer cMsg.Free()
|
||||||
|
|
||||||
switch len(addrs) {
|
switch len(peers) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
nm.net.SendMsg(cMsg, addrs[0])
|
nm.net.SendMsg(cMsg, peers[0])
|
||||||
default:
|
default:
|
||||||
nm.net.MulticastMsgByMove(cMsg, addrs)
|
nm.net.MulticastMsgByMove(cMsg, peers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkPeerCertificate of a new inbound connection
|
// connHandler notifies of a new inbound connection
|
||||||
//export checkPeerCertificate
|
//export connHandler
|
||||||
func checkPeerCertificate(_ *C.struct_msgnetwork_conn_t, connected C.bool, _ unsafe.Pointer) C.bool {
|
func connHandler(_conn *C.struct_msgnetwork_conn_t, connected C.bool, _ unsafe.Pointer) C.bool {
|
||||||
return connected
|
if !HandshakeNet.enableStaking || !bool(connected) {
|
||||||
|
return connected
|
||||||
|
}
|
||||||
|
|
||||||
|
HandshakeNet.requestedLock.Lock()
|
||||||
|
defer HandshakeNet.requestedLock.Unlock()
|
||||||
|
|
||||||
|
conn := salticidae.MsgNetworkConnFromC(salticidae.CMsgNetworkConn(_conn))
|
||||||
|
addr := conn.GetAddr()
|
||||||
|
ip := toIPDesc(addr)
|
||||||
|
ipStr := ip.String()
|
||||||
|
|
||||||
|
ipID := ids.NewID(hashing.ComputeHash256Array([]byte(ipStr)))
|
||||||
|
HandshakeNet.requestedTimeout.Remove(ipID)
|
||||||
|
|
||||||
|
if _, exists := HandshakeNet.requested[ipStr]; !exists {
|
||||||
|
HandshakeNet.log.Debug("connHandler called with %s", ip)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
delete(HandshakeNet.requested, ipStr)
|
||||||
|
|
||||||
|
cert := conn.GetPeerCert()
|
||||||
|
peer := salticidae.NewPeerIDFromX509(cert, true)
|
||||||
|
|
||||||
|
HandshakeNet.ConnectTo(peer, getCert(cert), addr)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *Handshake) connectedToPeer(conn *C.struct_peernetwork_conn_t, addr salticidae.NetAddr) {
|
func (nm *Handshake) connectedToPeer(conn *C.struct_peernetwork_conn_t, peer salticidae.PeerID) {
|
||||||
ip := toIPDesc(addr)
|
peerBytes := toID(peer)
|
||||||
|
peerID := ids.NewID(peerBytes)
|
||||||
|
|
||||||
// If we're enforcing staking, use a peer's certificate to uniquely identify them
|
// If we're enforcing staking, use a peer's certificate to uniquely identify them
|
||||||
// Otherwise, use a hash of their ip to identify them
|
// Otherwise, use a hash of their ip to identify them
|
||||||
cert := ids.ShortID{}
|
cert := ids.ShortID{}
|
||||||
ipCert := toShortID(ip)
|
|
||||||
if nm.enableStaking {
|
if nm.enableStaking {
|
||||||
cert = getPeerCert(conn)
|
cert = getPeerCert(conn)
|
||||||
} else {
|
} else {
|
||||||
cert = ipCert
|
key := [20]byte{}
|
||||||
|
copy(key[:], peerID.Bytes())
|
||||||
|
cert = ids.NewShortID(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
nm.log.Debug("Connected to %s", ip)
|
nm.log.Debug("Connected to %s", cert)
|
||||||
|
|
||||||
longCert := cert.LongID()
|
nm.reconnectTimeout.Remove(peerID)
|
||||||
nm.reconnectTimeout.Remove(longCert)
|
|
||||||
nm.reconnectTimeout.Remove(ipCert.LongID())
|
|
||||||
|
|
||||||
nm.pending.Add(addr, cert)
|
|
||||||
|
|
||||||
handler := new(func())
|
handler := new(func())
|
||||||
*handler = func() {
|
*handler = func() {
|
||||||
if nm.pending.ContainsIP(addr) {
|
if nm.pending.ContainsPeerID(peer) {
|
||||||
nm.SendGetVersion(addr)
|
nm.SendGetVersion(peer)
|
||||||
nm.versionTimeout.Put(longCert, *handler)
|
nm.versionTimeout.Put(peerID, *handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*handler)()
|
(*handler)()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *Handshake) disconnectedFromPeer(addr salticidae.NetAddr) {
|
func (nm *Handshake) disconnectedFromPeer(peer salticidae.PeerID) {
|
||||||
cert := ids.ShortID{}
|
cert := ids.ShortID{}
|
||||||
if pendingCert, exists := nm.pending.GetID(addr); exists {
|
if pendingCert, exists := nm.pending.GetID(peer); exists {
|
||||||
cert = pendingCert
|
cert = pendingCert
|
||||||
} else if connectedCert, exists := nm.connections.GetID(addr); exists {
|
nm.log.Info("Disconnected from pending peer %s", cert)
|
||||||
|
} else if connectedCert, exists := nm.connections.GetID(peer); exists {
|
||||||
cert = connectedCert
|
cert = connectedCert
|
||||||
|
nm.log.Info("Disconnected from peer %s", cert)
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nm.log.Info("Disconnected from %s", toIPDesc(addr))
|
peerBytes := toID(peer)
|
||||||
|
peerID := ids.NewID(peerBytes)
|
||||||
|
|
||||||
|
nm.versionTimeout.Remove(peerID)
|
||||||
|
nm.connections.Remove(peer, cert)
|
||||||
|
nm.numPeers.Set(float64(nm.connections.Len()))
|
||||||
|
|
||||||
longCert := cert.LongID()
|
|
||||||
if nm.vdrs.Contains(cert) {
|
if nm.vdrs.Contains(cert) {
|
||||||
nm.reconnectTimeout.Put(longCert, func() {
|
nm.reconnectTimeout.Put(peerID, func() {
|
||||||
nm.net.DelPeer(addr)
|
nm.pending.Remove(peer, cert)
|
||||||
|
nm.connections.Remove(peer, cert)
|
||||||
|
nm.net.DelPeer(peer)
|
||||||
|
|
||||||
|
nm.numPeers.Set(float64(nm.connections.Len()))
|
||||||
})
|
})
|
||||||
|
nm.pending.Add(peer, cert, utils.IPDesc{})
|
||||||
} else {
|
} else {
|
||||||
nm.net.DelPeer(addr)
|
nm.pending.Remove(peer, cert)
|
||||||
|
nm.net.DelPeer(peer)
|
||||||
}
|
}
|
||||||
nm.versionTimeout.Remove(longCert)
|
|
||||||
|
|
||||||
if !nm.enableStaking {
|
if !nm.enableStaking {
|
||||||
nm.vdrs.Remove(cert)
|
nm.vdrs.Remove(cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
nm.pending.RemoveIP(addr)
|
|
||||||
nm.connections.RemoveIP(addr)
|
|
||||||
nm.numPeers.Set(float64(nm.connections.Len()))
|
|
||||||
|
|
||||||
nm.awaitingLock.Lock()
|
nm.awaitingLock.Lock()
|
||||||
defer nm.awaitingLock.Unlock()
|
defer nm.awaitingLock.Unlock()
|
||||||
for _, awaiting := range HandshakeNet.awaiting {
|
for _, awaiting := range HandshakeNet.awaiting {
|
||||||
|
@ -372,57 +534,123 @@ func (nm *Handshake) disconnectedFromPeer(addr salticidae.NetAddr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkCompatibility Check to make sure that the peer and I speak the same language.
|
||||||
|
func (nm *Handshake) checkCompatibility(peerVersion string) bool {
|
||||||
|
if !strings.HasPrefix(peerVersion, VersionPrefix) {
|
||||||
|
nm.log.Warn("Peer attempted to connect with an invalid version prefix")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
peerVersion = peerVersion[len(VersionPrefix):]
|
||||||
|
splitPeerVersion := strings.SplitN(peerVersion, VersionSeparator, 3)
|
||||||
|
if len(splitPeerVersion) != 3 {
|
||||||
|
nm.log.Warn("Peer attempted to connect with an invalid number of subversions")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
major, err := strconv.Atoi(splitPeerVersion[0])
|
||||||
|
if err != nil {
|
||||||
|
nm.log.Warn("Peer attempted to connect with an invalid major version")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
minor, err := strconv.Atoi(splitPeerVersion[1])
|
||||||
|
if err != nil {
|
||||||
|
nm.log.Warn("Peer attempted to connect with an invalid minor version")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
patch, err := strconv.Atoi(splitPeerVersion[2])
|
||||||
|
if err != nil {
|
||||||
|
nm.log.Warn("Peer attempted to connect with an invalid patch version")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case major < MajorVersion:
|
||||||
|
// peers major version is too low
|
||||||
|
return false
|
||||||
|
case major > MajorVersion:
|
||||||
|
nm.log.Warn("Peer attempted to connect with a higher major version, this client may need to be updated")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case minor < MinorVersion:
|
||||||
|
// peers minor version is too low
|
||||||
|
return false
|
||||||
|
case minor > MinorVersion:
|
||||||
|
nm.log.Warn("Peer attempted to connect with a higher minor version, this client may need to be updated")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch > PatchVersion {
|
||||||
|
nm.log.Warn("Peer is connecting with a higher patch version, this client may need to be updated")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// peerHandler notifies a change to the set of connected peers
|
// peerHandler notifies a change to the set of connected peers
|
||||||
// connected is true if a new peer is connected
|
// connected is true if a new peer is connected
|
||||||
// connected is false if a formerly connected peer has disconnected
|
// connected is false if a formerly connected peer has disconnected
|
||||||
//export peerHandler
|
//export peerHandler
|
||||||
func peerHandler(_conn *C.struct_peernetwork_conn_t, connected C.bool, _ unsafe.Pointer) {
|
func peerHandler(_conn *C.struct_peernetwork_conn_t, connected C.bool, _ unsafe.Pointer) {
|
||||||
|
HandshakeNet.log.Debug("peerHandler called")
|
||||||
|
|
||||||
pConn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
pConn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
||||||
addr := pConn.GetPeerAddr(true)
|
peer := pConn.GetPeerID(true)
|
||||||
|
|
||||||
if connected {
|
if connected {
|
||||||
HandshakeNet.connectedToPeer(_conn, addr)
|
HandshakeNet.connectedToPeer(_conn, peer)
|
||||||
} else {
|
} else {
|
||||||
HandshakeNet.disconnectedFromPeer(addr)
|
HandshakeNet.disconnectedFromPeer(peer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unknownPeerHandler notifies of an unknown peer connection attempt
|
// unknownPeerHandler notifies of an unknown peer connection attempt
|
||||||
//export unknownPeerHandler
|
//export unknownPeerHandler
|
||||||
func unknownPeerHandler(_addr *C.netaddr_t, _cert *C.x509_t, _ unsafe.Pointer) {
|
func unknownPeerHandler(_addr *C.netaddr_t, _cert *C.x509_t, _ unsafe.Pointer) {
|
||||||
|
HandshakeNet.log.Debug("unknownPeerHandler called")
|
||||||
|
|
||||||
addr := salticidae.NetAddrFromC(salticidae.CNetAddr(_addr)).Copy(true)
|
addr := salticidae.NetAddrFromC(salticidae.CNetAddr(_addr)).Copy(true)
|
||||||
ip := toIPDesc(addr)
|
ip := toIPDesc(addr)
|
||||||
|
|
||||||
HandshakeNet.log.Info("Adding peer %s", ip)
|
HandshakeNet.log.Info("Adding peer %s", ip)
|
||||||
|
|
||||||
cert := ids.ShortID{}
|
var peer salticidae.PeerID
|
||||||
|
var id ids.ShortID
|
||||||
if HandshakeNet.enableStaking {
|
if HandshakeNet.enableStaking {
|
||||||
cert = getCert(salticidae.X509FromC(salticidae.CX509(_cert)))
|
cert := salticidae.X509FromC(salticidae.CX509(_cert))
|
||||||
|
peer = salticidae.NewPeerIDFromX509(cert, true)
|
||||||
|
id = getCert(cert)
|
||||||
} else {
|
} else {
|
||||||
cert = toShortID(ip)
|
peer = salticidae.NewPeerIDFromNetAddr(addr, true)
|
||||||
|
id = toShortID(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeNet.reconnectTimeout.Put(cert.LongID(), func() {
|
peerBytes := toID(peer)
|
||||||
HandshakeNet.net.DelPeer(addr)
|
peerID := ids.NewID(peerBytes)
|
||||||
|
|
||||||
|
HandshakeNet.reconnectTimeout.Put(peerID, func() {
|
||||||
|
HandshakeNet.pending.Remove(peer, id)
|
||||||
|
HandshakeNet.connections.Remove(peer, id)
|
||||||
|
HandshakeNet.net.DelPeer(peer)
|
||||||
|
|
||||||
|
HandshakeNet.numPeers.Set(float64(HandshakeNet.connections.Len()))
|
||||||
})
|
})
|
||||||
HandshakeNet.net.AddPeer(addr)
|
HandshakeNet.pending.Add(peer, id, utils.IPDesc{})
|
||||||
|
HandshakeNet.net.AddPeer(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ping handles the recept of a ping message
|
// ping handles the recept of a ping message
|
||||||
//export ping
|
//export ping
|
||||||
func ping(_ *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.Pointer) {
|
func ping(_ *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.Pointer) {
|
||||||
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
||||||
addr := conn.GetPeerAddr(false)
|
peer := conn.GetPeerID(false)
|
||||||
defer addr.Free()
|
defer peer.Free()
|
||||||
if addr.IsNull() {
|
|
||||||
HandshakeNet.log.Warn("Ping sent from unknown peer")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
build := Builder{}
|
build := Builder{}
|
||||||
pong, err := build.Pong()
|
pong, err := build.Pong()
|
||||||
HandshakeNet.log.AssertNoError(err)
|
HandshakeNet.log.AssertNoError(err)
|
||||||
|
|
||||||
HandshakeNet.send(pong, addr)
|
HandshakeNet.send(pong, peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pong handles the recept of a pong message
|
// pong handles the recept of a pong message
|
||||||
|
@ -435,15 +663,10 @@ func getVersion(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsaf
|
||||||
HandshakeNet.numGetVersionReceived.Inc()
|
HandshakeNet.numGetVersionReceived.Inc()
|
||||||
|
|
||||||
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
||||||
addr := conn.GetPeerAddr(false)
|
peer := conn.GetPeerID(false)
|
||||||
defer addr.Free()
|
defer peer.Free()
|
||||||
|
|
||||||
if addr.IsNull() {
|
HandshakeNet.SendVersion(peer)
|
||||||
HandshakeNet.log.Warn("GetVersion sent from unknown peer")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
HandshakeNet.SendVersion(addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// version handles the recept of a version message
|
// version handles the recept of a version message
|
||||||
|
@ -453,35 +676,33 @@ func version(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.P
|
||||||
|
|
||||||
msg := salticidae.MsgFromC(salticidae.CMsg(_msg))
|
msg := salticidae.MsgFromC(salticidae.CMsg(_msg))
|
||||||
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
||||||
addr := conn.GetPeerAddr(true)
|
peer := conn.GetPeerID(true)
|
||||||
if addr.IsNull() {
|
|
||||||
HandshakeNet.log.Warn("Version sent from unknown peer")
|
peerBytes := toID(peer)
|
||||||
|
peerID := ids.NewID(peerBytes)
|
||||||
|
|
||||||
|
HandshakeNet.versionTimeout.Remove(peerID)
|
||||||
|
|
||||||
|
id, exists := HandshakeNet.pending.GetID(peer)
|
||||||
|
if !exists {
|
||||||
|
HandshakeNet.log.Warn("Dropping Version message because the peer isn't pending")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
HandshakeNet.pending.Remove(peer, id)
|
||||||
cert := ids.ShortID{}
|
|
||||||
if HandshakeNet.enableStaking {
|
|
||||||
cert = getMsgCert(_conn)
|
|
||||||
} else {
|
|
||||||
ip := toIPDesc(addr)
|
|
||||||
cert = toShortID(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer HandshakeNet.pending.Remove(addr, cert)
|
|
||||||
|
|
||||||
build := Builder{}
|
build := Builder{}
|
||||||
pMsg, err := build.Parse(Version, msg.GetPayloadByMove())
|
pMsg, err := build.Parse(Version, msg.GetPayloadByMove())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandshakeNet.log.Warn("Failed to parse Version message")
|
HandshakeNet.log.Warn("Failed to parse Version message")
|
||||||
|
|
||||||
HandshakeNet.net.DelPeer(addr)
|
HandshakeNet.net.DelPeer(peer)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if networkID := pMsg.Get(NetworkID).(uint32); networkID != HandshakeNet.networkID {
|
if networkID := pMsg.Get(NetworkID).(uint32); networkID != HandshakeNet.networkID {
|
||||||
HandshakeNet.log.Warn("Peer's network ID doesn't match our networkID: Peer's = %d ; Ours = %d", networkID, HandshakeNet.networkID)
|
HandshakeNet.log.Warn("Peer's network ID doesn't match our networkID: Peer's = %d ; Ours = %d", networkID, HandshakeNet.networkID)
|
||||||
|
|
||||||
HandshakeNet.net.DelPeer(addr)
|
HandshakeNet.net.DelPeer(peer)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,36 +710,35 @@ func version(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.P
|
||||||
if peerTime := float64(pMsg.Get(MyTime).(uint64)); math.Abs(peerTime-myTime) > MaxClockDifference.Seconds() {
|
if peerTime := float64(pMsg.Get(MyTime).(uint64)); math.Abs(peerTime-myTime) > MaxClockDifference.Seconds() {
|
||||||
HandshakeNet.log.Warn("Peer's clock is too far out of sync with mine. His = %d, Mine = %d (seconds)", uint64(peerTime), uint64(myTime))
|
HandshakeNet.log.Warn("Peer's clock is too far out of sync with mine. His = %d, Mine = %d (seconds)", uint64(peerTime), uint64(myTime))
|
||||||
|
|
||||||
HandshakeNet.net.DelPeer(addr)
|
HandshakeNet.net.DelPeer(peer)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if peerVersion := pMsg.Get(VersionStr).(string); !checkCompatibility(CurrentVersion, peerVersion) {
|
if peerVersion := pMsg.Get(VersionStr).(string); !HandshakeNet.checkCompatibility(peerVersion) {
|
||||||
HandshakeNet.log.Warn("Bad version")
|
HandshakeNet.log.Debug("Dropping connection due to an incompatible version from peer")
|
||||||
|
|
||||||
HandshakeNet.net.DelPeer(addr)
|
HandshakeNet.net.DelPeer(peer)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeNet.log.Debug("Finishing handshake with %s", toIPDesc(addr))
|
ip := pMsg.Get(IP).(utils.IPDesc)
|
||||||
|
|
||||||
HandshakeNet.SendPeerList(addr)
|
HandshakeNet.log.Debug("Finishing handshake with %s", ip)
|
||||||
HandshakeNet.connections.Add(addr, cert)
|
|
||||||
|
|
||||||
HandshakeNet.versionTimeout.Remove(cert.LongID())
|
HandshakeNet.SendPeerList(peer)
|
||||||
|
HandshakeNet.connections.Add(peer, id, ip)
|
||||||
|
HandshakeNet.numPeers.Set(float64(HandshakeNet.connections.Len()))
|
||||||
|
|
||||||
if !HandshakeNet.enableStaking {
|
if !HandshakeNet.enableStaking {
|
||||||
HandshakeNet.vdrs.Add(validators.NewValidator(cert, 1))
|
HandshakeNet.vdrs.Add(validators.NewValidator(id, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeNet.numPeers.Set(float64(HandshakeNet.connections.Len()))
|
|
||||||
|
|
||||||
HandshakeNet.awaitingLock.Lock()
|
HandshakeNet.awaitingLock.Lock()
|
||||||
defer HandshakeNet.awaitingLock.Unlock()
|
defer HandshakeNet.awaitingLock.Unlock()
|
||||||
|
|
||||||
for i := 0; i < len(HandshakeNet.awaiting); i++ {
|
for i := 0; i < len(HandshakeNet.awaiting); i++ {
|
||||||
awaiting := HandshakeNet.awaiting[i]
|
awaiting := HandshakeNet.awaiting[i]
|
||||||
awaiting.Add(cert)
|
awaiting.Add(id)
|
||||||
if !awaiting.Ready() {
|
if !awaiting.Ready() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -539,13 +759,10 @@ func getPeerList(_ *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.
|
||||||
HandshakeNet.numGetPeerlistReceived.Inc()
|
HandshakeNet.numGetPeerlistReceived.Inc()
|
||||||
|
|
||||||
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn))
|
||||||
addr := conn.GetPeerAddr(false)
|
peer := conn.GetPeerID(false)
|
||||||
defer addr.Free()
|
defer peer.Free()
|
||||||
if addr.IsNull() {
|
|
||||||
HandshakeNet.log.Warn("GetPeerList sent from unknown peer")
|
HandshakeNet.SendPeerList(peer)
|
||||||
return
|
|
||||||
}
|
|
||||||
HandshakeNet.SendPeerList(addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// peerList handles the recept of a peerList message
|
// peerList handles the recept of a peerList message
|
||||||
|
@ -565,27 +782,15 @@ func peerList(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.
|
||||||
ips := pMsg.Get(Peers).([]utils.IPDesc)
|
ips := pMsg.Get(Peers).([]utils.IPDesc)
|
||||||
cErr := salticidae.NewError()
|
cErr := salticidae.NewError()
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
HandshakeNet.log.Verbo("Trying to adding peer %s", ip)
|
|
||||||
addr := salticidae.NewNetAddrFromIPPortString(ip.String(), true, &cErr)
|
addr := salticidae.NewNetAddrFromIPPortString(ip.String(), true, &cErr)
|
||||||
if cErr.GetCode() == 0 && !HandshakeNet.myAddr.IsEq(addr) { // Make sure not to connect to myself
|
|
||||||
ip := toIPDesc(addr)
|
|
||||||
ipCert := toShortID(ip)
|
|
||||||
|
|
||||||
if !HandshakeNet.pending.ContainsIP(addr) && !HandshakeNet.connections.ContainsIP(addr) {
|
if cErr.GetCode() != 0 || HandshakeNet.myAddr.IsEq(addr) {
|
||||||
HandshakeNet.log.Debug("Adding peer %s", ip)
|
// Make sure not to connect to myself
|
||||||
|
continue
|
||||||
HandshakeNet.reconnectTimeout.Put(ipCert.LongID(), func() {
|
|
||||||
HandshakeNet.net.DelPeer(addr)
|
|
||||||
})
|
|
||||||
HandshakeNet.net.AddPeer(addr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMsgCert(_conn *C.struct_msgnetwork_conn_t) ids.ShortID {
|
HandshakeNet.Connect(addr)
|
||||||
conn := salticidae.MsgNetworkConnFromC(salticidae.CMsgNetworkConn(_conn))
|
}
|
||||||
return getCert(conn.GetPeerCert())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPeerCert(_conn *C.struct_peernetwork_conn_t) ids.ShortID {
|
func getPeerCert(_conn *C.struct_peernetwork_conn_t) ids.ShortID {
|
||||||
|
@ -595,29 +800,16 @@ func getPeerCert(_conn *C.struct_peernetwork_conn_t) ids.ShortID {
|
||||||
|
|
||||||
func getCert(cert salticidae.X509) ids.ShortID {
|
func getCert(cert salticidae.X509) ids.ShortID {
|
||||||
der := cert.GetDer(false)
|
der := cert.GetDer(false)
|
||||||
defer der.Free()
|
|
||||||
|
|
||||||
certDS := salticidae.NewDataStreamMovedFromByteArray(der, false)
|
certDS := salticidae.NewDataStreamMovedFromByteArray(der, false)
|
||||||
defer certDS.Free()
|
|
||||||
|
|
||||||
certBytes := certDS.GetDataInPlace(certDS.Size()).Get()
|
certBytes := certDS.GetDataInPlace(certDS.Size()).Get()
|
||||||
certID, err := ids.ToShortID(hashing.PubkeyBytesToAddress(certBytes))
|
certID, err := ids.ToShortID(hashing.PubkeyBytesToAddress(certBytes))
|
||||||
|
|
||||||
|
certDS.Free()
|
||||||
|
der.Free()
|
||||||
HandshakeNet.log.AssertNoError(err)
|
HandshakeNet.log.AssertNoError(err)
|
||||||
return certID
|
return certID
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkCompatibility Check to make sure that the peer and I speak the same language.
|
|
||||||
func checkCompatibility(myVersion string, peerVersion string) bool {
|
|
||||||
// At the moment, we are all compatible.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func toAddr(ip utils.IPDesc, autoFree bool) salticidae.NetAddr {
|
|
||||||
err := salticidae.NewError()
|
|
||||||
addr := salticidae.NewNetAddrFromIPPortString(ip.String(), autoFree, &err)
|
|
||||||
HandshakeNet.log.AssertTrue(err.GetCode() == 0, "IP Failed parsing")
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
func toShortID(ip utils.IPDesc) ids.ShortID {
|
func toShortID(ip utils.IPDesc) ids.ShortID {
|
||||||
return ids.NewShortID(hashing.ComputeHash160Array([]byte(ip.String())))
|
return ids.NewShortID(hashing.ComputeHash160Array([]byte(ip.String())))
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,12 +89,12 @@ func (s *Voting) Shutdown() { s.executor.Stop() }
|
||||||
|
|
||||||
// Accept is called after every consensus decision
|
// Accept is called after every consensus decision
|
||||||
func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error {
|
func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error {
|
||||||
addrs := []salticidae.NetAddr(nil)
|
peers := []salticidae.PeerID(nil)
|
||||||
|
|
||||||
allAddrs, allIDs := s.conns.RawConns()
|
allPeers, allIDs, _ := s.conns.Conns()
|
||||||
for i, id := range allIDs {
|
for i, id := range allIDs {
|
||||||
if !s.vdrs.Contains(id) {
|
if !s.vdrs.Contains(id) {
|
||||||
addrs = append(addrs, allAddrs[i])
|
peers = append(peers, allPeers[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,25 +109,25 @@ func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error {
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nContainer ID: %s"+
|
"\nContainer ID: %s"+
|
||||||
"\nContainer:\n%s",
|
"\nContainer:\n%s",
|
||||||
len(addrs),
|
len(peers),
|
||||||
chainID,
|
chainID,
|
||||||
containerID,
|
containerID,
|
||||||
formatting.DumpBytes{Bytes: container},
|
formatting.DumpBytes{Bytes: container},
|
||||||
)
|
)
|
||||||
s.send(msg, addrs...)
|
s.send(msg, peers...)
|
||||||
s.numPutSent.Add(float64(len(addrs)))
|
s.numPutSent.Add(float64(len(peers)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAcceptedFrontier implements the Sender interface.
|
// GetAcceptedFrontier implements the Sender interface.
|
||||||
func (s *Voting) GetAcceptedFrontier(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32) {
|
func (s *Voting) GetAcceptedFrontier(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32) {
|
||||||
addrs := []salticidae.NetAddr(nil)
|
peers := []salticidae.PeerID(nil)
|
||||||
validatorIDList := validatorIDs.List()
|
validatorIDList := validatorIDs.List()
|
||||||
for _, validatorID := range validatorIDList {
|
for _, validatorID := range validatorIDList {
|
||||||
vID := validatorID
|
vID := validatorID
|
||||||
if addr, exists := s.conns.GetIP(vID); exists {
|
if peer, exists := s.conns.GetPeerID(vID); exists {
|
||||||
addrs = append(addrs, addr)
|
peers = append(peers, peer)
|
||||||
s.log.Verbo("Sending a GetAcceptedFrontier to %s", toIPDesc(addr))
|
s.log.Verbo("Sending a GetAcceptedFrontier to %s", vID)
|
||||||
} else {
|
} else {
|
||||||
s.log.Debug("Attempted to send a GetAcceptedFrontier message to a disconnected validator: %s", vID)
|
s.log.Debug("Attempted to send a GetAcceptedFrontier message to a disconnected validator: %s", vID)
|
||||||
s.executor.Add(func() { s.router.GetAcceptedFrontierFailed(vID, chainID, requestID) })
|
s.executor.Add(func() { s.router.GetAcceptedFrontierFailed(vID, chainID, requestID) })
|
||||||
|
@ -142,17 +142,17 @@ func (s *Voting) GetAcceptedFrontier(validatorIDs ids.ShortSet, chainID ids.ID,
|
||||||
"\nNumber of Validators: %d"+
|
"\nNumber of Validators: %d"+
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d",
|
"\nRequest ID: %d",
|
||||||
len(addrs),
|
len(peers),
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
)
|
)
|
||||||
s.send(msg, addrs...)
|
s.send(msg, peers...)
|
||||||
s.numGetAcceptedFrontierSent.Add(float64(len(addrs)))
|
s.numGetAcceptedFrontierSent.Add(float64(len(peers)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptedFrontier implements the Sender interface.
|
// AcceptedFrontier implements the Sender interface.
|
||||||
func (s *Voting) AcceptedFrontier(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set) {
|
func (s *Voting) AcceptedFrontier(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set) {
|
||||||
addr, exists := s.conns.GetIP(validatorID)
|
peer, exists := s.conns.GetPeerID(validatorID)
|
||||||
if !exists {
|
if !exists {
|
||||||
s.log.Debug("Attempted to send an AcceptedFrontier message to a disconnected validator: %s", validatorID)
|
s.log.Debug("Attempted to send an AcceptedFrontier message to a disconnected validator: %s", validatorID)
|
||||||
return // Validator is not connected
|
return // Validator is not connected
|
||||||
|
@ -167,29 +167,27 @@ func (s *Voting) AcceptedFrontier(validatorID ids.ShortID, chainID ids.ID, reque
|
||||||
|
|
||||||
s.log.Verbo("Sending an AcceptedFrontier message."+
|
s.log.Verbo("Sending an AcceptedFrontier message."+
|
||||||
"\nValidator: %s"+
|
"\nValidator: %s"+
|
||||||
"\nDestination: %s"+
|
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nContainer IDs: %s",
|
"\nContainer IDs: %s",
|
||||||
validatorID,
|
validatorID,
|
||||||
toIPDesc(addr),
|
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
containerIDs,
|
containerIDs,
|
||||||
)
|
)
|
||||||
s.send(msg, addr)
|
s.send(msg, peer)
|
||||||
s.numAcceptedFrontierSent.Inc()
|
s.numAcceptedFrontierSent.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccepted implements the Sender interface.
|
// GetAccepted implements the Sender interface.
|
||||||
func (s *Voting) GetAccepted(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerIDs ids.Set) {
|
func (s *Voting) GetAccepted(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerIDs ids.Set) {
|
||||||
addrs := []salticidae.NetAddr(nil)
|
peers := []salticidae.PeerID(nil)
|
||||||
validatorIDList := validatorIDs.List()
|
validatorIDList := validatorIDs.List()
|
||||||
for _, validatorID := range validatorIDList {
|
for _, validatorID := range validatorIDList {
|
||||||
vID := validatorID
|
vID := validatorID
|
||||||
if addr, exists := s.conns.GetIP(validatorID); exists {
|
if peer, exists := s.conns.GetPeerID(validatorID); exists {
|
||||||
addrs = append(addrs, addr)
|
peers = append(peers, peer)
|
||||||
s.log.Verbo("Sending a GetAccepted to %s", toIPDesc(addr))
|
s.log.Verbo("Sending a GetAccepted to %s", vID)
|
||||||
} else {
|
} else {
|
||||||
s.log.Debug("Attempted to send a GetAccepted message to a disconnected validator: %s", vID)
|
s.log.Debug("Attempted to send a GetAccepted message to a disconnected validator: %s", vID)
|
||||||
s.executor.Add(func() { s.router.GetAcceptedFailed(vID, chainID, requestID) })
|
s.executor.Add(func() { s.router.GetAcceptedFailed(vID, chainID, requestID) })
|
||||||
|
@ -199,8 +197,8 @@ func (s *Voting) GetAccepted(validatorIDs ids.ShortSet, chainID ids.ID, requestI
|
||||||
build := Builder{}
|
build := Builder{}
|
||||||
msg, err := build.GetAccepted(chainID, requestID, containerIDs)
|
msg, err := build.GetAccepted(chainID, requestID, containerIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, addr := range addrs {
|
for _, peer := range peers {
|
||||||
if validatorID, exists := s.conns.GetID(addr); exists {
|
if validatorID, exists := s.conns.GetID(peer); exists {
|
||||||
s.executor.Add(func() { s.router.GetAcceptedFailed(validatorID, chainID, requestID) })
|
s.executor.Add(func() { s.router.GetAcceptedFailed(validatorID, chainID, requestID) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,18 +211,18 @@ func (s *Voting) GetAccepted(validatorIDs ids.ShortSet, chainID ids.ID, requestI
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nContainer IDs:%s",
|
"\nContainer IDs:%s",
|
||||||
len(addrs),
|
len(peers),
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
containerIDs,
|
containerIDs,
|
||||||
)
|
)
|
||||||
s.send(msg, addrs...)
|
s.send(msg, peers...)
|
||||||
s.numGetAcceptedSent.Add(float64(len(addrs)))
|
s.numGetAcceptedSent.Add(float64(len(peers)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accepted implements the Sender interface.
|
// Accepted implements the Sender interface.
|
||||||
func (s *Voting) Accepted(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set) {
|
func (s *Voting) Accepted(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set) {
|
||||||
addr, exists := s.conns.GetIP(validatorID)
|
peer, exists := s.conns.GetPeerID(validatorID)
|
||||||
if !exists {
|
if !exists {
|
||||||
s.log.Debug("Attempted to send an Accepted message to a disconnected validator: %s", validatorID)
|
s.log.Debug("Attempted to send an Accepted message to a disconnected validator: %s", validatorID)
|
||||||
return // Validator is not connected
|
return // Validator is not connected
|
||||||
|
@ -239,23 +237,21 @@ func (s *Voting) Accepted(validatorID ids.ShortID, chainID ids.ID, requestID uin
|
||||||
|
|
||||||
s.log.Verbo("Sending an Accepted message."+
|
s.log.Verbo("Sending an Accepted message."+
|
||||||
"\nValidator: %s"+
|
"\nValidator: %s"+
|
||||||
"\nDestination: %s"+
|
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nContainer IDs: %s",
|
"\nContainer IDs: %s",
|
||||||
validatorID,
|
validatorID,
|
||||||
toIPDesc(addr),
|
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
containerIDs,
|
containerIDs,
|
||||||
)
|
)
|
||||||
s.send(msg, addr)
|
s.send(msg, peer)
|
||||||
s.numAcceptedSent.Inc()
|
s.numAcceptedSent.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get implements the Sender interface.
|
// Get implements the Sender interface.
|
||||||
func (s *Voting) Get(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID) {
|
func (s *Voting) Get(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID) {
|
||||||
addr, exists := s.conns.GetIP(validatorID)
|
peer, exists := s.conns.GetPeerID(validatorID)
|
||||||
if !exists {
|
if !exists {
|
||||||
s.log.Debug("Attempted to send a Get message to a disconnected validator: %s", validatorID)
|
s.log.Debug("Attempted to send a Get message to a disconnected validator: %s", validatorID)
|
||||||
s.executor.Add(func() { s.router.GetFailed(validatorID, chainID, requestID, containerID) })
|
s.executor.Add(func() { s.router.GetFailed(validatorID, chainID, requestID, containerID) })
|
||||||
|
@ -268,23 +264,21 @@ func (s *Voting) Get(validatorID ids.ShortID, chainID ids.ID, requestID uint32,
|
||||||
|
|
||||||
s.log.Verbo("Sending a Get message."+
|
s.log.Verbo("Sending a Get message."+
|
||||||
"\nValidator: %s"+
|
"\nValidator: %s"+
|
||||||
"\nDestination: %s"+
|
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nContainer ID: %s",
|
"\nContainer ID: %s",
|
||||||
validatorID,
|
validatorID,
|
||||||
toIPDesc(addr),
|
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
containerID,
|
containerID,
|
||||||
)
|
)
|
||||||
s.send(msg, addr)
|
s.send(msg, peer)
|
||||||
s.numGetSent.Inc()
|
s.numGetSent.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put implements the Sender interface.
|
// Put implements the Sender interface.
|
||||||
func (s *Voting) Put(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte) {
|
func (s *Voting) Put(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte) {
|
||||||
addr, exists := s.conns.GetIP(validatorID)
|
peer, exists := s.conns.GetPeerID(validatorID)
|
||||||
if !exists {
|
if !exists {
|
||||||
s.log.Debug("Attempted to send a Container message to a disconnected validator: %s", validatorID)
|
s.log.Debug("Attempted to send a Container message to a disconnected validator: %s", validatorID)
|
||||||
return // Validator is not connected
|
return // Validator is not connected
|
||||||
|
@ -299,31 +293,29 @@ func (s *Voting) Put(validatorID ids.ShortID, chainID ids.ID, requestID uint32,
|
||||||
|
|
||||||
s.log.Verbo("Sending a Container message."+
|
s.log.Verbo("Sending a Container message."+
|
||||||
"\nValidator: %s"+
|
"\nValidator: %s"+
|
||||||
"\nDestination: %s"+
|
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nContainer ID: %s"+
|
"\nContainer ID: %s"+
|
||||||
"\nContainer:\n%s",
|
"\nContainer:\n%s",
|
||||||
validatorID,
|
validatorID,
|
||||||
toIPDesc(addr),
|
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
containerID,
|
containerID,
|
||||||
formatting.DumpBytes{Bytes: container},
|
formatting.DumpBytes{Bytes: container},
|
||||||
)
|
)
|
||||||
s.send(msg, addr)
|
s.send(msg, peer)
|
||||||
s.numPutSent.Inc()
|
s.numPutSent.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushQuery implements the Sender interface.
|
// PushQuery implements the Sender interface.
|
||||||
func (s *Voting) PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte) {
|
func (s *Voting) PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte) {
|
||||||
addrs := []salticidae.NetAddr(nil)
|
peers := []salticidae.PeerID(nil)
|
||||||
validatorIDList := validatorIDs.List()
|
validatorIDList := validatorIDs.List()
|
||||||
for _, validatorID := range validatorIDList {
|
for _, validatorID := range validatorIDList {
|
||||||
vID := validatorID
|
vID := validatorID
|
||||||
if addr, exists := s.conns.GetIP(vID); exists {
|
if peer, exists := s.conns.GetPeerID(vID); exists {
|
||||||
addrs = append(addrs, addr)
|
peers = append(peers, peer)
|
||||||
s.log.Verbo("Sending a PushQuery to %s", toIPDesc(addr))
|
s.log.Verbo("Sending a PushQuery to %s", vID)
|
||||||
} else {
|
} else {
|
||||||
s.log.Debug("Attempted to send a PushQuery message to a disconnected validator: %s", vID)
|
s.log.Debug("Attempted to send a PushQuery message to a disconnected validator: %s", vID)
|
||||||
s.executor.Add(func() { s.router.QueryFailed(vID, chainID, requestID) })
|
s.executor.Add(func() { s.router.QueryFailed(vID, chainID, requestID) })
|
||||||
|
@ -333,8 +325,8 @@ func (s *Voting) PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID
|
||||||
build := Builder{}
|
build := Builder{}
|
||||||
msg, err := build.PushQuery(chainID, requestID, containerID, container)
|
msg, err := build.PushQuery(chainID, requestID, containerID, container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, addr := range addrs {
|
for _, peer := range peers {
|
||||||
if validatorID, exists := s.conns.GetID(addr); exists {
|
if validatorID, exists := s.conns.GetID(peer); exists {
|
||||||
s.executor.Add(func() { s.router.QueryFailed(validatorID, chainID, requestID) })
|
s.executor.Add(func() { s.router.QueryFailed(validatorID, chainID, requestID) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,25 +340,25 @@ func (s *Voting) PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nContainer ID: %s"+
|
"\nContainer ID: %s"+
|
||||||
"\nContainer:\n%s",
|
"\nContainer:\n%s",
|
||||||
len(addrs),
|
len(peers),
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
containerID,
|
containerID,
|
||||||
formatting.DumpBytes{Bytes: container},
|
formatting.DumpBytes{Bytes: container},
|
||||||
)
|
)
|
||||||
s.send(msg, addrs...)
|
s.send(msg, peers...)
|
||||||
s.numPushQuerySent.Add(float64(len(addrs)))
|
s.numPushQuerySent.Add(float64(len(peers)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PullQuery implements the Sender interface.
|
// PullQuery implements the Sender interface.
|
||||||
func (s *Voting) PullQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID) {
|
func (s *Voting) PullQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID) {
|
||||||
addrs := []salticidae.NetAddr(nil)
|
peers := []salticidae.PeerID(nil)
|
||||||
validatorIDList := validatorIDs.List()
|
validatorIDList := validatorIDs.List()
|
||||||
for _, validatorID := range validatorIDList {
|
for _, validatorID := range validatorIDList {
|
||||||
vID := validatorID
|
vID := validatorID
|
||||||
if addr, exists := s.conns.GetIP(vID); exists {
|
if peer, exists := s.conns.GetPeerID(vID); exists {
|
||||||
addrs = append(addrs, addr)
|
peers = append(peers, peer)
|
||||||
s.log.Verbo("Sending a PullQuery to %s", toIPDesc(addr))
|
s.log.Verbo("Sending a PullQuery to %s", vID)
|
||||||
} else {
|
} else {
|
||||||
s.log.Warn("Attempted to send a PullQuery message to a disconnected validator: %s", vID)
|
s.log.Warn("Attempted to send a PullQuery message to a disconnected validator: %s", vID)
|
||||||
s.executor.Add(func() { s.router.QueryFailed(vID, chainID, requestID) })
|
s.executor.Add(func() { s.router.QueryFailed(vID, chainID, requestID) })
|
||||||
|
@ -382,18 +374,18 @@ func (s *Voting) PullQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nContainer ID: %s",
|
"\nContainer ID: %s",
|
||||||
len(addrs),
|
len(peers),
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
containerID,
|
containerID,
|
||||||
)
|
)
|
||||||
s.send(msg, addrs...)
|
s.send(msg, peers...)
|
||||||
s.numPullQuerySent.Add(float64(len(addrs)))
|
s.numPullQuerySent.Add(float64(len(peers)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chits implements the Sender interface.
|
// Chits implements the Sender interface.
|
||||||
func (s *Voting) Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32, votes ids.Set) {
|
func (s *Voting) Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32, votes ids.Set) {
|
||||||
addr, exists := s.conns.GetIP(validatorID)
|
peer, exists := s.conns.GetPeerID(validatorID)
|
||||||
if !exists {
|
if !exists {
|
||||||
s.log.Debug("Attempted to send a Chits message to a disconnected validator: %s", validatorID)
|
s.log.Debug("Attempted to send a Chits message to a disconnected validator: %s", validatorID)
|
||||||
return // Validator is not connected
|
return // Validator is not connected
|
||||||
|
@ -408,21 +400,19 @@ func (s *Voting) Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32
|
||||||
|
|
||||||
s.log.Verbo("Sending a Chits message."+
|
s.log.Verbo("Sending a Chits message."+
|
||||||
"\nValidator: %s"+
|
"\nValidator: %s"+
|
||||||
"\nDestination: %s"+
|
|
||||||
"\nChain: %s"+
|
"\nChain: %s"+
|
||||||
"\nRequest ID: %d"+
|
"\nRequest ID: %d"+
|
||||||
"\nNumber of Chits: %d",
|
"\nNumber of Chits: %d",
|
||||||
validatorID,
|
validatorID,
|
||||||
toIPDesc(addr),
|
|
||||||
chainID,
|
chainID,
|
||||||
requestID,
|
requestID,
|
||||||
votes.Len(),
|
votes.Len(),
|
||||||
)
|
)
|
||||||
s.send(msg, addr)
|
s.send(msg, peer)
|
||||||
s.numChitsSent.Inc()
|
s.numChitsSent.Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Voting) send(msg Msg, addrs ...salticidae.NetAddr) {
|
func (s *Voting) send(msg Msg, peers ...salticidae.PeerID) {
|
||||||
ds := msg.DataStream()
|
ds := msg.DataStream()
|
||||||
defer ds.Free()
|
defer ds.Free()
|
||||||
ba := salticidae.NewByteArrayMovedFromDataStream(ds, false)
|
ba := salticidae.NewByteArrayMovedFromDataStream(ds, false)
|
||||||
|
@ -430,12 +420,12 @@ func (s *Voting) send(msg Msg, addrs ...salticidae.NetAddr) {
|
||||||
cMsg := salticidae.NewMsgMovedFromByteArray(msg.Op(), ba, false)
|
cMsg := salticidae.NewMsgMovedFromByteArray(msg.Op(), ba, false)
|
||||||
defer cMsg.Free()
|
defer cMsg.Free()
|
||||||
|
|
||||||
switch len(addrs) {
|
switch len(peers) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
s.net.SendMsg(cMsg, addrs[0])
|
s.net.SendMsg(cMsg, peers[0])
|
||||||
default:
|
default:
|
||||||
s.net.MulticastMsgByMove(cMsg, addrs)
|
s.net.MulticastMsgByMove(cMsg, peers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,18 +610,16 @@ func chits(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.Poi
|
||||||
|
|
||||||
func (s *Voting) sanitize(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, op salticidae.Opcode) (ids.ShortID, ids.ID, uint32, Msg, error) {
|
func (s *Voting) sanitize(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, op salticidae.Opcode) (ids.ShortID, ids.ID, uint32, Msg, error) {
|
||||||
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn((*C.peernetwork_conn_t)(_conn)))
|
conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn((*C.peernetwork_conn_t)(_conn)))
|
||||||
addr := conn.GetPeerAddr(false)
|
peer := conn.GetPeerID(false)
|
||||||
defer addr.Free()
|
defer peer.Free()
|
||||||
if addr.IsNull() {
|
|
||||||
return ids.ShortID{}, ids.ID{}, 0, nil, errConnectionDropped
|
|
||||||
}
|
|
||||||
s.log.Verbo("Receiving message from %s", toIPDesc(addr))
|
|
||||||
|
|
||||||
validatorID, exists := s.conns.GetID(addr)
|
validatorID, exists := s.conns.GetID(peer)
|
||||||
if !exists {
|
if !exists {
|
||||||
return ids.ShortID{}, ids.ID{}, 0, nil, fmt.Errorf("message received from an un-registered source: %s", toIPDesc(addr))
|
return ids.ShortID{}, ids.ID{}, 0, nil, fmt.Errorf("message received from an un-registered peer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.log.Verbo("Receiving message from %s", validatorID)
|
||||||
|
|
||||||
msg := salticidae.MsgFromC(salticidae.CMsg(_msg))
|
msg := salticidae.MsgFromC(salticidae.CMsg(_msg))
|
||||||
codec := Codec{}
|
codec := Codec{}
|
||||||
pMsg, err := codec.Parse(op, msg.GetPayloadByMove())
|
pMsg, err := codec.Parse(op, msg.GetPayloadByMove())
|
||||||
|
|
|
@ -4,9 +4,8 @@
|
||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ava-labs/go-ethereum/p2p/nat"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/database"
|
"github.com/ava-labs/gecko/database"
|
||||||
|
"github.com/ava-labs/gecko/nat"
|
||||||
"github.com/ava-labs/gecko/snow/consensus/avalanche"
|
"github.com/ava-labs/gecko/snow/consensus/avalanche"
|
||||||
"github.com/ava-labs/gecko/snow/networking/router"
|
"github.com/ava-labs/gecko/snow/networking/router"
|
||||||
"github.com/ava-labs/gecko/utils"
|
"github.com/ava-labs/gecko/utils"
|
||||||
|
@ -16,7 +15,7 @@ import (
|
||||||
// Config contains all of the configurations of an Ava node.
|
// Config contains all of the configurations of an Ava node.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// protocol to use for opening the network interface
|
// protocol to use for opening the network interface
|
||||||
Nat nat.Interface
|
Nat nat.Router
|
||||||
|
|
||||||
// ID of the network this node should connect to
|
// ID of the network this node should connect to
|
||||||
NetworkID uint32
|
NetworkID uint32
|
||||||
|
@ -56,6 +55,9 @@ type Config struct {
|
||||||
// Logging configuration
|
// Logging configuration
|
||||||
LoggingConfig logging.Config
|
LoggingConfig logging.Config
|
||||||
|
|
||||||
|
// Plugin directory
|
||||||
|
PluginDir string
|
||||||
|
|
||||||
// Consensus configuration
|
// Consensus configuration
|
||||||
ConsensusParams avalanche.Parameters
|
ConsensusParams avalanche.Parameters
|
||||||
|
|
||||||
|
|
29
node/node.go
29
node/node.go
|
@ -5,7 +5,7 @@ package node
|
||||||
|
|
||||||
// #include "salticidae/network.h"
|
// #include "salticidae/network.h"
|
||||||
// void onTerm(int sig, void *);
|
// void onTerm(int sig, void *);
|
||||||
// void errorHandler(SalticidaeCError *, bool, void *);
|
// void errorHandler(SalticidaeCError *, bool, int32_t, void *);
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -14,6 +14,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -39,10 +40,10 @@ import (
|
||||||
"github.com/ava-labs/gecko/utils/wrappers"
|
"github.com/ava-labs/gecko/utils/wrappers"
|
||||||
"github.com/ava-labs/gecko/vms"
|
"github.com/ava-labs/gecko/vms"
|
||||||
"github.com/ava-labs/gecko/vms/avm"
|
"github.com/ava-labs/gecko/vms/avm"
|
||||||
"github.com/ava-labs/gecko/vms/evm"
|
|
||||||
"github.com/ava-labs/gecko/vms/nftfx"
|
"github.com/ava-labs/gecko/vms/nftfx"
|
||||||
"github.com/ava-labs/gecko/vms/platformvm"
|
"github.com/ava-labs/gecko/vms/platformvm"
|
||||||
"github.com/ava-labs/gecko/vms/propertyfx"
|
"github.com/ava-labs/gecko/vms/propertyfx"
|
||||||
|
"github.com/ava-labs/gecko/vms/rpcchainvm"
|
||||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||||
"github.com/ava-labs/gecko/vms/spchainvm"
|
"github.com/ava-labs/gecko/vms/spchainvm"
|
||||||
"github.com/ava-labs/gecko/vms/spdagvm"
|
"github.com/ava-labs/gecko/vms/spdagvm"
|
||||||
|
@ -129,14 +130,14 @@ func onTerm(C.int, unsafe.Pointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//export errorHandler
|
//export errorHandler
|
||||||
func errorHandler(_err *C.struct_SalticidaeCError, fatal C.bool, _ unsafe.Pointer) {
|
func errorHandler(_err *C.struct_SalticidaeCError, fatal C.bool, asyncID C.int32_t, _ unsafe.Pointer) {
|
||||||
err := (*salticidae.Error)(unsafe.Pointer(_err))
|
err := (*salticidae.Error)(unsafe.Pointer(_err))
|
||||||
if fatal {
|
if fatal {
|
||||||
MainNode.Log.Fatal("Error during async call: %s", salticidae.StrError(err.GetCode()))
|
MainNode.Log.Fatal("Error during async call: %s", salticidae.StrError(err.GetCode()))
|
||||||
MainNode.EC.Stop()
|
MainNode.EC.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
MainNode.Log.Error("Error during async call: %s", salticidae.StrError(err.GetCode()))
|
MainNode.Log.Debug("Error during async with ID %d call: %s", asyncID, salticidae.StrError(err.GetCode()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) initNetlib() error {
|
func (n *Node) initNetlib() error {
|
||||||
|
@ -151,9 +152,12 @@ func (n *Node) initNetlib() error {
|
||||||
|
|
||||||
// Create peer network config, may have tls enabled
|
// Create peer network config, may have tls enabled
|
||||||
peerConfig := salticidae.NewPeerNetworkConfig()
|
peerConfig := salticidae.NewPeerNetworkConfig()
|
||||||
|
peerConfig.ConnTimeout(60)
|
||||||
|
|
||||||
|
msgConfig := peerConfig.AsMsgNetworkConfig()
|
||||||
|
msgConfig.MaxMsgSize(maxMessageSize)
|
||||||
|
|
||||||
if n.Config.EnableStaking {
|
if n.Config.EnableStaking {
|
||||||
msgConfig := peerConfig.AsMsgNetworkConfig()
|
|
||||||
msgConfig.MaxMsgSize(maxMessageSize)
|
|
||||||
msgConfig.EnableTLS(true)
|
msgConfig.EnableTLS(true)
|
||||||
msgConfig.TLSKeyFile(n.Config.StakingKeyFile)
|
msgConfig.TLSKeyFile(n.Config.StakingKeyFile)
|
||||||
msgConfig.TLSCertFile(n.Config.StakingCertFile)
|
msgConfig.TLSCertFile(n.Config.StakingCertFile)
|
||||||
|
@ -252,7 +256,7 @@ func (n *Node) StartConsensusServer() error {
|
||||||
// Listen for P2P messages
|
// Listen for P2P messages
|
||||||
n.PeerNet.Listen(serverIP, &err)
|
n.PeerNet.Listen(serverIP, &err)
|
||||||
if code := err.GetCode(); code != 0 {
|
if code := err.GetCode(); code != 0 {
|
||||||
return fmt.Errorf("failed to start consensus server: %s", salticidae.StrError(code))
|
return fmt.Errorf("failed to listen on consensus server at %s: %s", n.Config.StakingIP, salticidae.StrError(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a server to handle throughput tests if configuration says to. Disabled by default.
|
// Start a server to handle throughput tests if configuration says to. Disabled by default.
|
||||||
|
@ -266,18 +270,19 @@ func (n *Node) StartConsensusServer() error {
|
||||||
|
|
||||||
n.ClientNet.Listen(clientIP, &err)
|
n.ClientNet.Listen(clientIP, &err)
|
||||||
if code := err.GetCode(); code != 0 {
|
if code := err.GetCode(); code != 0 {
|
||||||
return fmt.Errorf("failed to listen on xput server: %s", salticidae.StrError(code))
|
return fmt.Errorf("failed to listen on xput server at 127.0.0.1:%d: %s", n.Config.ThroughputPort, salticidae.StrError(code))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add bootstrap nodes to the peer network
|
// Add bootstrap nodes to the peer network
|
||||||
for _, peer := range n.Config.BootstrapPeers {
|
for _, peer := range n.Config.BootstrapPeers {
|
||||||
if !peer.IP.Equal(n.Config.StakingIP) {
|
if !peer.IP.Equal(n.Config.StakingIP) {
|
||||||
bootstrapIP := salticidae.NewNetAddrFromIPPortString(peer.IP.String(), true, &err)
|
bootstrapAddr := salticidae.NewNetAddrFromIPPortString(peer.IP.String(), true, &err)
|
||||||
if code := err.GetCode(); code != 0 {
|
if code := err.GetCode(); code != 0 {
|
||||||
return fmt.Errorf("failed to create bootstrap ip addr: %s", salticidae.StrError(code))
|
return fmt.Errorf("failed to create bootstrap ip addr: %s", salticidae.StrError(code))
|
||||||
}
|
}
|
||||||
n.PeerNet.AddPeer(bootstrapIP)
|
|
||||||
|
n.ValidatorAPI.Connect(bootstrapAddr)
|
||||||
} else {
|
} else {
|
||||||
n.Log.Error("can't add self as a bootstrapper")
|
n.Log.Error("can't add self as a bootstrapper")
|
||||||
}
|
}
|
||||||
|
@ -359,7 +364,7 @@ func (n *Node) initNodeID() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the vmManager and register the following vms:
|
// Create the vmManager and register the following vms:
|
||||||
// AVM, EVM, Simple Payments DAG, Simple Payments Chain
|
// AVM, Simple Payments DAG, Simple Payments Chain
|
||||||
// The Platform VM is registered in initStaking because
|
// The Platform VM is registered in initStaking because
|
||||||
// its factory needs to reference n.chainManager, which is nil right now
|
// its factory needs to reference n.chainManager, which is nil right now
|
||||||
func (n *Node) initVMManager() error {
|
func (n *Node) initVMManager() error {
|
||||||
|
@ -376,7 +381,7 @@ func (n *Node) initVMManager() error {
|
||||||
AVA: avaAssetID,
|
AVA: avaAssetID,
|
||||||
Platform: ids.Empty,
|
Platform: ids.Empty,
|
||||||
}),
|
}),
|
||||||
n.vmManager.RegisterVMFactory(evm.ID, &evm.Factory{}),
|
n.vmManager.RegisterVMFactory(genesis.EVMID, &rpcchainvm.Factory{Path: path.Join(n.Config.PluginDir, "evm")}),
|
||||||
n.vmManager.RegisterVMFactory(spdagvm.ID, &spdagvm.Factory{TxFee: n.Config.AvaTxFee}),
|
n.vmManager.RegisterVMFactory(spdagvm.ID, &spdagvm.Factory{TxFee: n.Config.AvaTxFee}),
|
||||||
n.vmManager.RegisterVMFactory(spchainvm.ID, &spchainvm.Factory{}),
|
n.vmManager.RegisterVMFactory(spchainvm.ID, &spchainvm.Factory{}),
|
||||||
n.vmManager.RegisterVMFactory(timestampvm.ID, ×tampvm.Factory{}),
|
n.vmManager.RegisterVMFactory(timestampvm.ID, ×tampvm.Factory{}),
|
||||||
|
|
|
@ -28,6 +28,7 @@ borealis_bootstrap:
|
||||||
staking_tls_enabled: true
|
staking_tls_enabled: true
|
||||||
staking_tls_key_file: "/home/ubuntu/keys/staker.key"
|
staking_tls_key_file: "/home/ubuntu/keys/staker.key"
|
||||||
staking_tls_cert_file: "/home/ubuntu/keys/staker.crt"
|
staking_tls_cert_file: "/home/ubuntu/keys/staker.crt"
|
||||||
|
plugin_dir: "/home/ubuntu/go/src/github.com/ava-labs/gecko/build/plugins"
|
||||||
log_dir: "/home/ubuntu/.gecko"
|
log_dir: "/home/ubuntu/.gecko"
|
||||||
log_level: debug
|
log_level: debug
|
||||||
snow_sample_size: 3
|
snow_sample_size: 3
|
||||||
|
@ -73,6 +74,7 @@ borealis_node:
|
||||||
staking_tls_enabled: true
|
staking_tls_enabled: true
|
||||||
staking_tls_key_file: "/home/ubuntu/keys/staker.key"
|
staking_tls_key_file: "/home/ubuntu/keys/staker.key"
|
||||||
staking_tls_cert_file: "/home/ubuntu/keys/staker.crt"
|
staking_tls_cert_file: "/home/ubuntu/keys/staker.crt"
|
||||||
|
plugin_dir: "/home/ubuntu/go/src/github.com/ava-labs/gecko/build/plugins"
|
||||||
log_dir: "/home/ubuntu/.gecko"
|
log_dir: "/home/ubuntu/.gecko"
|
||||||
log_level: debug
|
log_level: debug
|
||||||
snow_sample_size: 3
|
snow_sample_size: 3
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Update the network
|
||||||
|
connection: ssh
|
||||||
|
gather_facts: false
|
||||||
|
hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: Kill Node
|
||||||
|
command: killall ava
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
---
|
||||||
|
- name: Update the network
|
||||||
|
connection: ssh
|
||||||
|
gather_facts: false
|
||||||
|
hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: Ping node
|
||||||
|
shell: "ls"
|
||||||
|
environment:
|
||||||
|
PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin
|
|
@ -7,8 +7,8 @@
|
||||||
vars:
|
vars:
|
||||||
ava_binary: ~/go/src/github.com/ava-labs/gecko/build/ava
|
ava_binary: ~/go/src/github.com/ava-labs/gecko/build/ava
|
||||||
repo_folder: ~/go/src/github.com/ava-labs/gecko
|
repo_folder: ~/go/src/github.com/ava-labs/gecko
|
||||||
repo_name: ava-labs/gecko
|
repo_name: ava-labs/gecko-internal
|
||||||
repo_branch: cascade
|
repo_branch: platformvm-proposal-accept
|
||||||
tasks:
|
tasks:
|
||||||
- name: Kill Node
|
- name: Kill Node
|
||||||
command: killall ava
|
command: killall ava
|
||||||
|
@ -33,6 +33,6 @@
|
||||||
path: "{{ log_dir }}"
|
path: "{{ log_dir }}"
|
||||||
state: absent
|
state: absent
|
||||||
- name: Start node
|
- name: Start node
|
||||||
shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &"
|
shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --plugin-dir={{ plugin_dir }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &"
|
||||||
environment:
|
environment:
|
||||||
PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin
|
PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
borealis_bootstrap:
|
||||||
|
hosts:
|
||||||
|
bootstrap1:
|
||||||
|
ansible_host: 3.84.129.247
|
||||||
|
staking_tls_key_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker1.key"
|
||||||
|
staking_tls_cert_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker1.crt"
|
||||||
|
vars:
|
||||||
|
ansible_connection: ssh
|
||||||
|
ansible_user: ubuntu
|
||||||
|
|
||||||
|
network_id: "local"
|
||||||
|
api_admin_enabled: true
|
||||||
|
api_keystore_enabled: true
|
||||||
|
api_metrics_enabled: true
|
||||||
|
ava_tx_fee: 0
|
||||||
|
assertions_enabled: true
|
||||||
|
signature_verification_enabled: true
|
||||||
|
db_enabled: true
|
||||||
|
db_dir: "/home/ubuntu/db"
|
||||||
|
http_port: 21000
|
||||||
|
http_tls_enabled: false
|
||||||
|
http_tls_key_file: ""
|
||||||
|
http_tls_cert_file: ""
|
||||||
|
bootstrap_ips: ""
|
||||||
|
bootstrap_ids: ""
|
||||||
|
staking_port: 21001
|
||||||
|
staking_tls_enabled: true
|
||||||
|
staking_tls_key_file: "/home/ubuntu/keys/staker.key"
|
||||||
|
staking_tls_cert_file: "/home/ubuntu/keys/staker.crt"
|
||||||
|
plugin_dir: "/home/ubuntu/go/src/github.com/ava-labs/gecko/build/plugins"
|
||||||
|
log_dir: "/home/ubuntu/.gecko"
|
||||||
|
log_level: debug
|
||||||
|
snow_sample_size: 3
|
||||||
|
snow_quorum_size: 2
|
||||||
|
snow_virtuous_commit_threshold: 20
|
||||||
|
snow_rogue_commit_threshold: 30
|
||||||
|
snow_avalanche_num_parents: 5
|
||||||
|
snow_avalanche_batch_size: 30
|
||||||
|
api_ipcs_enabled: false
|
||||||
|
xput_server_enabled: false
|
||||||
|
xput_server_port: 21002
|
||||||
|
|
||||||
|
borealis_node:
|
||||||
|
hosts:
|
||||||
|
node1:
|
||||||
|
ansible_host: 35.153.99.244
|
||||||
|
staking_tls_key_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker2.key"
|
||||||
|
staking_tls_cert_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker2.crt"
|
||||||
|
node2:
|
||||||
|
ansible_host: 34.201.137.119
|
||||||
|
staking_tls_key_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker3.key"
|
||||||
|
staking_tls_cert_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker3.crt"
|
||||||
|
node3:
|
||||||
|
ansible_host: 54.146.1.110
|
||||||
|
staking_tls_key_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker4.key"
|
||||||
|
staking_tls_cert_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker4.crt"
|
||||||
|
node4:
|
||||||
|
ansible_host: 54.91.255.231
|
||||||
|
staking_tls_key_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker5.key"
|
||||||
|
staking_tls_cert_file: "/home/ubuntu/go/src/github.com/ava-labs/gecko/keys/local/staker5.crt"
|
||||||
|
vars:
|
||||||
|
ansible_connection: ssh
|
||||||
|
ansible_user: ubuntu
|
||||||
|
|
||||||
|
network_id: "local"
|
||||||
|
api_admin_enabled: true
|
||||||
|
api_keystore_enabled: true
|
||||||
|
api_metrics_enabled: true
|
||||||
|
ava_tx_fee: 0
|
||||||
|
assertions_enabled: true
|
||||||
|
signature_verification_enabled: true
|
||||||
|
db_enabled: true
|
||||||
|
db_dir: "/home/ubuntu/db"
|
||||||
|
http_port: 21000
|
||||||
|
http_tls_enabled: false
|
||||||
|
http_tls_key_file: ""
|
||||||
|
http_tls_cert_file: ""
|
||||||
|
bootstrap_ips: "3.84.129.247:21001"
|
||||||
|
bootstrap_ids: "7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg"
|
||||||
|
staking_port: 21001
|
||||||
|
staking_tls_enabled: true
|
||||||
|
staking_tls_key_file: "/home/ubuntu/keys/staker.key"
|
||||||
|
staking_tls_cert_file: "/home/ubuntu/keys/staker.crt"
|
||||||
|
plugin_dir: "/home/ubuntu/go/src/github.com/ava-labs/gecko/build/plugins"
|
||||||
|
log_dir: "/home/ubuntu/.gecko"
|
||||||
|
log_level: debug
|
||||||
|
snow_sample_size: 3
|
||||||
|
snow_quorum_size: 2
|
||||||
|
snow_virtuous_commit_threshold: 20
|
||||||
|
snow_rogue_commit_threshold: 30
|
||||||
|
snow_avalanche_num_parents: 5
|
||||||
|
snow_avalanche_batch_size: 30
|
||||||
|
api_ipcs_enabled: false
|
||||||
|
xput_server_enabled: false
|
||||||
|
xput_server_port: 21002
|
|
@ -7,8 +7,8 @@
|
||||||
vars:
|
vars:
|
||||||
ava_binary: ~/go/src/github.com/ava-labs/gecko/build/ava
|
ava_binary: ~/go/src/github.com/ava-labs/gecko/build/ava
|
||||||
repo_folder: ~/go/src/github.com/ava-labs/gecko
|
repo_folder: ~/go/src/github.com/ava-labs/gecko
|
||||||
repo_name: ava-labs/gecko
|
repo_name: ava-labs/gecko-internal
|
||||||
repo_branch: cascade
|
repo_branch: platformvm-proposal-accept
|
||||||
tasks:
|
tasks:
|
||||||
- name: Kill Node
|
- name: Kill Node
|
||||||
command: killall ava
|
command: killall ava
|
||||||
|
@ -25,6 +25,6 @@
|
||||||
environment:
|
environment:
|
||||||
PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin
|
PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin
|
||||||
- name: Start node
|
- name: Start node
|
||||||
shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &"
|
shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --plugin-dir={{ plugin_dir }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &"
|
||||||
environment:
|
environment:
|
||||||
PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin
|
PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin
|
||||||
|
|
|
@ -6,14 +6,14 @@ Start a number of AVA nodes on Amazon EC2
|
||||||
import boto3
|
import boto3
|
||||||
|
|
||||||
|
|
||||||
bootstapNode = "Borealis-Bootstrap"
|
bootstapNode = "Cascade-Bootstrap"
|
||||||
fullNode = "Borealis-Node"
|
fullNode = "Cascade-Node"
|
||||||
|
|
||||||
|
|
||||||
def runInstances(ec2, num: int, name: str):
|
def runInstances(ec2, num: int, name: str):
|
||||||
if num > 0:
|
if num > 0:
|
||||||
ec2.run_instances(
|
ec2.run_instances(
|
||||||
ImageId="ami-0badd1c10cb7673e9",
|
ImageId="ami-0c088c21fbcdb8a48",
|
||||||
InstanceType="c5.large",
|
InstanceType="c5.large",
|
||||||
MaxCount=num,
|
MaxCount=num,
|
||||||
MinCount=num,
|
MinCount=num,
|
||||||
|
@ -29,11 +29,9 @@ def runInstances(ec2, num: int, name: str):
|
||||||
def main():
|
def main():
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
description=__doc__,
|
parser.add_argument("numBootstraps", type=int)
|
||||||
)
|
parser.add_argument("numNodes", type=int)
|
||||||
parser.add_argument('numBootstraps', type=int)
|
|
||||||
parser.add_argument('numNodes', type=int)
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
ec2 = boto3.client("ec2")
|
ec2 = boto3.client("ec2")
|
||||||
|
|
|
@ -3,18 +3,39 @@
|
||||||
# Ted: contact me when you make any changes
|
# Ted: contact me when you make any changes
|
||||||
|
|
||||||
PREFIX="${PREFIX:-$(pwd)/build}"
|
PREFIX="${PREFIX:-$(pwd)/build}"
|
||||||
|
PLUGIN_PREFIX="$PREFIX/plugins"
|
||||||
|
|
||||||
SRC_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
SRC_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
||||||
source "$SRC_DIR/env.sh"
|
source "$SRC_DIR/env.sh"
|
||||||
|
|
||||||
|
CORETH_PKG=github.com/ava-labs/coreth
|
||||||
|
CORETH_PATH="$GOPATH/src/$CORETH_PKG"
|
||||||
|
if [[ -d "$CORETH_PATH/.git" ]]; then
|
||||||
|
cd "$CORETH_PATH"
|
||||||
|
go get -t -v -d "./..."
|
||||||
|
cd -
|
||||||
|
else
|
||||||
|
go get -t -v -d "$CORETH_PKG/..."
|
||||||
|
fi
|
||||||
|
cd "$CORETH_PATH"
|
||||||
|
git -c advice.detachedHead=false checkout v0.1.0
|
||||||
|
cd -
|
||||||
|
|
||||||
GECKO_PKG=github.com/ava-labs/gecko
|
GECKO_PKG=github.com/ava-labs/gecko
|
||||||
GECKO_PATH="$GOPATH/src/$GECKO_PKG"
|
GECKO_PATH="$GOPATH/src/$GECKO_PKG"
|
||||||
if [[ -d "$GECKO_PATH/.git" ]]; then
|
if [[ -d "$GECKO_PATH/.git" ]]; then
|
||||||
cd "$GECKO_PATH"
|
cd "$GECKO_PATH"
|
||||||
go get -t -v "./..."
|
go get -t -v -d "./..."
|
||||||
cd -
|
cd -
|
||||||
else
|
else
|
||||||
go get -t -v "$GECKO_PKG/..."
|
go get -t -v -d "$GECKO_PKG/..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
go build -o "$PREFIX/ava" "$GECKO_PATH/main/"*.go
|
go build -o "$PREFIX/ava" "$GECKO_PATH/main/"*.go
|
||||||
go build -o "$PREFIX/xputtest" "$GECKO_PATH/xputtest/"*.go
|
go build -o "$PREFIX/xputtest" "$GECKO_PATH/xputtest/"*.go
|
||||||
|
go build -o "$PLUGIN_PREFIX/evm" "$CORETH_PATH/plugin/"*.go
|
||||||
|
if [[ -f "$PREFIX/ava" && -f "$PREFIX/xputtest" && -f "$PLUGIN_PREFIX/evm" ]]; then
|
||||||
|
echo "Build Successful"
|
||||||
|
else
|
||||||
|
echo "Build failure"
|
||||||
|
fi
|
|
@ -17,8 +17,6 @@ fi
|
||||||
if [[ ! -d "$WORKPREFIX" ]]; then
|
if [[ ! -d "$WORKPREFIX" ]]; then
|
||||||
mkdir -p "$WORKPREFIX"
|
mkdir -p "$WORKPREFIX"
|
||||||
git config --global credential.helper cache
|
git config --global credential.helper cache
|
||||||
git clone https://github.com/ava-labs/coreth.git "$WORKPREFIX/coreth"
|
|
||||||
git clone --depth 1 https://github.com/ava-labs/go-ethereum.git "$WORKPREFIX/go-ethereum"
|
|
||||||
git clone https://github.com/ava-labs/gecko.git "$WORKPREFIX/gecko"
|
git clone https://github.com/ava-labs/gecko.git "$WORKPREFIX/gecko"
|
||||||
fi
|
fi
|
||||||
GECKO_COMMIT="$(git --git-dir="$WORKPREFIX/gecko/.git" rev-parse --short HEAD)"
|
GECKO_COMMIT="$(git --git-dir="$WORKPREFIX/gecko/.git" rev-parse --short HEAD)"
|
||||||
|
|
|
@ -9,5 +9,5 @@ SALTICIDAE_GO_HOME="$GOPATH/src/github.com/ava-labs/salticidae-go/"
|
||||||
if [[ -f "$SALTICIDAE_GO_HOME/salticidae/libsalticidae.a" ]]; then
|
if [[ -f "$SALTICIDAE_GO_HOME/salticidae/libsalticidae.a" ]]; then
|
||||||
source "$SALTICIDAE_GO_HOME/scripts/env.sh"
|
source "$SALTICIDAE_GO_HOME/scripts/env.sh"
|
||||||
else
|
else
|
||||||
source /dev/stdin <<<"$(curl -sS https://raw.githubusercontent.com/ava-labs/salticidae-go/master/setup.sh)"
|
source /dev/stdin <<<"$(curl -sS https://raw.githubusercontent.com/ava-labs/salticidae-go/v0.1.0/setup.sh)"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -10,20 +10,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errMsg = "__________ .___\n" +
|
errMsg = "" +
|
||||||
"\\______ \\____________ __| _/__.__.\n" +
|
`__________ .___` + "\n" +
|
||||||
" | | _/\\_ __ \\__ \\ / __ < | |\n" +
|
`\______ \____________ __| _/__.__.` + "\n" +
|
||||||
" | | \\ | | \\// __ \\_/ /_/ |\\___ |\n" +
|
` | | _/\_ __ \__ \ / __ < | |` + "\n" +
|
||||||
" |______ / |__| (____ /\\____ |/ ____|\n" +
|
` | | \ | | \// __ \_/ /_/ |\___ |` + "\n" +
|
||||||
" \\/ \\/ \\/\\/\n" +
|
` |______ / |__| (____ /\____ |/ ____|` + "\n" +
|
||||||
|
` \/ \/ \/\/` + "\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"🏆 🏆 🏆 🏆 🏆 🏆\n" +
|
`🏆 🏆 🏆 🏆 🏆 🏆` + "\n" +
|
||||||
" ________ ________ ________________\n" +
|
` ________ ________ ________________` + "\n" +
|
||||||
" / _____/ \\_____ \\ / _ \\__ ___/\n" +
|
` / _____/ \_____ \ / _ \__ ___/` + "\n" +
|
||||||
"/ \\ ___ / | \\ / /_\\ \\| |\n" +
|
`/ \ ___ / | \ / /_\ \| |` + "\n" +
|
||||||
"\\ \\_\\ \\/ | \\/ | \\ |\n" +
|
`\ \_\ \/ | \/ | \ |` + "\n" +
|
||||||
" \\______ /\\_______ /\\____|__ /____|\n" +
|
` \______ /\_______ /\____|__ /____|` + "\n" +
|
||||||
" \\/ \\/ \\/\n"
|
` \/ \/ \/` + "\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parameters required for snowball consensus
|
// Parameters required for snowball consensus
|
||||||
|
|
|
@ -177,9 +177,7 @@ func (b *bootstrapper) storeVertex(vtx avalanche.Vertex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, parent := range vtx.Parents() {
|
vts = append(vts, vtx.Parents()...)
|
||||||
vts = append(vts, parent)
|
|
||||||
}
|
|
||||||
case choices.Accepted:
|
case choices.Accepted:
|
||||||
b.BootstrapConfig.Context.Log.Verbo("Bootstrapping confirmed %s", vtxID)
|
b.BootstrapConfig.Context.Log.Verbo("Bootstrapping confirmed %s", vtxID)
|
||||||
case choices.Rejected:
|
case choices.Rejected:
|
||||||
|
@ -207,6 +205,7 @@ func (b *bootstrapper) finish() {
|
||||||
func (b *bootstrapper) executeAll(jobs *queue.Jobs, numBlocked prometheus.Gauge) {
|
func (b *bootstrapper) executeAll(jobs *queue.Jobs, numBlocked prometheus.Gauge) {
|
||||||
for job, err := jobs.Pop(); err == nil; job, err = jobs.Pop() {
|
for job, err := jobs.Pop(); err == nil; job, err = jobs.Pop() {
|
||||||
numBlocked.Dec()
|
numBlocked.Dec()
|
||||||
|
b.BootstrapConfig.Context.Log.Debug("Executing: %s", job.ID())
|
||||||
if err := jobs.Execute(job); err != nil {
|
if err := jobs.Execute(job); err != nil {
|
||||||
b.BootstrapConfig.Context.Log.Warn("Error executing: %s", err)
|
b.BootstrapConfig.Context.Log.Warn("Error executing: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,10 @@ func (s *Serializer) ParseVertex(b []byte) (avacon.Vertex, error) {
|
||||||
|
|
||||||
// BuildVertex implements the avalanche.State interface
|
// BuildVertex implements the avalanche.State interface
|
||||||
func (s *Serializer) BuildVertex(parentSet ids.Set, txs []snowstorm.Tx) (avacon.Vertex, error) {
|
func (s *Serializer) BuildVertex(parentSet ids.Set, txs []snowstorm.Tx) (avacon.Vertex, error) {
|
||||||
|
if len(txs) == 0 {
|
||||||
|
return nil, errNoTxs
|
||||||
|
}
|
||||||
|
|
||||||
parentIDs := parentSet.List()
|
parentIDs := parentSet.List()
|
||||||
ids.SortIDs(parentIDs)
|
ids.SortIDs(parentIDs)
|
||||||
sortTxs(txs)
|
sortTxs(txs)
|
||||||
|
|
|
@ -24,6 +24,7 @@ var (
|
||||||
errExtraSpace = errors.New("trailing buffer space")
|
errExtraSpace = errors.New("trailing buffer space")
|
||||||
errInvalidParents = errors.New("vertex contains non-sorted or duplicated parentIDs")
|
errInvalidParents = errors.New("vertex contains non-sorted or duplicated parentIDs")
|
||||||
errInvalidTxs = errors.New("vertex contains non-sorted or duplicated transactions")
|
errInvalidTxs = errors.New("vertex contains non-sorted or duplicated transactions")
|
||||||
|
errNoTxs = errors.New("vertex contains no transactions")
|
||||||
)
|
)
|
||||||
|
|
||||||
type vertex struct {
|
type vertex struct {
|
||||||
|
@ -45,6 +46,8 @@ func (vtx *vertex) Verify() error {
|
||||||
switch {
|
switch {
|
||||||
case !ids.IsSortedAndUniqueIDs(vtx.parentIDs):
|
case !ids.IsSortedAndUniqueIDs(vtx.parentIDs):
|
||||||
return errInvalidParents
|
return errInvalidParents
|
||||||
|
case len(vtx.txs) == 0:
|
||||||
|
return errNoTxs
|
||||||
case !isSortedAndUniqueTxs(vtx.txs):
|
case !isSortedAndUniqueTxs(vtx.txs):
|
||||||
return errInvalidTxs
|
return errInvalidTxs
|
||||||
default:
|
default:
|
||||||
|
@ -55,7 +58,7 @@ func (vtx *vertex) Verify() error {
|
||||||
/*
|
/*
|
||||||
* Vertex:
|
* Vertex:
|
||||||
* Codec | 04 Bytes
|
* Codec | 04 Bytes
|
||||||
* Chain | 32 Bytes
|
* Chain | 32 Bytes
|
||||||
* Height | 08 Bytes
|
* Height | 08 Bytes
|
||||||
* NumParents | 04 Bytes
|
* NumParents | 04 Bytes
|
||||||
* Repeated (NumParents):
|
* Repeated (NumParents):
|
||||||
|
|
|
@ -316,8 +316,37 @@ func (t *Transitive) batch(txs []snowstorm.Tx, force, empty bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(batch) > 0 || (empty && !issued) {
|
if len(batch) > 0 {
|
||||||
t.issueBatch(batch)
|
t.issueBatch(batch)
|
||||||
|
} else if empty && !issued {
|
||||||
|
t.issueRepoll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transitive) issueRepoll() {
|
||||||
|
preferredIDs := t.Consensus.Preferences().List()
|
||||||
|
numPreferredIDs := len(preferredIDs)
|
||||||
|
if numPreferredIDs == 0 {
|
||||||
|
t.Config.Context.Log.Error("Re-query attempt was dropped due to no pending vertices")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sampler := random.Uniform{N: len(preferredIDs)}
|
||||||
|
vtxID := preferredIDs[sampler.Sample()]
|
||||||
|
|
||||||
|
p := t.Consensus.Parameters()
|
||||||
|
vdrs := t.Config.Validators.Sample(p.K) // Validators to sample
|
||||||
|
|
||||||
|
vdrSet := ids.ShortSet{} // Validators to sample repr. as a set
|
||||||
|
for _, vdr := range vdrs {
|
||||||
|
vdrSet.Add(vdr.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
t.RequestID++
|
||||||
|
if numVdrs := len(vdrs); numVdrs == p.K && t.polls.Add(t.RequestID, vdrSet.Len()) {
|
||||||
|
t.Config.Sender.PullQuery(vdrSet, t.RequestID, vtxID)
|
||||||
|
} else if numVdrs < p.K {
|
||||||
|
t.Config.Context.Log.Error("Re-query for %s was dropped due to an insufficient number of validators", vtxID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -698,23 +698,12 @@ func TestEngineScheduleRepoll(t *testing.T) {
|
||||||
|
|
||||||
sender.PushQueryF = nil
|
sender.PushQueryF = nil
|
||||||
|
|
||||||
st.buildVertex = func(_ ids.Set, txs []snowstorm.Tx) (avalanche.Vertex, error) {
|
|
||||||
consumers := []snowstorm.Tx{}
|
|
||||||
for _, tx := range txs {
|
|
||||||
consumers = append(consumers, tx)
|
|
||||||
}
|
|
||||||
return &Vtx{
|
|
||||||
parents: []avalanche.Vertex{gVtx, mVtx},
|
|
||||||
id: GenerateID(),
|
|
||||||
txs: consumers,
|
|
||||||
status: choices.Processing,
|
|
||||||
bytes: []byte{1},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
repolled := new(bool)
|
repolled := new(bool)
|
||||||
sender.PushQueryF = func(_ ids.ShortSet, _ uint32, _ ids.ID, _ []byte) {
|
sender.PullQueryF = func(_ ids.ShortSet, _ uint32, vtxID ids.ID) {
|
||||||
*repolled = true
|
*repolled = true
|
||||||
|
if !vtxID.Equals(vtx.ID()) {
|
||||||
|
t.Fatalf("Wrong vertex queried")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
te.QueryFailed(vdr.ID(), *requestID)
|
te.QueryFailed(vdr.ID(), *requestID)
|
||||||
|
@ -979,31 +968,14 @@ func TestEngineIssueRepoll(t *testing.T) {
|
||||||
te.Initialize(config)
|
te.Initialize(config)
|
||||||
te.finishBootstrapping()
|
te.finishBootstrapping()
|
||||||
|
|
||||||
newVtxID := new(ids.ID)
|
sender.PullQueryF = func(vdrs ids.ShortSet, _ uint32, vtxID ids.ID) {
|
||||||
|
|
||||||
st.buildVertex = func(s ids.Set, txs []snowstorm.Tx) (avalanche.Vertex, error) {
|
|
||||||
if len(txs) != 0 {
|
|
||||||
t.Fatalf("Wrong vertex issued")
|
|
||||||
}
|
|
||||||
if s.Len() != 2 || !s.Contains(gVtx.ID()) || !s.Contains(mVtx.ID()) {
|
|
||||||
t.Fatalf("Wrong vertex issued")
|
|
||||||
}
|
|
||||||
|
|
||||||
vtx := &Vtx{
|
|
||||||
parents: []avalanche.Vertex{gVtx, mVtx},
|
|
||||||
id: GenerateID(),
|
|
||||||
status: choices.Processing,
|
|
||||||
bytes: []byte{1},
|
|
||||||
}
|
|
||||||
*newVtxID = vtx.ID()
|
|
||||||
return vtx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.PushQueryF = func(vdrs ids.ShortSet, _ uint32, vtxID ids.ID, vtx []byte) {
|
|
||||||
vdrSet := ids.ShortSet{}
|
vdrSet := ids.ShortSet{}
|
||||||
vdrSet.Add(vdr.ID())
|
vdrSet.Add(vdr.ID())
|
||||||
if !vdrs.Equals(vdrSet) || !vtxID.Equals(*newVtxID) {
|
if !vdrs.Equals(vdrSet) {
|
||||||
t.Fatalf("Wrong query message")
|
t.Fatalf("Wrong query recipients")
|
||||||
|
}
|
||||||
|
if !vtxID.Equals(gVtx.ID()) && !vtxID.Equals(mVtx.ID()) {
|
||||||
|
t.Fatalf("Unknown re-query")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,12 +79,23 @@ func (v *voter) bubbleVotes(votes ids.UniqueBag) ids.UniqueBag {
|
||||||
vtx := vts[0]
|
vtx := vts[0]
|
||||||
vts = vts[1:]
|
vts = vts[1:]
|
||||||
|
|
||||||
if status := vtx.Status(); status.Fetched() && !v.t.Consensus.VertexIssued(vtx) {
|
status := vtx.Status()
|
||||||
vts = append(vts, vtx.Parents()...)
|
if !status.Fetched() {
|
||||||
} else if !status.Decided() && v.t.Consensus.VertexIssued(vtx) {
|
v.t.Config.Context.Log.Verbo("Dropping %d vote(s) for %s because the vertex is unknown", set.Len(), vtx.ID())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.Decided() {
|
||||||
|
v.t.Config.Context.Log.Verbo("Dropping %d vote(s) for %s because the vertex is decided", set.Len(), vtx.ID())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.t.Consensus.VertexIssued(vtx) {
|
||||||
|
v.t.Config.Context.Log.Verbo("Applying %d vote(s) for %s", set.Len(), vtx.ID())
|
||||||
bubbledVotes.UnionSet(vtx.ID(), set)
|
bubbledVotes.UnionSet(vtx.ID(), set)
|
||||||
} else {
|
} else {
|
||||||
v.t.Config.Context.Log.Debug("Dropping %d vote(s) for %s because the vertex is invalid", set.Len(), vtx.ID())
|
v.t.Config.Context.Log.Verbo("Bubbling %d vote(s) for %s because the vertex isn't issued", set.Len(), vtx.ID())
|
||||||
|
vts = append(vts, vtx.Parents()...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// LockOption allows the vm to specify their lock option based on their endpoint
|
// LockOption allows the vm to specify their lock option based on their endpoint
|
||||||
type LockOption int
|
type LockOption uint32
|
||||||
|
|
||||||
// List of all allowed options
|
// List of all allowed options
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
// TODO: Consider renaming Message to, say, VMMessage
|
// TODO: Consider renaming Message to, say, VMMessage
|
||||||
|
|
||||||
// Message is an enum of the message types that vms can send to consensus
|
// Message is an enum of the message types that vms can send to consensus
|
||||||
type Message int
|
type Message uint32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// PendingTxs notifies a consensus engine that
|
// PendingTxs notifies a consensus engine that
|
||||||
|
|
|
@ -45,10 +45,24 @@ func (t *Transitive) Initialize(config Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transitive) finishBootstrapping() {
|
func (t *Transitive) finishBootstrapping() {
|
||||||
tail := t.Config.VM.LastAccepted()
|
|
||||||
t.Config.VM.SetPreference(tail)
|
|
||||||
t.Consensus.Initialize(t.Config.Context, t.Params, tail)
|
|
||||||
t.bootstrapped = true
|
t.bootstrapped = true
|
||||||
|
tailID := t.Config.VM.LastAccepted()
|
||||||
|
t.Consensus.Initialize(t.Config.Context, t.Params, tailID)
|
||||||
|
|
||||||
|
tail, err := t.Config.VM.GetBlock(tailID)
|
||||||
|
if err != nil {
|
||||||
|
t.Config.Context.Log.Error("Failed to get last accepted block due to: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch blk := tail.(type) {
|
||||||
|
case OracleBlock:
|
||||||
|
for _, blk := range blk.Options() {
|
||||||
|
t.deliver(blk)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Config.VM.SetPreference(tailID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown implements the Engine interface
|
// Shutdown implements the Engine interface
|
||||||
|
|
|
@ -56,8 +56,17 @@ func setup(t *testing.T) (validators.Validator, validators.Set, *common.SenderTe
|
||||||
te := &Transitive{}
|
te := &Transitive{}
|
||||||
|
|
||||||
te.Initialize(config)
|
te.Initialize(config)
|
||||||
|
|
||||||
|
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
||||||
|
if !blkID.Equals(gBlk.ID()) {
|
||||||
|
t.Fatalf("Wrong block requested")
|
||||||
|
}
|
||||||
|
return gBlk, nil
|
||||||
|
}
|
||||||
|
|
||||||
te.finishBootstrapping()
|
te.finishBootstrapping()
|
||||||
|
|
||||||
|
vm.GetBlockF = nil
|
||||||
vm.LastAcceptedF = nil
|
vm.LastAcceptedF = nil
|
||||||
sender.CantGetAcceptedFrontier = true
|
sender.CantGetAcceptedFrontier = true
|
||||||
|
|
||||||
|
@ -369,8 +378,17 @@ func TestEngineMultipleQuery(t *testing.T) {
|
||||||
|
|
||||||
te := &Transitive{}
|
te := &Transitive{}
|
||||||
te.Initialize(config)
|
te.Initialize(config)
|
||||||
|
|
||||||
|
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
||||||
|
if !blkID.Equals(gBlk.ID()) {
|
||||||
|
t.Fatalf("Wrong block requested")
|
||||||
|
}
|
||||||
|
return gBlk, nil
|
||||||
|
}
|
||||||
|
|
||||||
te.finishBootstrapping()
|
te.finishBootstrapping()
|
||||||
|
|
||||||
|
vm.GetBlockF = nil
|
||||||
vm.LastAcceptedF = nil
|
vm.LastAcceptedF = nil
|
||||||
sender.CantGetAcceptedFrontier = true
|
sender.CantGetAcceptedFrontier = true
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ func init() {
|
||||||
RSA: &FactoryRSA{},
|
RSA: &FactoryRSA{},
|
||||||
RSAPSS: &FactoryRSAPSS{},
|
RSAPSS: &FactoryRSAPSS{},
|
||||||
ED25519: &FactoryED25519{},
|
ED25519: &FactoryED25519{},
|
||||||
SECP256K1: &FactorySECP256K1{},
|
SECP256K1: &FactorySECP256K1R{},
|
||||||
}
|
}
|
||||||
for _, f := range factories {
|
for _, f := range factories {
|
||||||
fKeys := []PublicKey{}
|
fKeys := []PublicKey{}
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rand"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/crypto"
|
|
||||||
"github.com/ava-labs/go-ethereum/crypto/secp256k1"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/ava-labs/gecko/utils/hashing"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SECP256K1SigLen is the number of bytes in a secp2561k signature
|
|
||||||
SECP256K1SigLen = 64
|
|
||||||
|
|
||||||
// SECP256K1SKLen is the number of bytes in a secp2561k private key
|
|
||||||
SECP256K1SKLen = 32
|
|
||||||
|
|
||||||
// SECP256K1PKLen is the number of bytes in a secp2561k public key
|
|
||||||
SECP256K1PKLen = 33
|
|
||||||
)
|
|
||||||
|
|
||||||
// FactorySECP256K1 ...
|
|
||||||
type FactorySECP256K1 struct{}
|
|
||||||
|
|
||||||
// NewPrivateKey implements the Factory interface
|
|
||||||
func (*FactorySECP256K1) NewPrivateKey() (PrivateKey, error) {
|
|
||||||
k, err := ecdsa.GenerateKey(secp256k1.S256(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &PrivateKeySECP256K1{sk: k}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToPublicKey implements the Factory interface
|
|
||||||
func (*FactorySECP256K1) ToPublicKey(b []byte) (PublicKey, error) {
|
|
||||||
key, err := crypto.DecompressPubkey(b)
|
|
||||||
return &PublicKeySECP256K1{
|
|
||||||
pk: key,
|
|
||||||
bytes: b,
|
|
||||||
}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToPrivateKey implements the Factory interface
|
|
||||||
func (*FactorySECP256K1) ToPrivateKey(b []byte) (PrivateKey, error) {
|
|
||||||
key, err := crypto.ToECDSA(b)
|
|
||||||
return &PrivateKeySECP256K1{
|
|
||||||
sk: key,
|
|
||||||
bytes: b,
|
|
||||||
}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKeySECP256K1 ...
|
|
||||||
type PublicKeySECP256K1 struct {
|
|
||||||
pk *ecdsa.PublicKey
|
|
||||||
addr ids.ShortID
|
|
||||||
bytes []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify implements the PublicKey interface
|
|
||||||
func (k *PublicKeySECP256K1) Verify(msg, sig []byte) bool {
|
|
||||||
return k.VerifyHash(hashing.ComputeHash256(msg), sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyHash implements the PublicKey interface
|
|
||||||
func (k *PublicKeySECP256K1) VerifyHash(hash, sig []byte) bool {
|
|
||||||
if verifySECP256K1SignatureFormat(sig) != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return crypto.VerifySignature(k.Bytes(), hash, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address implements the PublicKey interface
|
|
||||||
func (k *PublicKeySECP256K1) Address() ids.ShortID {
|
|
||||||
if k.addr.IsZero() {
|
|
||||||
addr, err := ids.ToShortID(hashing.PubkeyBytesToAddress(k.Bytes()))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
k.addr = addr
|
|
||||||
}
|
|
||||||
return k.addr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes implements the PublicKey interface
|
|
||||||
func (k *PublicKeySECP256K1) Bytes() []byte {
|
|
||||||
if k.bytes == nil {
|
|
||||||
k.bytes = crypto.CompressPubkey(k.pk)
|
|
||||||
}
|
|
||||||
return k.bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrivateKeySECP256K1 ...
|
|
||||||
type PrivateKeySECP256K1 struct {
|
|
||||||
sk *ecdsa.PrivateKey
|
|
||||||
pk *PublicKeySECP256K1
|
|
||||||
bytes []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKey implements the PrivateKey interface
|
|
||||||
func (k *PrivateKeySECP256K1) PublicKey() PublicKey {
|
|
||||||
if k.pk == nil {
|
|
||||||
k.pk = &PublicKeySECP256K1{pk: (*ecdsa.PublicKey)(&k.sk.PublicKey)}
|
|
||||||
}
|
|
||||||
return k.pk
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign implements the PrivateKey interface
|
|
||||||
func (k *PrivateKeySECP256K1) Sign(msg []byte) ([]byte, error) {
|
|
||||||
return k.SignHash(hashing.ComputeHash256(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignHash implements the PrivateKey interface
|
|
||||||
func (k *PrivateKeySECP256K1) SignHash(hash []byte) ([]byte, error) {
|
|
||||||
sig, err := crypto.Sign(hash, k.sk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return sig[:len(sig)-1], err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes implements the PrivateKey interface
|
|
||||||
func (k *PrivateKeySECP256K1) Bytes() []byte {
|
|
||||||
if k.bytes == nil {
|
|
||||||
k.bytes = make([]byte, SECP256K1SKLen)
|
|
||||||
bytes := k.sk.D.Bytes()
|
|
||||||
copy(k.bytes[SECP256K1SKLen-len(bytes):], bytes)
|
|
||||||
}
|
|
||||||
return k.bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifySECP256K1SignatureFormat(sig []byte) error {
|
|
||||||
if len(sig) != SECP256K1SigLen {
|
|
||||||
return errInvalidSigLen
|
|
||||||
}
|
|
||||||
var r, s big.Int
|
|
||||||
r.SetBytes(sig[:32])
|
|
||||||
s.SetBytes(sig[32:])
|
|
||||||
if !crypto.ValidateSignatureValues(0, &r, &s, true) {
|
|
||||||
return errMutatedSig
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/utils/hashing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NumRecoveries is the number of recoveries to run per operation
|
|
||||||
const NumRecoveries = 1
|
|
||||||
|
|
||||||
var (
|
|
||||||
secpSigs [][]byte
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
factory := FactorySECP256K1R{}
|
|
||||||
|
|
||||||
hash := hashing.ComputeHash256(nil)
|
|
||||||
for i := byte(0); i < NumRecoveries; i++ {
|
|
||||||
key, err := factory.NewPrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
sig, err := key.SignHash(hash)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
secpSigs = append(secpSigs, sig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func recover() {
|
|
||||||
factory := FactorySECP256K1R{}
|
|
||||||
hash := hashing.ComputeHash256(nil)
|
|
||||||
for _, sig := range secpSigs {
|
|
||||||
if _, err := factory.RecoverHashPublicKey(hash, sig); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkSecp256k1RecoverVerify runs the benchmark with secp sig
|
|
||||||
func BenchmarkSecp256k1RecoverVerify(b *testing.B) {
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
recover()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,13 +5,11 @@ package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"errors"
|
||||||
"crypto/rand"
|
|
||||||
"math/big"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/crypto"
|
"github.com/decred/dcrd/dcrec/secp256k1"
|
||||||
"github.com/ava-labs/go-ethereum/crypto/secp256k1"
|
"github.com/decred/dcrd/dcrec/secp256k1/ecdsa"
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/cache"
|
"github.com/ava-labs/gecko/cache"
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
|
@ -31,6 +29,14 @@ const (
|
||||||
// SECP256K1RPKLen is the number of bytes in a secp2561k recoverable public
|
// SECP256K1RPKLen is the number of bytes in a secp2561k recoverable public
|
||||||
// key
|
// key
|
||||||
SECP256K1RPKLen = 33
|
SECP256K1RPKLen = 33
|
||||||
|
|
||||||
|
// from the decred library:
|
||||||
|
// compactSigMagicOffset is a value used when creating the compact signature
|
||||||
|
// recovery code inherited from Bitcoin and has no meaning, but has been
|
||||||
|
// retained for compatibility. For historical purposes, it was originally
|
||||||
|
// picked to avoid a binary representation that would allow compact
|
||||||
|
// signatures to be mistaken for other components.
|
||||||
|
compactSigMagicOffset = 27
|
||||||
)
|
)
|
||||||
|
|
||||||
// FactorySECP256K1R ...
|
// FactorySECP256K1R ...
|
||||||
|
@ -38,16 +44,13 @@ type FactorySECP256K1R struct{ Cache cache.LRU }
|
||||||
|
|
||||||
// NewPrivateKey implements the Factory interface
|
// NewPrivateKey implements the Factory interface
|
||||||
func (*FactorySECP256K1R) NewPrivateKey() (PrivateKey, error) {
|
func (*FactorySECP256K1R) NewPrivateKey() (PrivateKey, error) {
|
||||||
k, err := ecdsa.GenerateKey(secp256k1.S256(), rand.Reader)
|
k, err := secp256k1.GeneratePrivateKey()
|
||||||
if err != nil {
|
return &PrivateKeySECP256K1R{sk: k}, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &PrivateKeySECP256K1R{sk: k}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToPublicKey implements the Factory interface
|
// ToPublicKey implements the Factory interface
|
||||||
func (*FactorySECP256K1R) ToPublicKey(b []byte) (PublicKey, error) {
|
func (*FactorySECP256K1R) ToPublicKey(b []byte) (PublicKey, error) {
|
||||||
key, err := crypto.DecompressPubkey(b)
|
key, err := secp256k1.ParsePubKey(b)
|
||||||
return &PublicKeySECP256K1R{
|
return &PublicKeySECP256K1R{
|
||||||
pk: key,
|
pk: key,
|
||||||
bytes: b,
|
bytes: b,
|
||||||
|
@ -56,11 +59,10 @@ func (*FactorySECP256K1R) ToPublicKey(b []byte) (PublicKey, error) {
|
||||||
|
|
||||||
// ToPrivateKey implements the Factory interface
|
// ToPrivateKey implements the Factory interface
|
||||||
func (*FactorySECP256K1R) ToPrivateKey(b []byte) (PrivateKey, error) {
|
func (*FactorySECP256K1R) ToPrivateKey(b []byte) (PrivateKey, error) {
|
||||||
key, err := crypto.ToECDSA(b)
|
|
||||||
return &PrivateKeySECP256K1R{
|
return &PrivateKeySECP256K1R{
|
||||||
sk: key,
|
sk: secp256k1.PrivKeyFromBytes(b),
|
||||||
bytes: b,
|
bytes: b,
|
||||||
}, err
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecoverPublicKey returns the public key from a 65 byte signature
|
// RecoverPublicKey returns the public key from a 65 byte signature
|
||||||
|
@ -75,25 +77,35 @@ func (f *FactorySECP256K1R) RecoverHashPublicKey(hash, sig []byte) (PublicKey, e
|
||||||
copy(cacheBytes[len(hash):], sig)
|
copy(cacheBytes[len(hash):], sig)
|
||||||
id := ids.NewID(hashing.ComputeHash256Array(cacheBytes))
|
id := ids.NewID(hashing.ComputeHash256Array(cacheBytes))
|
||||||
if cachedPublicKey, ok := f.Cache.Get(id); ok {
|
if cachedPublicKey, ok := f.Cache.Get(id); ok {
|
||||||
return cachedPublicKey.(*PublicKeySECP256K1), nil
|
return cachedPublicKey.(*PublicKeySECP256K1R), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := verifySECP256K1RSignatureFormat(sig); err != nil {
|
if err := verifySECP256K1RSignatureFormat(sig); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rawPubkey, err := crypto.SigToPub(hash, sig)
|
sig, err := sigToRawSig(sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pubkey := &PublicKeySECP256K1{pk: rawPubkey}
|
|
||||||
|
rawPubkey, compressed, err := ecdsa.RecoverCompact(sig, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if compressed {
|
||||||
|
return nil, errors.New("wasn't expecting a compresses key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkey := &PublicKeySECP256K1R{pk: rawPubkey}
|
||||||
f.Cache.Put(id, pubkey)
|
f.Cache.Put(id, pubkey)
|
||||||
return pubkey, nil
|
return pubkey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeySECP256K1R ...
|
// PublicKeySECP256K1R ...
|
||||||
type PublicKeySECP256K1R struct {
|
type PublicKeySECP256K1R struct {
|
||||||
pk *ecdsa.PublicKey
|
pk *secp256k1.PublicKey
|
||||||
addr ids.ShortID
|
addr ids.ShortID
|
||||||
bytes []byte
|
bytes []byte
|
||||||
}
|
}
|
||||||
|
@ -105,10 +117,12 @@ func (k *PublicKeySECP256K1R) Verify(msg, sig []byte) bool {
|
||||||
|
|
||||||
// VerifyHash implements the PublicKey interface
|
// VerifyHash implements the PublicKey interface
|
||||||
func (k *PublicKeySECP256K1R) VerifyHash(hash, sig []byte) bool {
|
func (k *PublicKeySECP256K1R) VerifyHash(hash, sig []byte) bool {
|
||||||
if verifySECP256K1RSignatureFormat(sig) != nil {
|
factory := FactorySECP256K1R{}
|
||||||
|
pk, err := factory.RecoverHashPublicKey(hash, sig)
|
||||||
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return crypto.VerifySignature(k.Bytes(), hash, sig[:SECP256K1RSigLen-1])
|
return k.Address().Equals(pk.Address())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address implements the PublicKey interface
|
// Address implements the PublicKey interface
|
||||||
|
@ -126,14 +140,14 @@ func (k *PublicKeySECP256K1R) Address() ids.ShortID {
|
||||||
// Bytes implements the PublicKey interface
|
// Bytes implements the PublicKey interface
|
||||||
func (k *PublicKeySECP256K1R) Bytes() []byte {
|
func (k *PublicKeySECP256K1R) Bytes() []byte {
|
||||||
if k.bytes == nil {
|
if k.bytes == nil {
|
||||||
k.bytes = crypto.CompressPubkey(k.pk)
|
k.bytes = k.pk.SerializeCompressed()
|
||||||
}
|
}
|
||||||
return k.bytes
|
return k.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivateKeySECP256K1R ...
|
// PrivateKeySECP256K1R ...
|
||||||
type PrivateKeySECP256K1R struct {
|
type PrivateKeySECP256K1R struct {
|
||||||
sk *ecdsa.PrivateKey
|
sk *secp256k1.PrivateKey
|
||||||
pk *PublicKeySECP256K1R
|
pk *PublicKeySECP256K1R
|
||||||
bytes []byte
|
bytes []byte
|
||||||
}
|
}
|
||||||
|
@ -141,7 +155,7 @@ type PrivateKeySECP256K1R struct {
|
||||||
// PublicKey implements the PrivateKey interface
|
// PublicKey implements the PrivateKey interface
|
||||||
func (k *PrivateKeySECP256K1R) PublicKey() PublicKey {
|
func (k *PrivateKeySECP256K1R) PublicKey() PublicKey {
|
||||||
if k.pk == nil {
|
if k.pk == nil {
|
||||||
k.pk = &PublicKeySECP256K1R{pk: (*ecdsa.PublicKey)(&k.sk.PublicKey)}
|
k.pk = &PublicKeySECP256K1R{pk: k.sk.PubKey()}
|
||||||
}
|
}
|
||||||
return k.pk
|
return k.pk
|
||||||
}
|
}
|
||||||
|
@ -153,27 +167,49 @@ func (k *PrivateKeySECP256K1R) Sign(msg []byte) ([]byte, error) {
|
||||||
|
|
||||||
// SignHash implements the PrivateKey interface
|
// SignHash implements the PrivateKey interface
|
||||||
func (k *PrivateKeySECP256K1R) SignHash(hash []byte) ([]byte, error) {
|
func (k *PrivateKeySECP256K1R) SignHash(hash []byte) ([]byte, error) {
|
||||||
return crypto.Sign(hash, k.sk)
|
sig := ecdsa.SignCompact(k.sk, hash, false) // returns [v || r || s]
|
||||||
|
return rawSigToSig(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes implements the PrivateKey interface
|
// Bytes implements the PrivateKey interface
|
||||||
func (k *PrivateKeySECP256K1R) Bytes() []byte {
|
func (k *PrivateKeySECP256K1R) Bytes() []byte {
|
||||||
if k.bytes == nil {
|
if k.bytes == nil {
|
||||||
k.bytes = make([]byte, SECP256K1RSKLen)
|
k.bytes = k.sk.Serialize()
|
||||||
bytes := k.sk.D.Bytes()
|
|
||||||
copy(k.bytes[SECP256K1RSKLen-len(bytes):], bytes)
|
|
||||||
}
|
}
|
||||||
return k.bytes
|
return k.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// raw sig has format [v || r || s] whereas the sig has format [r || s || v]
|
||||||
|
func rawSigToSig(sig []byte) ([]byte, error) {
|
||||||
|
if len(sig) != SECP256K1RSigLen {
|
||||||
|
return nil, errInvalidSigLen
|
||||||
|
}
|
||||||
|
recCode := sig[0]
|
||||||
|
copy(sig, sig[1:])
|
||||||
|
sig[SECP256K1RSigLen-1] = recCode - compactSigMagicOffset
|
||||||
|
return sig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sig has format [r || s || v] whereas the raw sig has format [v || r || s]
|
||||||
|
func sigToRawSig(sig []byte) ([]byte, error) {
|
||||||
|
if len(sig) != SECP256K1RSigLen {
|
||||||
|
return nil, errInvalidSigLen
|
||||||
|
}
|
||||||
|
newSig := make([]byte, SECP256K1RSigLen)
|
||||||
|
newSig[0] = sig[SECP256K1RSigLen-1] + compactSigMagicOffset
|
||||||
|
copy(newSig[1:], sig)
|
||||||
|
return newSig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifies the signature format in format [r || s || v]
|
||||||
func verifySECP256K1RSignatureFormat(sig []byte) error {
|
func verifySECP256K1RSignatureFormat(sig []byte) error {
|
||||||
if len(sig) != SECP256K1RSigLen {
|
if len(sig) != SECP256K1RSigLen {
|
||||||
return errInvalidSigLen
|
return errInvalidSigLen
|
||||||
}
|
}
|
||||||
var r, s big.Int
|
|
||||||
r.SetBytes(sig[:32])
|
var s secp256k1.ModNScalar
|
||||||
s.SetBytes(sig[32:64])
|
s.SetByteSlice(sig[32:64])
|
||||||
if !crypto.ValidateSignatureValues(sig[64], &r, &s, true) {
|
if s.IsOverHalfOrder() {
|
||||||
return errMutatedSig
|
return errMutatedSig
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"github.com/ava-labs/gecko/cache"
|
"github.com/ava-labs/gecko/cache"
|
||||||
"github.com/ava-labs/gecko/utils/formatting"
|
"github.com/ava-labs/gecko/utils/formatting"
|
||||||
"github.com/ava-labs/gecko/utils/hashing"
|
"github.com/ava-labs/gecko/utils/hashing"
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRecover(t *testing.T) {
|
func TestRecover(t *testing.T) {
|
||||||
|
@ -73,3 +75,24 @@ func TestGenRecreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVerifyMutatedSignature(t *testing.T) {
|
||||||
|
factory := FactorySECP256K1R{}
|
||||||
|
|
||||||
|
sk, err := factory.NewPrivateKey()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
msg := []byte{'h', 'e', 'l', 'l', 'o'}
|
||||||
|
|
||||||
|
sig, err := sk.Sign(msg)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var s secp256k1.ModNScalar
|
||||||
|
s.SetByteSlice(sig[32:64])
|
||||||
|
s.Negate()
|
||||||
|
newSBytes := s.Bytes()
|
||||||
|
copy(sig[32:], newSBytes[:])
|
||||||
|
|
||||||
|
_, err = factory.RecoverPublicKey(msg, sig)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
48
utils/ip.go
48
utils/ip.go
|
@ -10,6 +10,28 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This was taken from: https://stackoverflow.com/a/50825191/3478466
|
||||||
|
var privateIPBlocks []*net.IPNet
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for _, cidr := range []string{
|
||||||
|
"127.0.0.0/8", // IPv4 loopback
|
||||||
|
"10.0.0.0/8", // RFC1918
|
||||||
|
"172.16.0.0/12", // RFC1918
|
||||||
|
"192.168.0.0/16", // RFC1918
|
||||||
|
"169.254.0.0/16", // RFC3927 link-local
|
||||||
|
"::1/128", // IPv6 loopback
|
||||||
|
"fe80::/10", // IPv6 link-local
|
||||||
|
"fc00::/7", // IPv6 unique local addr
|
||||||
|
} {
|
||||||
|
_, block, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("parse error on %q: %v", cidr, err))
|
||||||
|
}
|
||||||
|
privateIPBlocks = append(privateIPBlocks, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errBadIP = errors.New("bad ip format")
|
errBadIP = errors.New("bad ip format")
|
||||||
)
|
)
|
||||||
|
@ -35,6 +57,32 @@ func (ipDesc IPDesc) String() string {
|
||||||
return net.JoinHostPort(ipDesc.IP.String(), fmt.Sprintf("%d", ipDesc.Port))
|
return net.JoinHostPort(ipDesc.IP.String(), fmt.Sprintf("%d", ipDesc.Port))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPrivate attempts to decide if the ip address in this descriptor is a local
|
||||||
|
// ip address.
|
||||||
|
// This function was taken from: https://stackoverflow.com/a/50825191/3478466
|
||||||
|
func (ipDesc IPDesc) IsPrivate() bool {
|
||||||
|
ip := ipDesc.IP
|
||||||
|
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, block := range privateIPBlocks {
|
||||||
|
if block.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns if the IP or port is zeroed out
|
||||||
|
func (ipDesc IPDesc) IsZero() bool {
|
||||||
|
ip := ipDesc.IP
|
||||||
|
return ipDesc.Port == 0 ||
|
||||||
|
len(ip) == 0 ||
|
||||||
|
ip.Equal(net.IPv4zero) ||
|
||||||
|
ip.Equal(net.IPv6zero)
|
||||||
|
}
|
||||||
|
|
||||||
// ToIPDesc ...
|
// ToIPDesc ...
|
||||||
func ToIPDesc(str string) (IPDesc, error) {
|
func ToIPDesc(str string) (IPDesc, error) {
|
||||||
host, portStr, err := net.SplitHostPort(str)
|
host, portStr, err := net.SplitHostPort(str)
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (f *factory) Make() (Logger, error) {
|
||||||
// MakeChain ...
|
// MakeChain ...
|
||||||
func (f *factory) MakeChain(chainID ids.ID, subdir string) (Logger, error) {
|
func (f *factory) MakeChain(chainID ids.ID, subdir string) (Logger, error) {
|
||||||
config := f.config
|
config := f.config
|
||||||
config.MsgPrefix = "SN " + chainID.String()
|
config.MsgPrefix = "chain " + chainID.String()
|
||||||
config.Directory = path.Join(config.Directory, "chain", chainID.String(), subdir)
|
config.Directory = path.Join(config.Directory, "chain", chainID.String(), subdir)
|
||||||
|
|
||||||
log, err := New(config)
|
log, err := New(config)
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/ava-labs/gecko/snow/engine/common"
|
"github.com/ava-labs/gecko/snow/engine/common"
|
||||||
"github.com/ava-labs/gecko/utils/crypto"
|
"github.com/ava-labs/gecko/utils/crypto"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/codec"
|
|
||||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -108,17 +107,7 @@ func TestBaseTxSerialization(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
b, err := c.Marshal(&tx.UnsignedTx)
|
b, err := c.Marshal(&tx.UnsignedTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -185,17 +174,7 @@ func TestBaseTxGetters(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerify(t *testing.T) {
|
func TestBaseTxSyntacticVerify(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -237,17 +216,7 @@ func TestBaseTxSyntacticVerify(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyNil(t *testing.T) {
|
func TestBaseTxSyntacticVerifyNil(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := (*BaseTx)(nil)
|
tx := (*BaseTx)(nil)
|
||||||
if err := tx.SyntacticVerify(ctx, c, 0); err == nil {
|
if err := tx.SyntacticVerify(ctx, c, 0); err == nil {
|
||||||
|
@ -256,17 +225,7 @@ func TestBaseTxSyntacticVerifyNil(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyWrongNetworkID(t *testing.T) {
|
func TestBaseTxSyntacticVerifyWrongNetworkID(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: 0,
|
NetID: 0,
|
||||||
|
@ -308,17 +267,7 @@ func TestBaseTxSyntacticVerifyWrongNetworkID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyWrongChainID(t *testing.T) {
|
func TestBaseTxSyntacticVerifyWrongChainID(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -360,17 +309,7 @@ func TestBaseTxSyntacticVerifyWrongChainID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyInvalidOutput(t *testing.T) {
|
func TestBaseTxSyntacticVerifyInvalidOutput(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -403,17 +342,7 @@ func TestBaseTxSyntacticVerifyInvalidOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyUnsortedOutputs(t *testing.T) {
|
func TestBaseTxSyntacticVerifyUnsortedOutputs(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -469,17 +398,7 @@ func TestBaseTxSyntacticVerifyUnsortedOutputs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyInvalidInput(t *testing.T) {
|
func TestBaseTxSyntacticVerifyInvalidInput(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -504,17 +423,7 @@ func TestBaseTxSyntacticVerifyInvalidInput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyInputOverflow(t *testing.T) {
|
func TestBaseTxSyntacticVerifyInputOverflow(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -576,17 +485,7 @@ func TestBaseTxSyntacticVerifyInputOverflow(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyOutputOverflow(t *testing.T) {
|
func TestBaseTxSyntacticVerifyOutputOverflow(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -640,17 +539,7 @@ func TestBaseTxSyntacticVerifyOutputOverflow(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyInsufficientFunds(t *testing.T) {
|
func TestBaseTxSyntacticVerifyInsufficientFunds(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -692,17 +581,7 @@ func TestBaseTxSyntacticVerifyInsufficientFunds(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSyntacticVerifyUninitialized(t *testing.T) {
|
func TestBaseTxSyntacticVerifyUninitialized(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
tx := &BaseTx{
|
tx := &BaseTx{
|
||||||
NetID: networkID,
|
NetID: networkID,
|
||||||
|
@ -743,29 +622,9 @@ func TestBaseTxSyntacticVerifyUninitialized(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerify(t *testing.T) {
|
func TestBaseTxSemanticVerify(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, _, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
|
@ -827,29 +686,9 @@ func TestBaseTxSemanticVerify(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
|
func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, _, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
@ -896,29 +735,9 @@ func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerifyWrongAssetID(t *testing.T) {
|
func TestBaseTxSemanticVerifyWrongAssetID(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, _, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
@ -1073,29 +892,9 @@ func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
|
func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, _, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
|
@ -1144,29 +943,9 @@ func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
|
func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, _, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
|
@ -1228,29 +1007,9 @@ func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerifyInvalidUTXO(t *testing.T) {
|
func TestBaseTxSemanticVerifyInvalidUTXO(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, _, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
tx := &Tx{UnsignedTx: &BaseTx{
|
tx := &Tx{UnsignedTx: &BaseTx{
|
||||||
|
@ -1312,27 +1071,7 @@ func TestBaseTxSemanticVerifyInvalidUTXO(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
|
func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, issuer, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
|
@ -1463,27 +1202,7 @@ func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseTxSemanticVerifyPendingWrongAssetID(t *testing.T) {
|
func TestBaseTxSemanticVerifyPendingWrongAssetID(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, issuer, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,9 @@ type Factory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New ...
|
// New ...
|
||||||
func (f *Factory) New() interface{} {
|
func (f *Factory) New() (interface{}, error) {
|
||||||
return &VM{
|
return &VM{
|
||||||
ava: f.AVA,
|
ava: f.AVA,
|
||||||
platform: f.Platform,
|
platform: f.Platform,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"github.com/ava-labs/gecko/utils/crypto"
|
"github.com/ava-labs/gecko/utils/crypto"
|
||||||
"github.com/ava-labs/gecko/utils/logging"
|
"github.com/ava-labs/gecko/utils/logging"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/codec"
|
|
||||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -89,18 +88,7 @@ func TestImportTxSerialization(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
b, err := c.Marshal(&tx.UnsignedTx)
|
b, err := c.Marshal(&tx.UnsignedTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -16,7 +16,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPrefixedSetsAndGets(t *testing.T) {
|
func TestPrefixedSetsAndGets(t *testing.T) {
|
||||||
vm := GenesisVM(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
ctx.Lock.Unlock()
|
||||||
state := vm.state
|
state := vm.state
|
||||||
|
|
||||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||||
|
@ -110,7 +111,8 @@ func TestPrefixedSetsAndGets(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrefixedFundingNoAddresses(t *testing.T) {
|
func TestPrefixedFundingNoAddresses(t *testing.T) {
|
||||||
vm := GenesisVM(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
ctx.Lock.Unlock()
|
||||||
state := vm.state
|
state := vm.state
|
||||||
|
|
||||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||||
|
@ -133,7 +135,8 @@ func TestPrefixedFundingNoAddresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrefixedFundingAddresses(t *testing.T) {
|
func TestPrefixedFundingAddresses(t *testing.T) {
|
||||||
vm := GenesisVM(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
ctx.Lock.Unlock()
|
||||||
state := vm.state
|
state := vm.state
|
||||||
|
|
||||||
vm.codec.RegisterType(&testAddressable{})
|
vm.codec.RegisterType(&testAddressable{})
|
||||||
|
|
|
@ -4,20 +4,27 @@
|
||||||
package avm
|
package avm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ava-labs/gecko/snow/choices"
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/database/memdb"
|
"github.com/ava-labs/gecko/database/memdb"
|
||||||
"github.com/ava-labs/gecko/ids"
|
"github.com/ava-labs/gecko/ids"
|
||||||
"github.com/ava-labs/gecko/snow/engine/common"
|
"github.com/ava-labs/gecko/snow/engine/common"
|
||||||
|
"github.com/ava-labs/gecko/utils/formatting"
|
||||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetAssetDescription(t *testing.T) {
|
func setup(t *testing.T) ([]byte, *VM, *Service) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes := BuildGenesisTest(t)
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
ctx.Lock.Lock()
|
||||||
defer ctx.Lock.Unlock()
|
|
||||||
|
|
||||||
|
// This VM initilialzation is very similar to that done by GenesisVM().
|
||||||
|
// However replacing the body of this function, with a call to GenesisVM
|
||||||
|
// causes a timeout while executing the test suite.
|
||||||
|
// https://github.com/ava-labs/gecko/pull/59#pullrequestreview-392478636
|
||||||
vm := &VM{}
|
vm := &VM{}
|
||||||
err := vm.Initialize(
|
err := vm.Initialize(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -32,16 +39,163 @@ func TestGetAssetDescription(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
s := &Service{vm: vm}
|
||||||
|
return genesisBytes, vm, s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServiceIssueTx(t *testing.T) {
|
||||||
|
genesisBytes, vm, s := setup(t)
|
||||||
|
defer ctx.Lock.Unlock()
|
||||||
|
defer vm.Shutdown()
|
||||||
|
|
||||||
|
txArgs := &IssueTxArgs{}
|
||||||
|
txReply := &IssueTxReply{}
|
||||||
|
err := s.IssueTx(nil, txArgs, txReply)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected empty transaction to return an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := NewTx(t, genesisBytes, vm)
|
||||||
|
txArgs.Tx = formatting.CB58{Bytes: tx.Bytes()}
|
||||||
|
txReply = &IssueTxReply{}
|
||||||
|
if err := s.IssueTx(nil, txArgs, txReply); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !txReply.TxID.Equals(tx.ID()) {
|
||||||
|
t.Fatalf("Expected %q, got %q", txReply.TxID, tx.ID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServiceGetTxStatus(t *testing.T) {
|
||||||
|
genesisBytes, vm, s := setup(t)
|
||||||
|
defer ctx.Lock.Unlock()
|
||||||
|
defer vm.Shutdown()
|
||||||
|
|
||||||
|
statusArgs := &GetTxStatusArgs{}
|
||||||
|
statusReply := &GetTxStatusReply{}
|
||||||
|
if err := s.GetTxStatus(nil, statusArgs, statusReply); err == nil {
|
||||||
|
t.Fatal("Expected empty transaction to return an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := NewTx(t, genesisBytes, vm)
|
||||||
|
statusArgs.TxID = tx.ID()
|
||||||
|
statusReply = &GetTxStatusReply{}
|
||||||
|
if err := s.GetTxStatus(nil, statusArgs, statusReply); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected := choices.Unknown; expected != statusReply.Status {
|
||||||
|
t.Fatalf(
|
||||||
|
"Expected an unsubmitted tx to have status %q, got %q",
|
||||||
|
expected.String(), statusReply.Status.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
txArgs := &IssueTxArgs{Tx: formatting.CB58{Bytes: tx.Bytes()}}
|
||||||
|
txReply := &IssueTxReply{}
|
||||||
|
if err := s.IssueTx(nil, txArgs, txReply); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
statusReply = &GetTxStatusReply{}
|
||||||
|
if err := s.GetTxStatus(nil, statusArgs, statusReply); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected := choices.Processing; expected != statusReply.Status {
|
||||||
|
t.Fatalf(
|
||||||
|
"Expected a submitted tx to have status %q, got %q",
|
||||||
|
expected.String(), statusReply.Status.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServiceGetUTXOsInvalidAddress(t *testing.T) {
|
||||||
|
_, vm, s := setup(t)
|
||||||
|
defer ctx.Lock.Unlock()
|
||||||
|
defer vm.Shutdown()
|
||||||
|
|
||||||
|
addr0 := keys[0].PublicKey().Address()
|
||||||
|
tests := []struct {
|
||||||
|
label string
|
||||||
|
args *GetUTXOsArgs
|
||||||
|
}{
|
||||||
|
{"[", &GetUTXOsArgs{[]string{""}}},
|
||||||
|
{"[-]", &GetUTXOsArgs{[]string{"-"}}},
|
||||||
|
{"[foo]", &GetUTXOsArgs{[]string{"foo"}}},
|
||||||
|
{"[foo-bar]", &GetUTXOsArgs{[]string{"foo-bar"}}},
|
||||||
|
{"[<ChainID>]", &GetUTXOsArgs{[]string{ctx.ChainID.String()}}},
|
||||||
|
{"[<ChainID>-]", &GetUTXOsArgs{[]string{fmt.Sprintf("%s-", ctx.ChainID.String())}}},
|
||||||
|
{"[<Unknown ID>-<addr0>]", &GetUTXOsArgs{[]string{fmt.Sprintf("%s-%s", ids.NewID([32]byte{42}).String(), addr0.String())}}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.label, func(t *testing.T) {
|
||||||
|
utxosReply := &GetUTXOsReply{}
|
||||||
|
if err := s.GetUTXOs(nil, tt.args, utxosReply); err == nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServiceGetUTXOs(t *testing.T) {
|
||||||
|
_, vm, s := setup(t)
|
||||||
|
defer ctx.Lock.Unlock()
|
||||||
|
defer vm.Shutdown()
|
||||||
|
|
||||||
|
addr0 := keys[0].PublicKey().Address()
|
||||||
|
tests := []struct {
|
||||||
|
label string
|
||||||
|
args *GetUTXOsArgs
|
||||||
|
count int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Empty",
|
||||||
|
&GetUTXOsArgs{},
|
||||||
|
0,
|
||||||
|
}, {
|
||||||
|
"[<ChainID>-<unrelated address>]",
|
||||||
|
&GetUTXOsArgs{[]string{
|
||||||
|
// TODO: Should GetUTXOs() raise an error for this? The address portion is
|
||||||
|
// longer than addr0.String()
|
||||||
|
fmt.Sprintf("%s-%s", ctx.ChainID.String(), ids.NewID([32]byte{42}).String()),
|
||||||
|
}},
|
||||||
|
0,
|
||||||
|
}, {
|
||||||
|
"[<ChainID>-<addr0>]",
|
||||||
|
&GetUTXOsArgs{[]string{
|
||||||
|
fmt.Sprintf("%s-%s", ctx.ChainID.String(), addr0.String()),
|
||||||
|
}},
|
||||||
|
7,
|
||||||
|
}, {
|
||||||
|
"[<ChainID>-<addr0>,<ChainID>-<addr0>]",
|
||||||
|
&GetUTXOsArgs{[]string{
|
||||||
|
fmt.Sprintf("%s-%s", ctx.ChainID.String(), addr0.String()),
|
||||||
|
fmt.Sprintf("%s-%s", ctx.ChainID.String(), addr0.String()),
|
||||||
|
}},
|
||||||
|
7,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.label, func(t *testing.T) {
|
||||||
|
utxosReply := &GetUTXOsReply{}
|
||||||
|
if err := s.GetUTXOs(nil, tt.args, utxosReply); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else if tt.count != len(utxosReply.UTXOs) {
|
||||||
|
t.Errorf("Expected %d utxos, got %#v", tt.count, len(utxosReply.UTXOs))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAssetDescription(t *testing.T) {
|
||||||
|
genesisBytes, vm, s := setup(t)
|
||||||
|
defer ctx.Lock.Unlock()
|
||||||
defer vm.Shutdown()
|
defer vm.Shutdown()
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
avaAssetID := genesisTx.ID()
|
avaAssetID := genesisTx.ID()
|
||||||
|
|
||||||
s := Service{vm: vm}
|
|
||||||
|
|
||||||
reply := GetAssetDescriptionReply{}
|
reply := GetAssetDescriptionReply{}
|
||||||
err = s.GetAssetDescription(nil, &GetAssetDescriptionArgs{
|
err := s.GetAssetDescription(nil, &GetAssetDescriptionArgs{
|
||||||
AssetID: avaAssetID.String(),
|
AssetID: avaAssetID.String(),
|
||||||
}, &reply)
|
}, &reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -57,35 +211,16 @@ func TestGetAssetDescription(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetBalance(t *testing.T) {
|
func TestGetBalance(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, vm, s := setup(t)
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
make(chan common.Message, 1),
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer vm.Shutdown()
|
defer vm.Shutdown()
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
avaAssetID := genesisTx.ID()
|
avaAssetID := genesisTx.ID()
|
||||||
|
|
||||||
s := Service{vm: vm}
|
|
||||||
|
|
||||||
reply := GetBalanceReply{}
|
reply := GetBalanceReply{}
|
||||||
err = s.GetBalance(nil, &GetBalanceArgs{
|
err := s.GetBalance(nil, &GetBalanceArgs{
|
||||||
Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
|
Address: vm.Format(keys[0].PublicKey().Address().Bytes()),
|
||||||
AssetID: avaAssetID.String(),
|
AssetID: avaAssetID.String(),
|
||||||
}, &reply)
|
}, &reply)
|
||||||
|
@ -99,31 +234,12 @@ func TestGetBalance(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateFixedCapAsset(t *testing.T) {
|
func TestCreateFixedCapAsset(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
_, vm, s := setup(t)
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
make(chan common.Message, 1),
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer vm.Shutdown()
|
defer vm.Shutdown()
|
||||||
|
|
||||||
s := Service{vm: vm}
|
|
||||||
|
|
||||||
reply := CreateFixedCapAssetReply{}
|
reply := CreateFixedCapAssetReply{}
|
||||||
err = s.CreateFixedCapAsset(nil, &CreateFixedCapAssetArgs{
|
err := s.CreateFixedCapAsset(nil, &CreateFixedCapAssetArgs{
|
||||||
Name: "test asset",
|
Name: "test asset",
|
||||||
Symbol: "test",
|
Symbol: "test",
|
||||||
Denomination: 1,
|
Denomination: 1,
|
||||||
|
@ -142,31 +258,12 @@ func TestCreateFixedCapAsset(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateVariableCapAsset(t *testing.T) {
|
func TestCreateVariableCapAsset(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
_, vm, s := setup(t)
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
defer ctx.Lock.Unlock()
|
defer ctx.Lock.Unlock()
|
||||||
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
make(chan common.Message, 1),
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer vm.Shutdown()
|
defer vm.Shutdown()
|
||||||
|
|
||||||
s := Service{vm: vm}
|
|
||||||
|
|
||||||
reply := CreateVariableCapAssetReply{}
|
reply := CreateVariableCapAssetReply{}
|
||||||
err = s.CreateVariableCapAsset(nil, &CreateVariableCapAssetArgs{
|
err := s.CreateVariableCapAsset(nil, &CreateVariableCapAssetArgs{
|
||||||
Name: "test asset",
|
Name: "test asset",
|
||||||
Symbol: "test",
|
Symbol: "test",
|
||||||
MinterSets: []Owners{
|
MinterSets: []Owners{
|
||||||
|
|
|
@ -15,7 +15,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStateIDs(t *testing.T) {
|
func TestStateIDs(t *testing.T) {
|
||||||
vm := GenesisVM(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
ctx.Lock.Unlock()
|
||||||
state := vm.state.state
|
state := vm.state.state
|
||||||
|
|
||||||
id0 := ids.NewID([32]byte{0xff, 0})
|
id0 := ids.NewID([32]byte{0xff, 0})
|
||||||
|
@ -124,7 +125,8 @@ func TestStateIDs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateStatuses(t *testing.T) {
|
func TestStateStatuses(t *testing.T) {
|
||||||
vm := GenesisVM(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
ctx.Lock.Unlock()
|
||||||
state := vm.state.state
|
state := vm.state.state
|
||||||
|
|
||||||
if _, err := state.Status(ids.Empty); err == nil {
|
if _, err := state.Status(ids.Empty); err == nil {
|
||||||
|
@ -172,7 +174,8 @@ func TestStateStatuses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateUTXOs(t *testing.T) {
|
func TestStateUTXOs(t *testing.T) {
|
||||||
vm := GenesisVM(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
ctx.Lock.Unlock()
|
||||||
state := vm.state.state
|
state := vm.state.state
|
||||||
|
|
||||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||||
|
@ -242,7 +245,8 @@ func TestStateUTXOs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateTXs(t *testing.T) {
|
func TestStateTXs(t *testing.T) {
|
||||||
vm := GenesisVM(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
ctx.Lock.Unlock()
|
||||||
state := vm.state.state
|
state := vm.state.state
|
||||||
|
|
||||||
vm.codec.RegisterType(&ava.TestTransferable{})
|
vm.codec.RegisterType(&ava.TestTransferable{})
|
||||||
|
|
|
@ -20,9 +20,12 @@ func TestTxNil(t *testing.T) {
|
||||||
if err := tx.SyntacticVerify(ctx, c, 1); err == nil {
|
if err := tx.SyntacticVerify(ctx, c, 1); err == nil {
|
||||||
t.Fatalf("Should have errored due to nil tx")
|
t.Fatalf("Should have errored due to nil tx")
|
||||||
}
|
}
|
||||||
|
if err := tx.SemanticVerify(nil, nil); err == nil {
|
||||||
|
t.Fatalf("Should have errored due to nil tx")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxEmpty(t *testing.T) {
|
func setupCodec() codec.Codec {
|
||||||
c := codec.NewDefault()
|
c := codec.NewDefault()
|
||||||
c.RegisterType(&BaseTx{})
|
c.RegisterType(&BaseTx{})
|
||||||
c.RegisterType(&CreateAssetTx{})
|
c.RegisterType(&CreateAssetTx{})
|
||||||
|
@ -34,7 +37,11 @@ func TestTxEmpty(t *testing.T) {
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
c.RegisterType(&secp256k1fx.TransferOutput{})
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
c.RegisterType(&secp256k1fx.MintOperation{})
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
c.RegisterType(&secp256k1fx.Credential{})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxEmpty(t *testing.T) {
|
||||||
|
c := setupCodec()
|
||||||
tx := &Tx{}
|
tx := &Tx{}
|
||||||
if err := tx.SyntacticVerify(ctx, c, 1); err == nil {
|
if err := tx.SyntacticVerify(ctx, c, 1); err == nil {
|
||||||
t.Fatalf("Should have errored due to nil tx")
|
t.Fatalf("Should have errored due to nil tx")
|
||||||
|
@ -42,17 +49,7 @@ func TestTxEmpty(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxInvalidCredential(t *testing.T) {
|
func TestTxInvalidCredential(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
c.RegisterType(&ava.TestVerifiable{})
|
c.RegisterType(&ava.TestVerifiable{})
|
||||||
|
|
||||||
tx := &Tx{
|
tx := &Tx{
|
||||||
|
@ -90,17 +87,7 @@ func TestTxInvalidCredential(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxInvalidUnsignedTx(t *testing.T) {
|
func TestTxInvalidUnsignedTx(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
c.RegisterType(&ava.TestVerifiable{})
|
c.RegisterType(&ava.TestVerifiable{})
|
||||||
|
|
||||||
tx := &Tx{
|
tx := &Tx{
|
||||||
|
@ -158,17 +145,7 @@ func TestTxInvalidUnsignedTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxInvalidNumberOfCredentials(t *testing.T) {
|
func TestTxInvalidNumberOfCredentials(t *testing.T) {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
c.RegisterType(&ava.TestVerifiable{})
|
c.RegisterType(&ava.TestVerifiable{})
|
||||||
|
|
||||||
tx := &Tx{
|
tx := &Tx{
|
||||||
|
|
|
@ -91,8 +91,10 @@ func (cr *codecRegistry) RegisterType(val interface{}) error {
|
||||||
cr.typeToFxIndex[valType] = cr.index
|
cr.typeToFxIndex[valType] = cr.index
|
||||||
return cr.codec.RegisterType(val)
|
return cr.codec.RegisterType(val)
|
||||||
}
|
}
|
||||||
func (cr *codecRegistry) Marshal(val interface{}) ([]byte, error) { return cr.codec.Marshal(val) }
|
func (cr *codecRegistry) Marshal(val interface{}) ([]byte, error) { return cr.codec.Marshal(val) }
|
||||||
func (cr *codecRegistry) Unmarshal(b []byte, val interface{}) error { return cr.codec.Unmarshal(b, val) }
|
func (cr *codecRegistry) Unmarshal(b []byte, val interface{}) error {
|
||||||
|
return cr.codec.Unmarshal(b, val)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
|
@ -198,6 +200,10 @@ func (vm *VM) Initialize(
|
||||||
|
|
||||||
// Shutdown implements the avalanche.DAGVM interface
|
// Shutdown implements the avalanche.DAGVM interface
|
||||||
func (vm *VM) Shutdown() {
|
func (vm *VM) Shutdown() {
|
||||||
|
if vm.timer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
vm.timer.Stop()
|
vm.timer.Stop()
|
||||||
if err := vm.baseDB.Close(); err != nil {
|
if err := vm.baseDB.Close(); err != nil {
|
||||||
vm.ctx.Log.Error("Closing the database failed with %s", err)
|
vm.ctx.Log.Error("Closing the database failed with %s", err)
|
||||||
|
@ -383,7 +389,9 @@ func (vm *VM) initAliases(genesisBytes []byte) error {
|
||||||
|
|
||||||
txID := tx.ID()
|
txID := tx.ID()
|
||||||
|
|
||||||
vm.Alias(txID, genesisTx.Alias)
|
if err = vm.Alias(txID, genesisTx.Alias); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -452,7 +460,10 @@ func (vm *VM) parseTx(b []byte) (*UniqueTx, error) {
|
||||||
if err := vm.state.SetTx(tx.ID(), tx.Tx); err != nil {
|
if err := vm.state.SetTx(tx.ID(), tx.Tx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tx.setStatus(choices.Processing)
|
|
||||||
|
if err := tx.setStatus(choices.Processing); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx, nil
|
return tx, nil
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/ava-labs/gecko/utils/hashing"
|
"github.com/ava-labs/gecko/utils/hashing"
|
||||||
"github.com/ava-labs/gecko/utils/units"
|
"github.com/ava-labs/gecko/utils/units"
|
||||||
"github.com/ava-labs/gecko/vms/components/ava"
|
"github.com/ava-labs/gecko/vms/components/ava"
|
||||||
"github.com/ava-labs/gecko/vms/components/codec"
|
|
||||||
"github.com/ava-labs/gecko/vms/components/verify"
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||||||
"github.com/ava-labs/gecko/vms/nftfx"
|
"github.com/ava-labs/gecko/vms/nftfx"
|
||||||
"github.com/ava-labs/gecko/vms/propertyfx"
|
"github.com/ava-labs/gecko/vms/propertyfx"
|
||||||
|
@ -50,18 +49,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFirstTxFromGenesisTest(genesisBytes []byte, t *testing.T) *Tx {
|
func GetFirstTxFromGenesisTest(genesisBytes []byte, t *testing.T) *Tx {
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
genesis := Genesis{}
|
genesis := Genesis{}
|
||||||
if err := c.Unmarshal(genesisBytes, &genesis); err != nil {
|
if err := c.Unmarshal(genesisBytes, &genesis); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -166,18 +154,20 @@ func BuildGenesisTest(t *testing.T) []byte {
|
||||||
return reply.Bytes.Bytes
|
return reply.Bytes.Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenesisVM(t *testing.T) *VM {
|
func GenesisVM(t *testing.T) ([]byte, chan common.Message, *VM) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes := BuildGenesisTest(t)
|
||||||
|
|
||||||
|
// NB: this lock is intentionally left locked when this function returns.
|
||||||
|
// The caller of this function is responsible for unlocking.
|
||||||
ctx.Lock.Lock()
|
ctx.Lock.Lock()
|
||||||
defer ctx.Lock.Unlock()
|
|
||||||
|
|
||||||
|
issuer := make(chan common.Message, 1)
|
||||||
vm := &VM{}
|
vm := &VM{}
|
||||||
err := vm.Initialize(
|
err := vm.Initialize(
|
||||||
ctx,
|
ctx,
|
||||||
memdb.New(),
|
memdb.New(),
|
||||||
genesisBytes,
|
genesisBytes,
|
||||||
make(chan common.Message, 1),
|
issuer,
|
||||||
[]*common.Fx{&common.Fx{
|
[]*common.Fx{&common.Fx{
|
||||||
ID: ids.Empty,
|
ID: ids.Empty,
|
||||||
Fx: &secp256k1fx.Fx{},
|
Fx: &secp256k1fx.Fx{},
|
||||||
|
@ -188,7 +178,57 @@ func GenesisVM(t *testing.T) *VM {
|
||||||
}
|
}
|
||||||
vm.batchTimeout = 0
|
vm.batchTimeout = 0
|
||||||
|
|
||||||
return vm
|
return genesisBytes, issuer, vm
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTx(t *testing.T, genesisBytes []byte, vm *VM) *Tx {
|
||||||
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
|
newTx := &Tx{UnsignedTx: &BaseTx{
|
||||||
|
NetID: networkID,
|
||||||
|
BCID: chainID,
|
||||||
|
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
||||||
|
UTXOID: ava.UTXOID{
|
||||||
|
TxID: genesisTx.ID(),
|
||||||
|
OutputIndex: 1,
|
||||||
|
},
|
||||||
|
Asset: ava.Asset{ID: genesisTx.ID()},
|
||||||
|
In: &secp256k1fx.TransferInput{
|
||||||
|
Amt: 50000,
|
||||||
|
Input: secp256k1fx.Input{
|
||||||
|
SigIndices: []uint32{
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
unsignedBytes, err := vm.codec.Marshal(&newTx.UnsignedTx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := keys[0]
|
||||||
|
sig, err := key.Sign(unsignedBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fixedSig := [crypto.SECP256K1RSigLen]byte{}
|
||||||
|
copy(fixedSig[:], sig)
|
||||||
|
|
||||||
|
newTx.Creds = append(newTx.Creds, &secp256k1fx.Credential{
|
||||||
|
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
||||||
|
fixedSig,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
b, err := vm.codec.Marshal(newTx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
newTx.Initialize(b)
|
||||||
|
return newTx
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTxSerialization(t *testing.T) {
|
func TestTxSerialization(t *testing.T) {
|
||||||
|
@ -338,18 +378,7 @@ func TestTxSerialization(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
c := codec.NewDefault()
|
c := setupCodec()
|
||||||
c.RegisterType(&BaseTx{})
|
|
||||||
c.RegisterType(&CreateAssetTx{})
|
|
||||||
c.RegisterType(&OperationTx{})
|
|
||||||
c.RegisterType(&ImportTx{})
|
|
||||||
c.RegisterType(&ExportTx{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferInput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.TransferOutput{})
|
|
||||||
c.RegisterType(&secp256k1fx.MintOperation{})
|
|
||||||
c.RegisterType(&secp256k1fx.Credential{})
|
|
||||||
|
|
||||||
b, err := c.Marshal(tx)
|
b, err := c.Marshal(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -427,73 +456,9 @@ type testTxBytes struct{ unsignedBytes []byte }
|
||||||
func (tx *testTxBytes) UnsignedBytes() []byte { return tx.unsignedBytes }
|
func (tx *testTxBytes) UnsignedBytes() []byte { return tx.unsignedBytes }
|
||||||
|
|
||||||
func TestIssueTx(t *testing.T) {
|
func TestIssueTx(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, issuer, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
newTx := NewTx(t, genesisBytes, vm)
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
|
||||||
|
|
||||||
newTx := &Tx{UnsignedTx: &BaseTx{
|
|
||||||
NetID: networkID,
|
|
||||||
BCID: chainID,
|
|
||||||
Ins: []*ava.TransferableInput{&ava.TransferableInput{
|
|
||||||
UTXOID: ava.UTXOID{
|
|
||||||
TxID: genesisTx.ID(),
|
|
||||||
OutputIndex: 1,
|
|
||||||
},
|
|
||||||
Asset: ava.Asset{ID: genesisTx.ID()},
|
|
||||||
In: &secp256k1fx.TransferInput{
|
|
||||||
Amt: 50000,
|
|
||||||
Input: secp256k1fx.Input{
|
|
||||||
SigIndices: []uint32{
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}}
|
|
||||||
|
|
||||||
unsignedBytes, err := vm.codec.Marshal(&newTx.UnsignedTx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
key := keys[0]
|
|
||||||
sig, err := key.Sign(unsignedBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
fixedSig := [crypto.SECP256K1RSigLen]byte{}
|
|
||||||
copy(fixedSig[:], sig)
|
|
||||||
|
|
||||||
newTx.Creds = append(newTx.Creds, &secp256k1fx.Credential{
|
|
||||||
Sigs: [][crypto.SECP256K1RSigLen]byte{
|
|
||||||
fixedSig,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
b, err := vm.codec.Marshal(newTx)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
newTx.Initialize(b)
|
|
||||||
|
|
||||||
txID, err := vm.IssueTx(newTx.Bytes(), nil)
|
txID, err := vm.IssueTx(newTx.Bytes(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -515,24 +480,7 @@ func TestIssueTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenesisGetUTXOs(t *testing.T) {
|
func TestGenesisGetUTXOs(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
_, _, vm := GenesisVM(t)
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
make(chan common.Message, 1),
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
shortAddr := keys[0].PublicKey().Address()
|
shortAddr := keys[0].PublicKey().Address()
|
||||||
addr := ids.NewID(hashing.ComputeHash256Array(shortAddr.Bytes()))
|
addr := ids.NewID(hashing.ComputeHash256Array(shortAddr.Bytes()))
|
||||||
|
@ -554,26 +502,7 @@ func TestGenesisGetUTXOs(t *testing.T) {
|
||||||
// Test issuing a transaction that consumes a currently pending UTXO. The
|
// Test issuing a transaction that consumes a currently pending UTXO. The
|
||||||
// transaction should be issued successfully.
|
// transaction should be issued successfully.
|
||||||
func TestIssueDependentTx(t *testing.T) {
|
func TestIssueDependentTx(t *testing.T) {
|
||||||
genesisBytes := BuildGenesisTest(t)
|
genesisBytes, issuer, vm := GenesisVM(t)
|
||||||
|
|
||||||
issuer := make(chan common.Message, 1)
|
|
||||||
|
|
||||||
ctx.Lock.Lock()
|
|
||||||
vm := &VM{}
|
|
||||||
err := vm.Initialize(
|
|
||||||
ctx,
|
|
||||||
memdb.New(),
|
|
||||||
genesisBytes,
|
|
||||||
issuer,
|
|
||||||
[]*common.Fx{&common.Fx{
|
|
||||||
ID: ids.Empty,
|
|
||||||
Fx: &secp256k1fx.Fx{},
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
vm.batchTimeout = 0
|
|
||||||
|
|
||||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||||
|
|
||||||
|
@ -1005,3 +934,51 @@ func TestIssueProperty(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVMFormat(t *testing.T) {
|
||||||
|
_, _, vm := GenesisVM(t)
|
||||||
|
defer ctx.Lock.Unlock()
|
||||||
|
defer vm.Shutdown()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"", "3D7sudhzUKTYFkYj4Zoe7GgSKhuyP9bYwXunHwhZsmQe1z9Mp-45PJLL"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.in, func(t *testing.T) {
|
||||||
|
if res := vm.Format([]byte(tt.in)); tt.expected != res {
|
||||||
|
t.Errorf("Expected %q, got %q", tt.expected, res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVMFormatAliased(t *testing.T) {
|
||||||
|
_, _, vm := GenesisVM(t)
|
||||||
|
defer ctx.Lock.Unlock()
|
||||||
|
defer vm.Shutdown()
|
||||||
|
|
||||||
|
origAliases := ctx.BCLookup
|
||||||
|
defer func() { ctx.BCLookup = origAliases }()
|
||||||
|
|
||||||
|
tmpAliases := &ids.Aliaser{}
|
||||||
|
tmpAliases.Initialize()
|
||||||
|
tmpAliases.Alias(ctx.ChainID, "X")
|
||||||
|
ctx.BCLookup = tmpAliases
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"", "X-45PJLL"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.in, func(t *testing.T) {
|
||||||
|
if res := vm.Format([]byte(tt.in)); tt.expected != res {
|
||||||
|
t.Errorf("Expected %q, got %q", tt.expected, res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -341,8 +341,5 @@ func (c codec) unmarshal(p *wrappers.Packer, field reflect.Value) error {
|
||||||
|
|
||||||
// Returns true iff [field] should be serialized
|
// Returns true iff [field] should be serialized
|
||||||
func shouldSerialize(field reflect.StructField) bool {
|
func shouldSerialize(field reflect.StructField) bool {
|
||||||
if field.Tag.Get("serialize") == "true" {
|
return field.Tag.Get("serialize") == "true"
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (b *Block) Accept() {
|
||||||
b.SetStatus(choices.Accepted) // Change state of this block
|
b.SetStatus(choices.Accepted) // Change state of this block
|
||||||
b.VM.State.PutStatus(b.VM.DB, b.ID(), choices.Accepted) // Persist data
|
b.VM.State.PutStatus(b.VM.DB, b.ID(), choices.Accepted) // Persist data
|
||||||
b.VM.State.PutLastAccepted(b.VM.DB, b.ID())
|
b.VM.State.PutLastAccepted(b.VM.DB, b.ID())
|
||||||
b.VM.lastAccepted = b.ID() // Change state of VM
|
b.VM.LastAcceptedID = b.ID() // Change state of VM
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject sets this block's status to Rejected and saves the status in state
|
// Reject sets this block's status to Rejected and saves the status in state
|
||||||
|
|
|
@ -45,7 +45,7 @@ type SnowmanVM struct {
|
||||||
preferred ids.ID
|
preferred ids.ID
|
||||||
|
|
||||||
// ID of the last accepted block
|
// ID of the last accepted block
|
||||||
lastAccepted ids.ID
|
LastAcceptedID ids.ID
|
||||||
|
|
||||||
// unmarshals bytes to a block
|
// unmarshals bytes to a block
|
||||||
unmarshalBlockFunc func([]byte) (snowman.Block, error)
|
unmarshalBlockFunc func([]byte) (snowman.Block, error)
|
||||||
|
@ -61,7 +61,7 @@ func (svm *SnowmanVM) SetPreference(ID ids.ID) { svm.preferred = ID }
|
||||||
func (svm *SnowmanVM) Preferred() ids.ID { return svm.preferred }
|
func (svm *SnowmanVM) Preferred() ids.ID { return svm.preferred }
|
||||||
|
|
||||||
// LastAccepted returns the block most recently accepted
|
// LastAccepted returns the block most recently accepted
|
||||||
func (svm *SnowmanVM) LastAccepted() ids.ID { return svm.lastAccepted }
|
func (svm *SnowmanVM) LastAccepted() ids.ID { return svm.LastAcceptedID }
|
||||||
|
|
||||||
// ParseBlock parses [bytes] to a block
|
// ParseBlock parses [bytes] to a block
|
||||||
func (svm *SnowmanVM) ParseBlock(bytes []byte) (snowman.Block, error) {
|
func (svm *SnowmanVM) ParseBlock(bytes []byte) (snowman.Block, error) {
|
||||||
|
@ -83,6 +83,10 @@ func (svm *SnowmanVM) GetBlock(ID ids.ID) (snowman.Block, error) {
|
||||||
|
|
||||||
// Shutdown this vm
|
// Shutdown this vm
|
||||||
func (svm *SnowmanVM) Shutdown() {
|
func (svm *SnowmanVM) Shutdown() {
|
||||||
|
if svm.DB == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
svm.DB.Commit() // Flush DB
|
svm.DB.Commit() // Flush DB
|
||||||
svm.DB.GetDatabase().Close() // close underlying database
|
svm.DB.GetDatabase().Close() // close underlying database
|
||||||
svm.DB.Close() // close versionDB
|
svm.DB.Close() // close versionDB
|
||||||
|
@ -91,10 +95,7 @@ func (svm *SnowmanVM) Shutdown() {
|
||||||
// DBInitialized returns true iff [svm]'s database has values in it already
|
// DBInitialized returns true iff [svm]'s database has values in it already
|
||||||
func (svm *SnowmanVM) DBInitialized() bool {
|
func (svm *SnowmanVM) DBInitialized() bool {
|
||||||
status := svm.State.GetStatus(svm.DB, dbInitializedID)
|
status := svm.State.GetStatus(svm.DB, dbInitializedID)
|
||||||
if status == choices.Accepted {
|
return status == choices.Accepted
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDBInitialized marks the database as initialized
|
// SetDBInitialized marks the database as initialized
|
||||||
|
@ -157,10 +158,10 @@ func (svm *SnowmanVM) Initialize(
|
||||||
}
|
}
|
||||||
|
|
||||||
if svm.DBInitialized() {
|
if svm.DBInitialized() {
|
||||||
if svm.lastAccepted, err = svm.State.GetLastAccepted(svm.DB); err != nil {
|
if svm.LastAcceptedID, err = svm.State.GetLastAccepted(svm.DB); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
svm.preferred = svm.lastAccepted
|
svm.preferred = svm.LastAcceptedID
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/core/types"
|
|
||||||
"github.com/ava-labs/go-ethereum/rlp"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/ava-labs/gecko/snow/choices"
|
|
||||||
"github.com/ava-labs/gecko/snow/consensus/snowman"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Block implements the snowman.Block interface
|
|
||||||
type Block struct {
|
|
||||||
id ids.ID
|
|
||||||
ethBlock *types.Block
|
|
||||||
vm *VM
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID implements the snowman.Block interface
|
|
||||||
func (b *Block) ID() ids.ID { return b.id }
|
|
||||||
|
|
||||||
// Accept implements the snowman.Block interface
|
|
||||||
func (b *Block) Accept() {
|
|
||||||
b.vm.ctx.Log.Verbo("Block %s is accepted", b.ID())
|
|
||||||
b.vm.updateStatus(b.ID(), choices.Accepted)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject implements the snowman.Block interface
|
|
||||||
func (b *Block) Reject() {
|
|
||||||
b.vm.ctx.Log.Verbo("Block %s is rejected", b.ID())
|
|
||||||
b.vm.updateStatus(b.ID(), choices.Rejected)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status implements the snowman.Block interface
|
|
||||||
func (b *Block) Status() choices.Status {
|
|
||||||
status := b.vm.getCachedStatus(b.ID())
|
|
||||||
if status == choices.Unknown && b.ethBlock != nil {
|
|
||||||
return choices.Processing
|
|
||||||
}
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent implements the snowman.Block interface
|
|
||||||
func (b *Block) Parent() snowman.Block {
|
|
||||||
parentID := ids.NewID(b.ethBlock.ParentHash())
|
|
||||||
block := &Block{
|
|
||||||
id: parentID,
|
|
||||||
ethBlock: b.vm.getCachedBlock(parentID),
|
|
||||||
vm: b.vm,
|
|
||||||
}
|
|
||||||
b.vm.ctx.Log.Verbo("Parent(%s) has status: %s", block.ID(), block.Status())
|
|
||||||
return block
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify implements the snowman.Block interface
|
|
||||||
func (b *Block) Verify() error {
|
|
||||||
_, err := b.vm.chain.InsertChain([]*types.Block{b.ethBlock})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes implements the snowman.Block interface
|
|
||||||
func (b *Block) Bytes() []byte {
|
|
||||||
res, err := rlp.EncodeToBytes(b.ethBlock)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Block) String() string { return fmt.Sprintf("EVM block, ID = %s", b.ID()) }
|
|
|
@ -1,66 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/ethdb"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errOpNotSupported = errors.New("this operation is not supported")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Database implements ethdb.Database
|
|
||||||
type Database struct{ database.Database }
|
|
||||||
|
|
||||||
// HasAncient returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db Database) HasAncient(kind string, number uint64) (bool, error) {
|
|
||||||
return false, errOpNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ancient returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db Database) Ancient(kind string, number uint64) ([]byte, error) { return nil, errOpNotSupported }
|
|
||||||
|
|
||||||
// Ancients returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db Database) Ancients() (uint64, error) { return 0, errOpNotSupported }
|
|
||||||
|
|
||||||
// AncientSize returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db Database) AncientSize(kind string) (uint64, error) { return 0, errOpNotSupported }
|
|
||||||
|
|
||||||
// AppendAncient returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db Database) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
|
|
||||||
return errOpNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// TruncateAncients returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db Database) TruncateAncients(items uint64) error { return errOpNotSupported }
|
|
||||||
|
|
||||||
// Sync returns an error as we don't have a backing chain freezer.
|
|
||||||
func (db Database) Sync() error { return errOpNotSupported }
|
|
||||||
|
|
||||||
// NewBatch implements ethdb.Database
|
|
||||||
func (db Database) NewBatch() ethdb.Batch { return Batch{db.Database.NewBatch()} }
|
|
||||||
|
|
||||||
// NewIterator implements ethdb.Database
|
|
||||||
func (db Database) NewIterator() ethdb.Iterator { return db.Database.NewIterator() }
|
|
||||||
|
|
||||||
// NewIteratorWithPrefix implements ethdb.Database
|
|
||||||
func (db Database) NewIteratorWithPrefix(prefix []byte) ethdb.Iterator {
|
|
||||||
return db.NewIteratorWithPrefix(prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewIteratorWithStart implements ethdb.Database
|
|
||||||
func (db Database) NewIteratorWithStart(start []byte) ethdb.Iterator {
|
|
||||||
return db.NewIteratorWithStart(start)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Batch implements ethdb.Batch
|
|
||||||
type Batch struct{ database.Batch }
|
|
||||||
|
|
||||||
// Replay implements ethdb.Batch
|
|
||||||
func (batch Batch) Replay(w ethdb.KeyValueWriter) error { return batch.Batch.Replay(w) }
|
|
|
@ -1,19 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ID this VM should be referenced by
|
|
||||||
var (
|
|
||||||
ID = ids.NewID([32]byte{'e', 'v', 'm'})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Factory ...
|
|
||||||
type Factory struct{}
|
|
||||||
|
|
||||||
// New ...
|
|
||||||
func (f *Factory) New() interface{} { return &VM{} }
|
|
|
@ -1,122 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ava-labs/coreth"
|
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/common"
|
|
||||||
"github.com/ava-labs/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ava-labs/go-ethereum/core/types"
|
|
||||||
"github.com/ava-labs/go-ethereum/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
version = "Athereum 1.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
// test constants
|
|
||||||
const (
|
|
||||||
GenesisTestAddr = "0x751a0b96e1042bee789452ecb20253fba40dbe85"
|
|
||||||
GenesisTestKey = "0xabd71b35d559563fea757f0f5edbde286fb8c043105b15abb7cd57189306d7d1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DebugAPI introduces helper functions for debuging
|
|
||||||
type DebugAPI struct{ vm *VM }
|
|
||||||
|
|
||||||
// SnowmanAPI introduces snowman specific functionality to the evm
|
|
||||||
type SnowmanAPI struct{ vm *VM }
|
|
||||||
|
|
||||||
// NetAPI offers network related API methods
|
|
||||||
type NetAPI struct{ vm *VM }
|
|
||||||
|
|
||||||
// NewNetAPI creates a new net API instance.
|
|
||||||
func NewNetAPI(vm *VM) *NetAPI { return &NetAPI{vm} }
|
|
||||||
|
|
||||||
// Listening returns an indication if the node is listening for network connections.
|
|
||||||
func (s *NetAPI) Listening() bool { return true } // always listening
|
|
||||||
|
|
||||||
// PeerCount returns the number of connected peers
|
|
||||||
func (s *NetAPI) PeerCount() hexutil.Uint { return hexutil.Uint(0) } // TODO: report number of connected peers
|
|
||||||
|
|
||||||
// Version returns the current ethereum protocol version.
|
|
||||||
func (s *NetAPI) Version() string { return fmt.Sprintf("%d", s.vm.networkID) }
|
|
||||||
|
|
||||||
// Web3API offers helper API methods
|
|
||||||
type Web3API struct{}
|
|
||||||
|
|
||||||
// ClientVersion returns the version of the vm running
|
|
||||||
func (s *Web3API) ClientVersion() string { return version }
|
|
||||||
|
|
||||||
// Sha3 returns the bytes returned by hashing [input] with Keccak256
|
|
||||||
func (s *Web3API) Sha3(input hexutil.Bytes) hexutil.Bytes { return crypto.Keccak256(input) }
|
|
||||||
|
|
||||||
// GetAcceptedFrontReply defines the reply that will be sent from the
|
|
||||||
// GetAcceptedFront API call
|
|
||||||
type GetAcceptedFrontReply struct {
|
|
||||||
Hash common.Hash `json:"hash"`
|
|
||||||
Number *big.Int `json:"number"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAcceptedFront returns the last accepted block's hash and height
|
|
||||||
func (api *SnowmanAPI) GetAcceptedFront(ctx context.Context) (*GetAcceptedFrontReply, error) {
|
|
||||||
blk := api.vm.getLastAccepted().ethBlock
|
|
||||||
return &GetAcceptedFrontReply{
|
|
||||||
Hash: blk.Hash(),
|
|
||||||
Number: blk.Number(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGenesisBalance returns the current funds in the genesis
|
|
||||||
func (api *DebugAPI) GetGenesisBalance(ctx context.Context) (*hexutil.Big, error) {
|
|
||||||
lastAccepted := api.vm.getLastAccepted()
|
|
||||||
api.vm.ctx.Log.Verbo("Currently accepted block front: %s", lastAccepted.ethBlock.Hash().Hex())
|
|
||||||
state, err := api.vm.chain.BlockState(lastAccepted.ethBlock)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return (*hexutil.Big)(state.GetBalance(common.HexToAddress(GenesisTestAddr))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpendGenesis funds
|
|
||||||
func (api *DebugAPI) SpendGenesis(ctx context.Context, nonce uint64) error {
|
|
||||||
api.vm.ctx.Log.Info("Spending the genesis")
|
|
||||||
|
|
||||||
value := big.NewInt(1000000000000)
|
|
||||||
gasLimit := 21000
|
|
||||||
gasPrice := big.NewInt(1000000000)
|
|
||||||
|
|
||||||
genPrivateKey, err := crypto.HexToECDSA(GenesisTestKey[2:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bob, err := coreth.NewKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tx := types.NewTransaction(nonce, bob.Address, value, uint64(gasLimit), gasPrice, nil)
|
|
||||||
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(api.vm.chainID), genPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := api.vm.issueRemoteTxs([]*types.Transaction{signedTx}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IssueBlock to the chain
|
|
||||||
func (api *DebugAPI) IssueBlock(ctx context.Context) error {
|
|
||||||
api.vm.ctx.Log.Info("Issuing a new block")
|
|
||||||
|
|
||||||
return api.vm.tryBlockGen()
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/ava-labs/coreth/core"
|
|
||||||
"github.com/ava-labs/gecko/utils/formatting"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StaticService defines the static API services exposed by the evm
|
|
||||||
type StaticService struct{}
|
|
||||||
|
|
||||||
// BuildGenesis returns the UTXOs such that at least one address in [args.Addresses] is
|
|
||||||
// referenced in the UTXO.
|
|
||||||
func (*StaticService) BuildGenesis(_ context.Context, args *core.Genesis) (formatting.CB58, error) {
|
|
||||||
bytes, err := json.Marshal(args)
|
|
||||||
return formatting.CB58{Bytes: bytes}, err
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/common"
|
|
||||||
"github.com/ava-labs/go-ethereum/params"
|
|
||||||
|
|
||||||
"github.com/ava-labs/coreth/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuildGenesis(t *testing.T) {
|
|
||||||
expected := "3wP629bGfSGj9trh1UNBp5qGRGCcma5d8ezLeSmd9hnUJjSMUJesHHoxbZNcVUC9CjH7PEGNA96htNTd1saZCMt1Mf1dZFG7JDhcYNok6RS4TZufejXdxbVVgquohSa7nCCcrXpiVeiRFwzLJAxyQbXzYRhaCRtcDDfCcqfaVdtkFsPbNeQ49pDTbEC5hVkmfopeQ2Zz8tAG5QXKBdbYBCukR3xNHJ4xDxeixmEwPr1odb42yQRYrL7xREKNn2LFoFwAWUjBTsCkf5GPNgY2GvvN9o8wFWXTroW5fp754DhpdxHYxkMTfuE9DGyNWHTyrEbrUHutUdsfitcSHVj5ctFtkN2wGCs3cyv1eRRNvFFMggWTbarjne6AYaeCrJ631qAu3CbrUtrTH5N2E6G2yQKX4sT4Sk3qWPJdsGXuT95iKKcgNn1u5QRHHw9DXXuGPpJjkcKQRGUCuqpXy61iF5RNPEwAwKDa8f2Y25WMmNgWynUuLj8iSAyePj7USPWk54QFUr86ApVzqAdzzdD1qSVScpmudGnGbz9UNXdzHqSot6XLrNTYsgkabiu6TGntFm7qywbCRmtNdBuT9aznGQdUVimjt5QzUz68HXhUxBzTkrz7yXfVGV5JcWxVHQXYS4oc41U5yu83mH3A7WBrZLVq6UyNrvQVbim5nDxeKKbALPxwzVwywjgY5cp39AvzGnY8CX2AtuBNnKmZaAvG8JWAkx3yxjnJrwWhLgpDQYcCvRp2jg1EPBqN8FKJxSPE6eedjDHDJfB57mNzyEtmg22BPnem3eLdiovX8awkhBUHdE7uPrapNSVprnS85u1saW2Kwza3FsS2jAM3LckGW8KdtfPTpHBTRKAUo49zZLuPsyGL5WduedGyAdaM3a2KPoyXuz4UbexTVUWFNypFvvgyoDS8FMxDCNoMMaD7y4yVnoDpSpVFEVZD6EuSGHe9U8Ew57xLPbjhepDx6"
|
|
||||||
|
|
||||||
balance, success := new(big.Int).SetString("33b2e3c9fd0804000000000", 16)
|
|
||||||
if !success {
|
|
||||||
t.Fatal("Failed to initialize balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
args := core.Genesis{
|
|
||||||
Config: ¶ms.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{},
|
|
||||||
GasLimit: 100000000,
|
|
||||||
Difficulty: big.NewInt(0),
|
|
||||||
Mixhash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
Coinbase: common.HexToAddress("0x0000000000000000000000000000000000000000"),
|
|
||||||
Alloc: core.GenesisAlloc{
|
|
||||||
common.HexToAddress("751a0b96e1042bee789452ecb20253fba40dbe85"): core.GenesisAccount{
|
|
||||||
Balance: balance,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Number: 0,
|
|
||||||
GasUsed: 0,
|
|
||||||
ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
}
|
|
||||||
|
|
||||||
ss := StaticService{}
|
|
||||||
result, err := ss.BuildGenesis(nil, &args)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.String() != expected {
|
|
||||||
t.Fatalf("StaticService.BuildGenesis:\nReturned: %s\nExpected: %s", result, expected)
|
|
||||||
}
|
|
||||||
}
|
|
498
vms/evm/vm.go
498
vms/evm/vm.go
|
@ -1,498 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ava-labs/coreth"
|
|
||||||
"github.com/ava-labs/coreth/core"
|
|
||||||
"github.com/ava-labs/coreth/eth"
|
|
||||||
"github.com/ava-labs/coreth/node"
|
|
||||||
|
|
||||||
"github.com/ava-labs/go-ethereum/common"
|
|
||||||
"github.com/ava-labs/go-ethereum/core/types"
|
|
||||||
"github.com/ava-labs/go-ethereum/rlp"
|
|
||||||
"github.com/ava-labs/go-ethereum/rpc"
|
|
||||||
|
|
||||||
"github.com/ava-labs/gecko/cache"
|
|
||||||
"github.com/ava-labs/gecko/database"
|
|
||||||
"github.com/ava-labs/gecko/ids"
|
|
||||||
"github.com/ava-labs/gecko/snow"
|
|
||||||
"github.com/ava-labs/gecko/snow/choices"
|
|
||||||
"github.com/ava-labs/gecko/snow/consensus/snowman"
|
|
||||||
"github.com/ava-labs/gecko/utils/timer"
|
|
||||||
|
|
||||||
commonEng "github.com/ava-labs/gecko/snow/engine/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
lastAcceptedKey = "snowman_lastAccepted"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
minBlockTime = 250 * time.Millisecond
|
|
||||||
maxBlockTime = 1000 * time.Millisecond
|
|
||||||
batchSize = 250
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
bdTimerStateMin = iota
|
|
||||||
bdTimerStateMax
|
|
||||||
bdTimerStateLong
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errEmptyBlock = errors.New("empty block")
|
|
||||||
errCreateBlock = errors.New("couldn't create block")
|
|
||||||
errUnknownBlock = errors.New("unknown block")
|
|
||||||
errBlockFrequency = errors.New("too frequent block issuance")
|
|
||||||
errUnsupportedFXs = errors.New("unsupported feature extensions")
|
|
||||||
)
|
|
||||||
|
|
||||||
func maxDuration(x, y time.Duration) time.Duration {
|
|
||||||
if x > y {
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
|
|
||||||
// VM implements the snowman.ChainVM interface
|
|
||||||
type VM struct {
|
|
||||||
ctx *snow.Context
|
|
||||||
|
|
||||||
chainID *big.Int
|
|
||||||
networkID uint64
|
|
||||||
chain *coreth.ETHChain
|
|
||||||
chaindb Database
|
|
||||||
newBlockChan chan *Block
|
|
||||||
networkChan chan<- commonEng.Message
|
|
||||||
newTxPoolHeadChan chan core.NewTxPoolHeadEvent
|
|
||||||
|
|
||||||
txPoolStabilizedHead common.Hash
|
|
||||||
txPoolStabilizedOk chan struct{}
|
|
||||||
txPoolStabilizedLock sync.Mutex
|
|
||||||
|
|
||||||
metalock sync.Mutex
|
|
||||||
blockCache, blockStatusCache cache.LRU
|
|
||||||
lastAccepted *Block
|
|
||||||
writingMetadata uint32
|
|
||||||
|
|
||||||
bdlock sync.Mutex
|
|
||||||
blockDelayTimer *timer.Timer
|
|
||||||
bdTimerState int8
|
|
||||||
bdGenWaitFlag bool
|
|
||||||
bdGenFlag bool
|
|
||||||
|
|
||||||
genlock sync.Mutex
|
|
||||||
txSubmitChan <-chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
******************************************************************************
|
|
||||||
********************************* Snowman API ********************************
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Initialize implements the snowman.ChainVM interface
|
|
||||||
func (vm *VM) Initialize(
|
|
||||||
ctx *snow.Context,
|
|
||||||
db database.Database,
|
|
||||||
b []byte,
|
|
||||||
toEngine chan<- commonEng.Message,
|
|
||||||
fxs []*commonEng.Fx,
|
|
||||||
) error {
|
|
||||||
if len(fxs) > 0 {
|
|
||||||
return errUnsupportedFXs
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.ctx = ctx
|
|
||||||
vm.chaindb = Database{db}
|
|
||||||
g := new(core.Genesis)
|
|
||||||
err := json.Unmarshal(b, g)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.chainID = g.Config.ChainID
|
|
||||||
|
|
||||||
config := eth.DefaultConfig
|
|
||||||
config.ManualCanonical = true
|
|
||||||
config.Genesis = g
|
|
||||||
config.Miner.ManualMining = true
|
|
||||||
config.Miner.DisableUncle = true
|
|
||||||
if err := config.SetGCMode("archive"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
nodecfg := node.Config{NoUSB: true}
|
|
||||||
chain := coreth.NewETHChain(&config, &nodecfg, nil, vm.chaindb)
|
|
||||||
vm.chain = chain
|
|
||||||
vm.networkID = config.NetworkId
|
|
||||||
chain.SetOnHeaderNew(func(header *types.Header) {
|
|
||||||
hid := make([]byte, 32)
|
|
||||||
_, err := rand.Read(hid)
|
|
||||||
if err != nil {
|
|
||||||
panic("cannot generate hid")
|
|
||||||
}
|
|
||||||
header.Extra = append(header.Extra, hid...)
|
|
||||||
})
|
|
||||||
chain.SetOnSeal(func(block *types.Block) error {
|
|
||||||
if len(block.Transactions()) == 0 {
|
|
||||||
// this could happen due to the async logic of geth tx pool
|
|
||||||
vm.newBlockChan <- nil
|
|
||||||
return errEmptyBlock
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
chain.SetOnSealFinish(func(block *types.Block) error {
|
|
||||||
vm.ctx.Log.Verbo("EVM sealed a block")
|
|
||||||
|
|
||||||
blk := &Block{
|
|
||||||
id: ids.NewID(block.Hash()),
|
|
||||||
ethBlock: block,
|
|
||||||
vm: vm,
|
|
||||||
}
|
|
||||||
vm.newBlockChan <- blk
|
|
||||||
vm.updateStatus(ids.NewID(block.Hash()), choices.Processing)
|
|
||||||
vm.txPoolStabilizedLock.Lock()
|
|
||||||
vm.txPoolStabilizedHead = block.Hash()
|
|
||||||
vm.txPoolStabilizedLock.Unlock()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
chain.SetOnQueryAcceptedBlock(func() *types.Block {
|
|
||||||
return vm.getLastAccepted().ethBlock
|
|
||||||
})
|
|
||||||
vm.blockCache = cache.LRU{Size: 2048}
|
|
||||||
vm.blockStatusCache = cache.LRU{Size: 1024}
|
|
||||||
vm.newBlockChan = make(chan *Block)
|
|
||||||
vm.networkChan = toEngine
|
|
||||||
vm.blockDelayTimer = timer.NewTimer(func() {
|
|
||||||
vm.bdlock.Lock()
|
|
||||||
switch vm.bdTimerState {
|
|
||||||
case bdTimerStateMin:
|
|
||||||
vm.bdTimerState = bdTimerStateMax
|
|
||||||
vm.blockDelayTimer.SetTimeoutIn(maxDuration(maxBlockTime-minBlockTime, 0))
|
|
||||||
case bdTimerStateMax:
|
|
||||||
vm.bdTimerState = bdTimerStateLong
|
|
||||||
}
|
|
||||||
tryAgain := vm.bdGenWaitFlag
|
|
||||||
vm.bdlock.Unlock()
|
|
||||||
if tryAgain {
|
|
||||||
vm.tryBlockGen()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
go ctx.Log.RecoverAndPanic(vm.blockDelayTimer.Dispatch)
|
|
||||||
|
|
||||||
vm.bdTimerState = bdTimerStateLong
|
|
||||||
vm.bdGenWaitFlag = true
|
|
||||||
vm.newTxPoolHeadChan = make(chan core.NewTxPoolHeadEvent, 1)
|
|
||||||
vm.txPoolStabilizedOk = make(chan struct{}, 1)
|
|
||||||
chain.GetTxPool().SubscribeNewHeadEvent(vm.newTxPoolHeadChan)
|
|
||||||
// TODO: shutdown this go routine
|
|
||||||
go ctx.Log.RecoverAndPanic(func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case h := <-vm.newTxPoolHeadChan:
|
|
||||||
vm.txPoolStabilizedLock.Lock()
|
|
||||||
if vm.txPoolStabilizedHead == h.Block.Hash() {
|
|
||||||
vm.txPoolStabilizedOk <- struct{}{}
|
|
||||||
vm.txPoolStabilizedHead = common.Hash{}
|
|
||||||
}
|
|
||||||
vm.txPoolStabilizedLock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
chain.Start()
|
|
||||||
|
|
||||||
var lastAccepted *types.Block
|
|
||||||
if b, err := vm.chaindb.Get([]byte(lastAcceptedKey)); err == nil {
|
|
||||||
var hash common.Hash
|
|
||||||
if err = rlp.DecodeBytes(b, &hash); err == nil {
|
|
||||||
if block := chain.GetBlockByHash(hash); block == nil {
|
|
||||||
vm.ctx.Log.Debug("lastAccepted block not found in chaindb")
|
|
||||||
} else {
|
|
||||||
lastAccepted = block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lastAccepted == nil {
|
|
||||||
vm.ctx.Log.Debug("lastAccepted is unavailable, setting to the genesis block")
|
|
||||||
lastAccepted = chain.GetGenesisBlock()
|
|
||||||
}
|
|
||||||
vm.lastAccepted = &Block{
|
|
||||||
id: ids.NewID(lastAccepted.Hash()),
|
|
||||||
ethBlock: lastAccepted,
|
|
||||||
vm: vm,
|
|
||||||
}
|
|
||||||
vm.ctx.Log.Info(fmt.Sprintf("lastAccepted = %s", vm.lastAccepted.ethBlock.Hash().Hex()))
|
|
||||||
|
|
||||||
// TODO: shutdown this go routine
|
|
||||||
go vm.ctx.Log.RecoverAndPanic(func() {
|
|
||||||
vm.txSubmitChan = vm.chain.GetTxSubmitCh()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-vm.txSubmitChan:
|
|
||||||
vm.ctx.Log.Verbo("New tx detected, trying to generate a block")
|
|
||||||
vm.tryBlockGen()
|
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
vm.tryBlockGen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown implements the snowman.ChainVM interface
|
|
||||||
func (vm *VM) Shutdown() {
|
|
||||||
vm.writeBackMetadata()
|
|
||||||
vm.chain.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildBlock implements the snowman.ChainVM interface
|
|
||||||
func (vm *VM) BuildBlock() (snowman.Block, error) {
|
|
||||||
vm.chain.GenBlock()
|
|
||||||
block := <-vm.newBlockChan
|
|
||||||
if block == nil {
|
|
||||||
return nil, errCreateBlock
|
|
||||||
}
|
|
||||||
// reset the min block time timer
|
|
||||||
vm.bdlock.Lock()
|
|
||||||
vm.bdTimerState = bdTimerStateMin
|
|
||||||
vm.bdGenWaitFlag = false
|
|
||||||
vm.bdGenFlag = false
|
|
||||||
vm.blockDelayTimer.SetTimeoutIn(minBlockTime)
|
|
||||||
vm.bdlock.Unlock()
|
|
||||||
|
|
||||||
vm.ctx.Log.Debug("built block 0x%x", block.ID().Bytes())
|
|
||||||
// make sure Tx Pool is updated
|
|
||||||
<-vm.txPoolStabilizedOk
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBlock implements the snowman.ChainVM interface
|
|
||||||
func (vm *VM) ParseBlock(b []byte) (snowman.Block, error) {
|
|
||||||
vm.metalock.Lock()
|
|
||||||
defer vm.metalock.Unlock()
|
|
||||||
|
|
||||||
ethBlock := new(types.Block)
|
|
||||||
if err := rlp.DecodeBytes(b, ethBlock); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
block := &Block{
|
|
||||||
id: ids.NewID(ethBlock.Hash()),
|
|
||||||
ethBlock: ethBlock,
|
|
||||||
vm: vm,
|
|
||||||
}
|
|
||||||
vm.blockCache.Put(block.ID(), block)
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBlock implements the snowman.ChainVM interface
|
|
||||||
func (vm *VM) GetBlock(id ids.ID) (snowman.Block, error) {
|
|
||||||
vm.metalock.Lock()
|
|
||||||
defer vm.metalock.Unlock()
|
|
||||||
|
|
||||||
block := vm.getBlock(id)
|
|
||||||
if block == nil {
|
|
||||||
return nil, errUnknownBlock
|
|
||||||
}
|
|
||||||
return block, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPreference sets what the current tail of the chain is
|
|
||||||
func (vm *VM) SetPreference(blkID ids.ID) {
|
|
||||||
err := vm.chain.SetTail(blkID.Key())
|
|
||||||
vm.ctx.Log.AssertNoError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LastAccepted returns the ID of the block that was last accepted
|
|
||||||
func (vm *VM) LastAccepted() ids.ID {
|
|
||||||
vm.metalock.Lock()
|
|
||||||
defer vm.metalock.Unlock()
|
|
||||||
|
|
||||||
return vm.lastAccepted.ID()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateHandlers makes new http handlers that can handle API calls
|
|
||||||
func (vm *VM) CreateHandlers() map[string]*commonEng.HTTPHandler {
|
|
||||||
handler := vm.chain.NewRPCHandler()
|
|
||||||
vm.chain.AttachEthService(handler, []string{"eth", "personal", "txpool"})
|
|
||||||
handler.RegisterName("net", &NetAPI{vm})
|
|
||||||
handler.RegisterName("snowman", &SnowmanAPI{vm})
|
|
||||||
handler.RegisterName("web3", &Web3API{})
|
|
||||||
handler.RegisterName("debug", &DebugAPI{vm})
|
|
||||||
|
|
||||||
return map[string]*commonEng.HTTPHandler{
|
|
||||||
"/rpc": &commonEng.HTTPHandler{LockOptions: commonEng.NoLock, Handler: handler},
|
|
||||||
"/ws": &commonEng.HTTPHandler{LockOptions: commonEng.NoLock, Handler: handler.WebsocketHandler([]string{"*"})},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateStaticHandlers makes new http handlers that can handle API calls
|
|
||||||
func (vm *VM) CreateStaticHandlers() map[string]*commonEng.HTTPHandler {
|
|
||||||
handler := rpc.NewServer()
|
|
||||||
handler.RegisterName("static", &StaticService{})
|
|
||||||
return map[string]*commonEng.HTTPHandler{
|
|
||||||
"/rpc": &commonEng.HTTPHandler{LockOptions: commonEng.NoLock, Handler: handler},
|
|
||||||
"/ws": &commonEng.HTTPHandler{LockOptions: commonEng.NoLock, Handler: handler.WebsocketHandler([]string{"*"})},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
******************************************************************************
|
|
||||||
*********************************** Helpers **********************************
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (vm *VM) updateStatus(blockID ids.ID, status choices.Status) {
|
|
||||||
vm.metalock.Lock()
|
|
||||||
defer vm.metalock.Unlock()
|
|
||||||
|
|
||||||
if status == choices.Accepted {
|
|
||||||
vm.lastAccepted = vm.getBlock(blockID)
|
|
||||||
// TODO: improve this naive implementation
|
|
||||||
if atomic.SwapUint32(&vm.writingMetadata, 1) == 0 {
|
|
||||||
go vm.ctx.Log.RecoverAndPanic(vm.writeBackMetadata)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vm.blockStatusCache.Put(blockID, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) getCachedBlock(blockID ids.ID) *types.Block {
|
|
||||||
return vm.chain.GetBlockByHash(blockID.Key())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) tryBlockGen() error {
|
|
||||||
vm.bdlock.Lock()
|
|
||||||
defer vm.bdlock.Unlock()
|
|
||||||
if vm.bdGenFlag {
|
|
||||||
// skip if one call already generates a block in this round
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
vm.bdGenWaitFlag = true
|
|
||||||
|
|
||||||
vm.genlock.Lock()
|
|
||||||
defer vm.genlock.Unlock()
|
|
||||||
// get pending size
|
|
||||||
size, err := vm.chain.PendingSize()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if size == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch vm.bdTimerState {
|
|
||||||
case bdTimerStateMin:
|
|
||||||
return nil
|
|
||||||
case bdTimerStateMax:
|
|
||||||
if size < batchSize {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case bdTimerStateLong:
|
|
||||||
// timeout; go ahead and generate a new block anyway
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case vm.networkChan <- commonEng.PendingTxs:
|
|
||||||
// successfully push out the notification; this round ends
|
|
||||||
vm.bdGenFlag = true
|
|
||||||
default:
|
|
||||||
return errBlockFrequency
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) getCachedStatus(blockID ids.ID) choices.Status {
|
|
||||||
vm.metalock.Lock()
|
|
||||||
defer vm.metalock.Unlock()
|
|
||||||
status := choices.Processing
|
|
||||||
|
|
||||||
if statusIntf, ok := vm.blockStatusCache.Get(blockID); ok {
|
|
||||||
status = statusIntf.(choices.Status)
|
|
||||||
} else {
|
|
||||||
blk := vm.chain.GetBlockByHash(blockID.Key())
|
|
||||||
if blk == nil {
|
|
||||||
return choices.Unknown
|
|
||||||
}
|
|
||||||
acceptedBlk := vm.lastAccepted.ethBlock
|
|
||||||
|
|
||||||
// TODO: There must be a better way of doing this.
|
|
||||||
// Traverse up the chain from the lower block until the indices match
|
|
||||||
highBlock := blk
|
|
||||||
lowBlock := acceptedBlk
|
|
||||||
if highBlock.Number().Cmp(lowBlock.Number()) < 0 {
|
|
||||||
highBlock, lowBlock = lowBlock, highBlock
|
|
||||||
}
|
|
||||||
for highBlock.Number().Cmp(lowBlock.Number()) > 0 {
|
|
||||||
highBlock = vm.chain.GetBlockByHash(highBlock.ParentHash())
|
|
||||||
}
|
|
||||||
|
|
||||||
if highBlock.Hash() == lowBlock.Hash() { // on the same branch
|
|
||||||
if blk.Number().Cmp(acceptedBlk.Number()) <= 0 {
|
|
||||||
status = choices.Accepted
|
|
||||||
}
|
|
||||||
} else { // on different branches
|
|
||||||
status = choices.Rejected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.blockStatusCache.Put(blockID, status)
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) getBlock(id ids.ID) *Block {
|
|
||||||
if blockIntf, ok := vm.blockCache.Get(id); ok {
|
|
||||||
return blockIntf.(*Block)
|
|
||||||
}
|
|
||||||
ethBlock := vm.getCachedBlock(id)
|
|
||||||
if ethBlock == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
block := &Block{
|
|
||||||
id: ids.NewID(ethBlock.Hash()),
|
|
||||||
ethBlock: ethBlock,
|
|
||||||
vm: vm,
|
|
||||||
}
|
|
||||||
vm.blockCache.Put(id, block)
|
|
||||||
return block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) issueRemoteTxs(txs []*types.Transaction) error {
|
|
||||||
errs := vm.chain.AddRemoteTxs(txs)
|
|
||||||
for _, err := range errs {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vm.tryBlockGen()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) writeBackMetadata() {
|
|
||||||
vm.metalock.Lock()
|
|
||||||
defer vm.metalock.Unlock()
|
|
||||||
|
|
||||||
b, err := rlp.EncodeToBytes(vm.lastAccepted.ethBlock.Hash())
|
|
||||||
if err != nil {
|
|
||||||
vm.ctx.Log.Error("snowman-eth: error while writing back metadata")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vm.ctx.Log.Debug("writing back metadata")
|
|
||||||
vm.chaindb.Put([]byte(lastAcceptedKey), b)
|
|
||||||
atomic.StoreUint32(&vm.writingMetadata, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) getLastAccepted() *Block {
|
|
||||||
vm.metalock.Lock()
|
|
||||||
defer vm.metalock.Unlock()
|
|
||||||
|
|
||||||
return vm.lastAccepted
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
||||||
// See the file LICENSE for licensing terms.
|
|
||||||
|
|
||||||
package evm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ava-labs/coreth/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseGenesis(t *testing.T) {
|
|
||||||
genesis := []byte(`{"config":{"chainId":43110,"homesteadBlock":0,"daoForkBlock":0,"daoForkSupport":true,"eip150Block":0,"eip150Hash":"0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0},"nonce":"0x0","timestamp":"0x0","extraData":"0x00","gasLimit":"0x5f5e100","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"751a0b96e1042bee789452ecb20253fba40dbe85":{"balance":"0x33b2e3c9fd0804000000000"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`)
|
|
||||||
|
|
||||||
genesisBlock := new(core.Genesis)
|
|
||||||
err := json.Unmarshal(genesis, genesisBlock)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
marshalledBytes, err := json.Marshal(genesisBlock)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secondGenesisBlock := new(core.Genesis)
|
|
||||||
err = json.Unmarshal(marshalledBytes, secondGenesisBlock)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
|
|
||||||
// A VMFactory creates new instances of a VM
|
// A VMFactory creates new instances of a VM
|
||||||
type VMFactory interface {
|
type VMFactory interface {
|
||||||
New() interface{}
|
New() (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager is a VM manager.
|
// Manager is a VM manager.
|
||||||
|
@ -110,10 +110,17 @@ func (m *manager) addStaticAPIEndpoints(vmID ids.ID) {
|
||||||
vmFactory, err := m.GetVMFactory(vmID)
|
vmFactory, err := m.GetVMFactory(vmID)
|
||||||
m.log.AssertNoError(err)
|
m.log.AssertNoError(err)
|
||||||
m.log.Debug("adding static API for VM with ID %s", vmID)
|
m.log.Debug("adding static API for VM with ID %s", vmID)
|
||||||
vm := vmFactory.New()
|
vm, err := vmFactory.New()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
staticVM, ok := vm.(common.StaticVM)
|
staticVM, ok := vm.(common.StaticVM)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
staticVM, ok := vm.(common.VM)
|
||||||
|
if ok {
|
||||||
|
staticVM.Shutdown()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +131,8 @@ func (m *manager) addStaticAPIEndpoints(vmID ids.ID) {
|
||||||
// register the static endpoints
|
// register the static endpoints
|
||||||
for extension, service := range staticVM.CreateStaticHandlers() {
|
for extension, service := range staticVM.CreateStaticHandlers() {
|
||||||
m.log.Verbo("adding static API endpoint: %s", defaultEndpoint+extension)
|
m.log.Verbo("adding static API endpoint: %s", defaultEndpoint+extension)
|
||||||
m.apiServer.AddRoute(service, lock, defaultEndpoint, extension, m.log)
|
if err := m.apiServer.AddRoute(service, lock, defaultEndpoint, extension, m.log); err != nil {
|
||||||
|
m.log.Warn("failed to add static API endpoint %s: %v", fmt.Sprintf("%s%s", defaultEndpoint, extension), err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,4 @@ var (
|
||||||
type Factory struct{}
|
type Factory struct{}
|
||||||
|
|
||||||
// New ...
|
// New ...
|
||||||
func (f *Factory) New() interface{} { return &Fx{} }
|
func (f *Factory) New() (interface{}, error) { return &Fx{}, nil }
|
||||||
|
|
|
@ -6,7 +6,9 @@ import (
|
||||||
|
|
||||||
func TestFactory(t *testing.T) {
|
func TestFactory(t *testing.T) {
|
||||||
factory := Factory{}
|
factory := Factory{}
|
||||||
if fx := factory.New(); fx == nil {
|
if fx, err := factory.New(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if fx == nil {
|
||||||
t.Fatalf("Factory.New returned nil")
|
t.Fatalf("Factory.New returned nil")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// Abort being accepted results in the proposal of its parent (which must be a proposal block)
|
// Abort being accepted results in the proposal of its parent (which must be a proposal block)
|
||||||
// being rejected.
|
// being rejected.
|
||||||
type Abort struct {
|
type Abort struct {
|
||||||
CommonDecisionBlock `serialize:"true"`
|
DoubleDecisionBlock `serialize:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify this block performs a valid state transition.
|
// Verify this block performs a valid state transition.
|
||||||
|
@ -35,14 +35,10 @@ func (a *Abort) Verify() error {
|
||||||
// newAbortBlock returns a new *Abort block where the block's parent, a proposal
|
// newAbortBlock returns a new *Abort block where the block's parent, a proposal
|
||||||
// block, has ID [parentID].
|
// block, has ID [parentID].
|
||||||
func (vm *VM) newAbortBlock(parentID ids.ID) *Abort {
|
func (vm *VM) newAbortBlock(parentID ids.ID) *Abort {
|
||||||
abort := &Abort{
|
abort := &Abort{DoubleDecisionBlock: DoubleDecisionBlock{CommonDecisionBlock: CommonDecisionBlock{CommonBlock: CommonBlock{
|
||||||
CommonDecisionBlock: CommonDecisionBlock{
|
Block: core.NewBlock(parentID),
|
||||||
CommonBlock: CommonBlock{
|
vm: vm,
|
||||||
Block: core.NewBlock(parentID),
|
}}}}
|
||||||
vm: vm,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// We serialize this block as a Block so that it can be deserialized into a
|
// We serialize this block as a Block so that it can be deserialized into a
|
||||||
// Block
|
// Block
|
||||||
|
|
|
@ -36,7 +36,7 @@ type AtomicTx interface {
|
||||||
// AtomicBlock being accepted results in the transaction contained in the
|
// AtomicBlock being accepted results in the transaction contained in the
|
||||||
// block to be accepted and committed to the chain.
|
// block to be accepted and committed to the chain.
|
||||||
type AtomicBlock struct {
|
type AtomicBlock struct {
|
||||||
CommonDecisionBlock `serialize:"true"`
|
SingleDecisionBlock `serialize:"true"`
|
||||||
|
|
||||||
Tx AtomicTx `serialize:"true"`
|
Tx AtomicTx `serialize:"true"`
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ type AtomicBlock struct {
|
||||||
|
|
||||||
// initialize this block
|
// initialize this block
|
||||||
func (ab *AtomicBlock) initialize(vm *VM, bytes []byte) error {
|
func (ab *AtomicBlock) initialize(vm *VM, bytes []byte) error {
|
||||||
if err := ab.CommonDecisionBlock.initialize(vm, bytes); err != nil {
|
if err := ab.SingleDecisionBlock.initialize(vm, bytes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ab.Tx.initialize(vm)
|
return ab.Tx.initialize(vm)
|
||||||
|
@ -133,12 +133,12 @@ func (ab *AtomicBlock) Accept() {
|
||||||
// decision block, has ID [parentID].
|
// decision block, has ID [parentID].
|
||||||
func (vm *VM) newAtomicBlock(parentID ids.ID, tx AtomicTx) (*AtomicBlock, error) {
|
func (vm *VM) newAtomicBlock(parentID ids.ID, tx AtomicTx) (*AtomicBlock, error) {
|
||||||
ab := &AtomicBlock{
|
ab := &AtomicBlock{
|
||||||
CommonDecisionBlock: CommonDecisionBlock{
|
SingleDecisionBlock: SingleDecisionBlock{CommonDecisionBlock: CommonDecisionBlock{
|
||||||
CommonBlock: CommonBlock{
|
CommonBlock: CommonBlock{
|
||||||
Block: core.NewBlock(parentID),
|
Block: core.NewBlock(parentID),
|
||||||
vm: vm,
|
vm: vm,
|
||||||
},
|
},
|
||||||
},
|
}},
|
||||||
Tx: tx,
|
Tx: tx,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// Commit being accepted results in the proposal of its parent (which must be a proposal block)
|
// Commit being accepted results in the proposal of its parent (which must be a proposal block)
|
||||||
// being enacted.
|
// being enacted.
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
CommonDecisionBlock `serialize:"true"`
|
DoubleDecisionBlock `serialize:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify this block performs a valid state transition.
|
// Verify this block performs a valid state transition.
|
||||||
|
@ -35,14 +35,10 @@ func (c *Commit) Verify() error {
|
||||||
// newCommitBlock returns a new *Commit block where the block's parent, a
|
// newCommitBlock returns a new *Commit block where the block's parent, a
|
||||||
// proposal block, has ID [parentID].
|
// proposal block, has ID [parentID].
|
||||||
func (vm *VM) newCommitBlock(parentID ids.ID) *Commit {
|
func (vm *VM) newCommitBlock(parentID ids.ID) *Commit {
|
||||||
commit := &Commit{
|
commit := &Commit{DoubleDecisionBlock: DoubleDecisionBlock{CommonDecisionBlock: CommonDecisionBlock{CommonBlock: CommonBlock{
|
||||||
CommonDecisionBlock: CommonDecisionBlock{
|
Block: core.NewBlock(parentID),
|
||||||
CommonBlock: CommonBlock{
|
vm: vm,
|
||||||
Block: core.NewBlock(parentID),
|
}}}}
|
||||||
vm: vm,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// We serialize this block as a Block so that it can be deserialized into a
|
// We serialize this block as a Block so that it can be deserialized into a
|
||||||
// Block
|
// Block
|
||||||
|
|
|
@ -174,7 +174,7 @@ func (cb *CommonBlock) parentBlock() Block {
|
||||||
parentID := cb.ParentID()
|
parentID := cb.ParentID()
|
||||||
parent, err := cb.vm.getBlock(parentID)
|
parent, err := cb.vm.getBlock(parentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cb.vm.Ctx.Log.Warn("could not get parent (ID %s) of block %s", parentID, cb.ID())
|
cb.vm.Ctx.Log.Debug("could not get parent (ID %s) of block %s", parentID, cb.ID())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cb.parent = parent
|
cb.parent = parent
|
||||||
|
@ -185,7 +185,6 @@ func (cb *CommonBlock) parentBlock() Block {
|
||||||
func (cb *CommonBlock) addChild(child Block) { cb.children = append(cb.children, child) }
|
func (cb *CommonBlock) addChild(child Block) { cb.children = append(cb.children, child) }
|
||||||
|
|
||||||
// CommonDecisionBlock contains the fields and methods common to all decision blocks
|
// CommonDecisionBlock contains the fields and methods common to all decision blocks
|
||||||
// (ie *Commit and *Abort)
|
|
||||||
type CommonDecisionBlock struct {
|
type CommonDecisionBlock struct {
|
||||||
CommonBlock `serialize:"true"`
|
CommonBlock `serialize:"true"`
|
||||||
|
|
||||||
|
@ -220,29 +219,70 @@ func (cdb *CommonDecisionBlock) onAccept() database.Database {
|
||||||
return cdb.onAcceptDB
|
return cdb.onAcceptDB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept implements the snowman.Block interface
|
// SingleDecisionBlock contains the accept for standalone decision blocks
|
||||||
func (cdb *CommonDecisionBlock) Accept() {
|
type SingleDecisionBlock struct {
|
||||||
cdb.VM.Ctx.Log.Verbo("Accepting block with ID %s", cdb.ID())
|
CommonDecisionBlock `serialize:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
cdb.CommonBlock.Accept()
|
// Accept implements the snowman.Block interface
|
||||||
|
func (sdb *SingleDecisionBlock) Accept() {
|
||||||
|
sdb.VM.Ctx.Log.Verbo("Accepting block with ID %s", sdb.ID())
|
||||||
|
|
||||||
|
sdb.CommonBlock.Accept()
|
||||||
|
|
||||||
// Update the state of the chain in the database
|
// Update the state of the chain in the database
|
||||||
if err := cdb.onAcceptDB.Commit(); err != nil {
|
if err := sdb.onAcceptDB.Commit(); err != nil {
|
||||||
cdb.vm.Ctx.Log.Warn("unable to commit onAcceptDB")
|
sdb.vm.Ctx.Log.Warn("unable to commit onAcceptDB")
|
||||||
}
|
}
|
||||||
if err := cdb.vm.DB.Commit(); err != nil {
|
if err := sdb.vm.DB.Commit(); err != nil {
|
||||||
cdb.vm.Ctx.Log.Warn("unable to commit vm's DB")
|
sdb.vm.Ctx.Log.Warn("unable to commit vm's DB")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, child := range cdb.children {
|
for _, child := range sdb.children {
|
||||||
child.setBaseDatabase(cdb.vm.DB)
|
child.setBaseDatabase(sdb.vm.DB)
|
||||||
}
|
}
|
||||||
if cdb.onAcceptFunc != nil {
|
if sdb.onAcceptFunc != nil {
|
||||||
cdb.onAcceptFunc()
|
sdb.onAcceptFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
sdb.free()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoubleDecisionBlock contains the accept for a pair of blocks
|
||||||
|
type DoubleDecisionBlock struct {
|
||||||
|
CommonDecisionBlock `serialize:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept implements the snowman.Block interface
|
||||||
|
func (ddb *DoubleDecisionBlock) Accept() {
|
||||||
|
ddb.VM.Ctx.Log.Verbo("Accepting block with ID %s", ddb.ID())
|
||||||
|
|
||||||
|
parent, ok := ddb.parentBlock().(*ProposalBlock)
|
||||||
|
if !ok {
|
||||||
|
ddb.vm.Ctx.Log.Error("double decision block should only follow a proposal block")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.CommonBlock.Accept()
|
||||||
|
|
||||||
|
ddb.CommonBlock.Accept()
|
||||||
|
|
||||||
|
// Update the state of the chain in the database
|
||||||
|
if err := ddb.onAcceptDB.Commit(); err != nil {
|
||||||
|
ddb.vm.Ctx.Log.Warn("unable to commit onAcceptDB")
|
||||||
|
}
|
||||||
|
if err := ddb.vm.DB.Commit(); err != nil {
|
||||||
|
ddb.vm.Ctx.Log.Warn("unable to commit vm's DB")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range ddb.children {
|
||||||
|
child.setBaseDatabase(ddb.vm.DB)
|
||||||
|
}
|
||||||
|
if ddb.onAcceptFunc != nil {
|
||||||
|
ddb.onAcceptFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
parent := cdb.parentBlock()
|
|
||||||
// remove this block and its parent from memory
|
// remove this block and its parent from memory
|
||||||
parent.free()
|
parent.free()
|
||||||
cdb.free()
|
ddb.free()
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,12 @@ type Factory struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new instance of the Platform Chain
|
// New returns a new instance of the Platform Chain
|
||||||
func (f *Factory) New() interface{} {
|
func (f *Factory) New() (interface{}, error) {
|
||||||
return &VM{
|
return &VM{
|
||||||
chainManager: f.ChainManager,
|
chainManager: f.ChainManager,
|
||||||
validators: f.Validators,
|
validators: f.Validators,
|
||||||
stakingEnabled: f.StakingEnabled,
|
stakingEnabled: f.StakingEnabled,
|
||||||
ava: f.AVA,
|
ava: f.AVA,
|
||||||
avm: f.AVM,
|
avm: f.AVM,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue