init repo

This commit is contained in:
StephenButtolph 2020-03-10 15:20:34 -04:00
commit b9e34e3b15
522 changed files with 70992 additions and 0 deletions

14
.ci/after_success.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -ev
bash <(curl -s https://codecov.io/bash)
docker tag $DOCKERHUB_REPO:$COMMIT $DOCKERHUB_REPO:travis-$TRAVIS_BUILD_NUMBER
if [ "${TRAVIS_EVENT_TYPE}" == "push" ] && [ "${TRAVIS_BRANCH}" == "platform" ]; then
docker tag $DOCKERHUB_REPO:$COMMIT $DOCKERHUB_REPO:$TRAVIS_BRANCH
fi
echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin
docker push $DOCKERHUB_REPO

19
.ci/before_install.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
set -ev
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
# hack to address problem with using DOCKER_BUILDKIT=1, inspired by:
# * https://github.com/rootless-containers/usernetes/blob/master/.travis.yml
#
# links discussing the issue:
# * https://github.com/moby/buildkit/issues/606#issuecomment-453959632
# * https://travis-ci.community/t/docker-builds-are-broken-if-buildkit-is-used-docker-buildkit-1/2994
# * https://github.com/moby/moby/issues/39120
sudo docker --version
sudo cat /etc/docker/daemon.json
sudo rm -f /etc/docker/daemon.json
sudo systemctl restart docker

18
.codecov.yml Normal file
View File

@ -0,0 +1,18 @@
codecov:
branch: platform
coverage:
range: 60..100
round: down
precision: 5
status:
project:
default:
threshold: 5%
patch:
default:
threshold: 50%
comment:
layout: "header, diff, changes, sunburst, uncovered"
behavior: default

26
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,26 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Operating System**
Which OS you used to reveal the bug.
**Additional context**
Add any other context about the problem here.

45
.gitignore vendored Normal file
View File

@ -0,0 +1,45 @@
*.log
*~
.DS_Store
awscpu
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
*.profile
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
*logs/
.vscode*
*.pb*
*.ava
db*
*cpu[0-9]*
*mem[0-9]*
*lock[0-9]*
*.profile
*.swp
*.aux
*.fdb*
*.fls
*.gz
*.pdf
.coverage
bin/
build/
*/mykey/staker.*

16
.travis.yml Normal file
View File

@ -0,0 +1,16 @@
dist: bionic
language: go
services:
- docker
env:
global:
- CODECOV_TOKEN="8c18c993-fc6e-4706-998b-01ddc7987804"
- GECKO_HOME=/go/src/github.com/ava-labs/gecko/
- COMMIT=${TRAVIS_COMMIT::8}
- DOCKERHUB_REPO=avaplatform/gecko
- secure: "L/A9+re0NEKP6EV6H9RcTGiDhX3WMvsiWrkRKDYKqnviqbjY30RK6EM4vvjrM4Lrw2QwsO3YKgnku3+zioE/TxEZFkpkbjNUXru0nYBrWAg1TKVsDXnYaIZkHUejfryST3E8N7F4Hx6zCtGEO0sEdUeKuT+MNUIuHezHooTgGzDjMogm70EWMFjQHc7VucTJu7dWU1RBPjovWQ0q9qflrtCpbrvXFIiihQQ1PQha1Q2C4wLakKuLbhhSafue90Mnyss0blaPHy/tyewcASJu4vsGTKRBn0DzttlkNTwuD6+nKrbmJY0ohunnkVFzYjrZAw1gyN+DCDb/lPbz4ZDItKPwrIUPEtL5xuUOrxUZPUh+0io3Q2d6rjaqkdGjd1KQXzbnW1mn0BxX3d3b2UpIqhBn9umYYjHBKnMuoRiTK33b7U9+LF3K84+tEvVDCPeHs/mw6Inp5jGRSravnM6yPQ6feGzogs4+3EMzZXxnkngKFKCsnd67Oe9xfV9amOU2aQAx4jaAwlPjEpBEkUa8YKx3lPznvmUk1QsNCUbLjdSl5JBaXojLJoiuPbj29hp4S5AXXgn+3Hvwk3ndcFCxi6/l1W9mjYSOtFqg3EAUdF4EgnA/ykQg9ZokkoKY0+qgOzG2bKOAYuCDWeGr7P1apToh00ccsQXL81nVPiq7uDw="
- secure: "zfTm7tJBYiPYrli76d4Ep6Lc2TJQ8Xv//+7OoqTA/aIf6YJDHe05f2GFTWAHG2iOIix/yjwHYwnhyIW66eWPb+Ujejnmh4eXlYZFufX9J5jUpDpbFu/+ybOLgE1Tmr0je0ycneSMe/NAaS74nWU1wnP34/cEE4sYL7TJyhwbeEtgz3cbSWwkpdvHFbXCjSOA196jdIYYUwsnqU9yycAG+2WUSk3DHHzzdtMrh/UOH2r1VFyp5US0zmbW90WkWX+o3TIlzZJgTUGQRNnWKq95Mrh1EQotxgL6CJ8NkfY4bVAGAhusPjdjscJsHxfY93WRMH64TzPYYp0zdibatH0ztyhnZPXVKqv+AIIVTEW+xWv5V18kTQAd1uBW103NFacbgXhIGWtbFcN9g1+ws29HROMclYs7ci6+72Qnq0eL55huqSyFx6+InhYwn+LfJmaBcGW4wx1umdp505M0obZ4ghlyn6b0pDYmqsu1XyBC3mjUTFbwlQmWE2Fize4L5o+DdH4ZDc9japF9ntxIMvO+b3nOicr7tplY2AGp61bB89o3dUAFlN5mDaEJotiAuFk5mo244rY1FjSzyGiKkA3M9TkTIbgcbN098hOJoMCYybH7yqiPwNnZiFvUuYjHuC5D1kIYBWuqqO0iVcbIZn0rV2jyzbVFlhFVk2clTZGhkrY="
before_install: .ci/before_install.sh
install: DOCKER_BUILDKIT=1 docker build --progress plain --ssh default -t $DOCKERHUB_REPO:$COMMIT .
script: docker run --rm -v "$PWD:$GECKO_HOME" $DOCKERHUB_REPO:$COMMIT bash "$GECKO_HOME/scripts/build_test.sh"
after_success: .ci/after_success.sh

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
# syntax=docker/dockerfile:experimental
FROM golang:1.13.4-buster
RUN apt-get update && apt-get install -y libssl-dev libuv1-dev curl cmake
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/
COPY . gecko
WORKDIR $GOPATH/src/github.com/ava-labs/gecko
RUN ./scripts/build.sh

29
LICENSE Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2020, Ava Labs, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

84
README.md Normal file
View File

@ -0,0 +1,84 @@
# gecko
## Installation
AVA is an incredibly lightweight protocol, so the minimum computer requirements are quite modest.
- Hardware: 2 GHz or faster CPU, 3 GB RAM, 250 MB hard disk.
- OS: Ubuntu >= 18.04 or Mac OS X >= Catalina.
- Software: [Go](https://golang.org/doc/install) version >= 1.13.X and set up [`$GOPATH`](https://github.com/golang/go/wiki/SettingGOPATH).
- Network: IPv4 or IPv6 network connection, with an open public port.
### Native Install
Ubuntu users need the following libraries:
* libssl-dev
* libuv1-dev
* cmake
* make
* curl
* g++
Install the libraries:
```sh
sudo apt-get install libssl-dev libuv1-dev cmake make curl g++
```
#### Downloading Gecko Source Code
Clone the Gecko repository:
```sh
cd $GOPATH
mkdir -p src/github.com/ava-labs
cd src/github.com/ava-labs
git clone https://github.com/ava-labs/gecko.git
cd gecko
```
#### Building the Gecko Executable
Build Gecko using the build script:
```sh
./scripts/build.sh
```
The Gecko binary, named `ava`, is in the `build` directory.
### Docker Install
- Make sure you have docker installed on your machine (so commands like `docker run` etc. are available).
- Build the docker image of latest gecko branch by `scripts/build_image.sh`.
- Check the built image by `docker image ls`, you should see some image tagged
`gecko-xxxxxxxx`, where `xxxxxxxx` is the commit id of the Gecko source it was built from.
- Test Gecko by `docker run -ti -p 9651:9651 gecko-xxxxxxxx /gecko/build/ava
--public-ip=127.0.0.1 --snow-sample-size=1 --snow-quorum-size=1 --staking-tls-enabled=false`. (For a production deployment,
you may want to extend the docker image with required credentials for
staking and TLS.)
## Running Gecko and Creating a Local Test Network
To create your own local test network, run:
```sh
./build/ava --public-ip=127.0.0.1 --snow-sample-size=1 --snow-quorum-size=1 --staking-tls-enabled=false
```
This launches an AVA network with one node.
You should see some pretty ASCII art and log messages.
You may see a few warnings. These are OK.
You can use `Ctrl + C` to kill the node.
If you want to specify your log level. You should set `--log-level` to one of the following values, in decreasing order of logging.
* `--log-level=verbo`
* `--log-level=debug`
* `--log-level=info`
* `--log-level=warn`
* `--log-level=error`
* `--log-level=fatal`
* `--log-level=off`

27
api/admin/chain.go Normal file
View File

@ -0,0 +1,27 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package admin
import (
"net/http"
"github.com/ava-labs/gecko/ids"
)
// GetChainAliasesArgs are the arguments for Admin.GetChainAliases API call
type GetChainAliasesArgs struct{ ChainID string }
// GetChainAliasesReply are the arguments for Admin.GetChainAliases API call
type GetChainAliasesReply struct{ Aliases []string }
// GetChainAliases returns the aliases of the chain
// whose string representation is [args.ChainID]
func (service *Admin) GetChainAliases(r *http.Request, args *GetChainAliasesArgs, reply *GetChainAliasesReply) error {
ID, err := ids.FromString(args.ChainID)
if err != nil {
return err
}
reply.Aliases = service.chainManager.Aliases(ID)
return nil
}

27
api/admin/networking.go Normal file
View File

@ -0,0 +1,27 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package admin
import (
"sort"
"github.com/ava-labs/gecko/utils"
)
// Peerable can return a group of peers
type Peerable interface{ Peers() []utils.IPDesc }
// Networking provides helper methods for tracking the current network state
type Networking struct{ peers Peerable }
// Peers returns the current peers
func (n *Networking) Peers() ([]string, error) {
ipDescs := n.peers.Peers()
ips := make([]string, len(ipDescs))
for i, ipDesc := range ipDescs {
ips[i] = ipDesc.String()
}
sort.Strings(ips)
return ips, nil
}

81
api/admin/performance.go Normal file
View File

@ -0,0 +1,81 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package admin
import (
"errors"
"os"
"runtime"
"runtime/pprof"
)
var (
errCPUProfilerRunning = errors.New("cpu profiler already running")
errCPUProfilerNotRunning = errors.New("cpu profiler doesn't exist")
)
// Performance provides helper methods for measuring the current performance of
// the system
type Performance struct{ cpuProfileFile *os.File }
// StartCPUProfiler starts measuring the cpu utilization of this node
func (p *Performance) StartCPUProfiler(filename string) error {
if p.cpuProfileFile != nil {
return errCPUProfilerRunning
}
file, err := os.Create(filename)
if err != nil {
return err
}
if err := pprof.StartCPUProfile(file); err != nil {
file.Close()
return err
}
runtime.SetMutexProfileFraction(1)
p.cpuProfileFile = file
return nil
}
// StopCPUProfiler stops measuring the cpu utilization of this node
func (p *Performance) StopCPUProfiler() error {
if p.cpuProfileFile == nil {
return errCPUProfilerNotRunning
}
pprof.StopCPUProfile()
err := p.cpuProfileFile.Close()
p.cpuProfileFile = nil
return err
}
// MemoryProfile dumps the current memory utilization of this node
func (p *Performance) MemoryProfile(filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(file); err != nil {
file.Close()
return err
}
return file.Close()
}
// LockProfile dumps the current lock statistics of this node
func (p *Performance) LockProfile(filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
profile := pprof.Lookup("mutex")
if err := profile.WriteTo(file, 1); err != nil {
file.Close()
return err
}
return file.Close()
}

209
api/admin/service.go Normal file
View File

@ -0,0 +1,209 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package admin
import (
"net/http"
"github.com/gorilla/rpc/v2"
"github.com/ava-labs/gecko/api"
"github.com/ava-labs/gecko/chains"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/utils/logging"
cjson "github.com/ava-labs/gecko/utils/json"
)
// Admin is the API service for node admin management
type Admin struct {
networkID uint32
log logging.Logger
networking Networking
performance Performance
chainManager chains.Manager
httpServer *api.Server
}
// NewService returns a new admin API service
func NewService(networkID uint32, log logging.Logger, chainManager chains.Manager, peers Peerable, httpServer *api.Server) *common.HTTPHandler {
newServer := rpc.NewServer()
codec := cjson.NewCodec()
newServer.RegisterCodec(codec, "application/json")
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
newServer.RegisterService(&Admin{
networkID: networkID,
log: log,
chainManager: chainManager,
networking: Networking{
peers: peers,
},
httpServer: httpServer,
}, "admin")
return &common.HTTPHandler{Handler: newServer}
}
// GetNetworkIDArgs are the arguments for calling GetNetworkID
type GetNetworkIDArgs struct{}
// GetNetworkIDReply are the results from calling GetNetworkID
type GetNetworkIDReply struct {
NetworkID cjson.Uint32 `json:"networkID"`
}
// GetNetworkID returns the network ID this node is running on
func (service *Admin) GetNetworkID(r *http.Request, args *GetNetworkIDArgs, reply *GetNetworkIDReply) error {
service.log.Debug("Admin: GetNetworkID called")
reply.NetworkID = cjson.Uint32(service.networkID)
return nil
}
// GetBlockchainIDArgs are the arguments for calling GetBlockchainID
type GetBlockchainIDArgs struct {
Alias string `json:"alias"`
}
// GetBlockchainIDReply are the results from calling GetBlockchainID
type GetBlockchainIDReply struct {
BlockchainID string `json:"blockchainID"`
}
// GetBlockchainID returns the blockchain ID that resolves the alias that was supplied
func (service *Admin) GetBlockchainID(r *http.Request, args *GetBlockchainIDArgs, reply *GetBlockchainIDReply) error {
service.log.Debug("Admin: GetBlockchainID called")
bID, err := service.chainManager.Lookup(args.Alias)
reply.BlockchainID = bID.String()
return err
}
// PeersArgs are the arguments for calling Peers
type PeersArgs struct{}
// PeersReply are the results from calling Peers
type PeersReply struct {
Peers []string `json:"peers"`
}
// Peers returns the list of current validators
func (service *Admin) Peers(r *http.Request, args *PeersArgs, reply *PeersReply) error {
service.log.Debug("Admin: Peers called")
peers, err := service.networking.Peers()
reply.Peers = peers
return err
}
// StartCPUProfilerArgs are the arguments for calling StartCPUProfiler
type StartCPUProfilerArgs struct {
Filename string `json:"filename"`
}
// StartCPUProfilerReply are the results from calling StartCPUProfiler
type StartCPUProfilerReply struct {
Success bool `json:"success"`
}
// StartCPUProfiler starts a cpu profile writing to the specified file
func (service *Admin) StartCPUProfiler(r *http.Request, args *StartCPUProfilerArgs, reply *StartCPUProfilerReply) error {
service.log.Debug("Admin: StartCPUProfiler called with %s", args.Filename)
reply.Success = true
return service.performance.StartCPUProfiler(args.Filename)
}
// StopCPUProfilerArgs are the arguments for calling StopCPUProfiler
type StopCPUProfilerArgs struct{}
// StopCPUProfilerReply are the results from calling StopCPUProfiler
type StopCPUProfilerReply struct {
Success bool `json:"success"`
}
// StopCPUProfiler stops the cpu profile
func (service *Admin) StopCPUProfiler(r *http.Request, args *StopCPUProfilerArgs, reply *StopCPUProfilerReply) error {
service.log.Debug("Admin: StopCPUProfiler called")
reply.Success = true
return service.performance.StopCPUProfiler()
}
// MemoryProfileArgs are the arguments for calling MemoryProfile
type MemoryProfileArgs struct {
Filename string `json:"filename"`
}
// MemoryProfileReply are the results from calling MemoryProfile
type MemoryProfileReply struct {
Success bool `json:"success"`
}
// MemoryProfile runs a memory profile writing to the specified file
func (service *Admin) MemoryProfile(r *http.Request, args *MemoryProfileArgs, reply *MemoryProfileReply) error {
service.log.Debug("Admin: MemoryProfile called with %s", args.Filename)
reply.Success = true
return service.performance.MemoryProfile(args.Filename)
}
// LockProfileArgs are the arguments for calling LockProfile
type LockProfileArgs struct {
Filename string `json:"filename"`
}
// LockProfileReply are the results from calling LockProfile
type LockProfileReply struct {
Success bool `json:"success"`
}
// LockProfile runs a mutex profile writing to the specified file
func (service *Admin) LockProfile(r *http.Request, args *LockProfileArgs, reply *LockProfileReply) error {
service.log.Debug("Admin: LockProfile called with %s", args.Filename)
reply.Success = true
return service.performance.LockProfile(args.Filename)
}
// AliasArgs are the arguments for calling Alias
type AliasArgs struct {
Endpoint string `json:"endpoint"`
Alias string `json:"alias"`
}
// AliasReply are the results from calling Alias
type AliasReply struct {
Success bool `json:"success"`
}
// Alias attempts to alias an HTTP endpoint to a new name
func (service *Admin) Alias(r *http.Request, args *AliasArgs, reply *AliasReply) error {
service.log.Debug("Admin: Alias called with URL: %s, Alias: %s", args.Endpoint, args.Alias)
reply.Success = true
return service.httpServer.AddAliasesWithReadLock(args.Endpoint, args.Alias)
}
// AliasChainArgs are the arguments for calling AliasChain
type AliasChainArgs struct {
Chain string `json:"chain"`
Alias string `json:"alias"`
}
// AliasChainReply are the results from calling AliasChain
type AliasChainReply struct {
Success bool `json:"success"`
}
// AliasChain attempts to alias a chain to a new name
func (service *Admin) AliasChain(_ *http.Request, args *AliasChainArgs, reply *AliasChainReply) error {
service.log.Debug("Admin: AliasChain called with Chain: %s, Alias: %s", args.Chain, args.Alias)
chainID, err := service.chainManager.Lookup(args.Chain)
if err != nil {
return err
}
if err := service.chainManager.Alias(chainID, args.Alias); err != nil {
return err
}
reply.Success = true
return service.httpServer.AddAliasesWithReadLock("bc/"+chainID.String(), "bc/"+args.Alias)
}

33
api/ipcs/chainipc.go Normal file
View File

@ -0,0 +1,33 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ipcs
import (
"nanomsg.org/go/mangos/v2"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/logging"
)
// ChainIPC a struct which holds IPC socket information
type ChainIPC struct {
log logging.Logger
socket mangos.Socket
}
// Accept delivers a message to the ChainIPC
func (cipc *ChainIPC) Accept(chainID, containerID ids.ID, container []byte) error {
err := cipc.socket.Send(container)
if err != nil {
cipc.log.Error("%s while trying to send:\n%s", err, formatting.DumpBytes{Bytes: container})
}
return err
}
// Stop halts the ChainIPC event loop
func (cipc *ChainIPC) Stop() error {
cipc.log.Info("closing Chain IPC")
return cipc.socket.Close()
}

141
api/ipcs/server.go Normal file
View File

@ -0,0 +1,141 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ipcs
import (
"fmt"
"net/http"
"nanomsg.org/go/mangos/v2/protocol/pub"
_ "nanomsg.org/go/mangos/v2/transport/ipc" // registers the IPC transport
"github.com/gorilla/rpc/v2"
"github.com/ava-labs/gecko/api"
"github.com/ava-labs/gecko/chains"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/snow/triggers"
"github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/utils/wrappers"
)
const baseURL = "ipc:///tmp/"
// IPCs maintains the IPCs
type IPCs struct {
log logging.Logger
chainManager chains.Manager
httpServer *api.Server
events *triggers.EventDispatcher
chains map[[32]byte]*ChainIPC
}
// NewService returns a new IPCs API service
func NewService(log logging.Logger, chainManager chains.Manager, events *triggers.EventDispatcher, httpServer *api.Server) *common.HTTPHandler {
newServer := rpc.NewServer()
codec := json.NewCodec()
newServer.RegisterCodec(codec, "application/json")
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
newServer.RegisterService(&IPCs{
log: log,
chainManager: chainManager,
httpServer: httpServer,
events: events,
chains: map[[32]byte]*ChainIPC{},
}, "ipcs")
return &common.HTTPHandler{Handler: newServer}
}
// PublishBlockchainArgs are the arguments for calling PublishBlockchain
type PublishBlockchainArgs struct {
BlockchainID string `json:"blockchainID"`
}
// PublishBlockchainReply are the results from calling PublishBlockchain
type PublishBlockchainReply struct {
URL string `json:"url"`
}
// PublishBlockchain publishes the finalized accepted transactions from the blockchainID over the IPC
func (ipc *IPCs) PublishBlockchain(r *http.Request, args *PublishBlockchainArgs, reply *PublishBlockchainReply) error {
chainID, err := ipc.chainManager.Lookup(args.BlockchainID)
if err != nil {
ipc.log.Error("unknown blockchainID: %s", err)
return err
}
chainIDKey := chainID.Key()
chainIDStr := chainID.String()
url := baseURL + chainIDStr + ".ipc"
reply.URL = url
if _, ok := ipc.chains[chainIDKey]; ok {
ipc.log.Info("returning existing blockchainID %s", chainIDStr)
return nil
}
sock, err := pub.NewSocket()
if err != nil {
ipc.log.Error("can't get new pub socket: %s", err)
return err
}
if err = sock.Listen(url); err != nil {
ipc.log.Error("can't listen on pub socket: %s", err)
sock.Close()
return err
}
chainIPC := &ChainIPC{
log: ipc.log,
socket: sock,
}
if err := ipc.events.RegisterChain(chainID, "ipc", chainIPC); err != nil {
ipc.log.Error("couldn't register event: %s", err)
sock.Close()
return err
}
ipc.chains[chainIDKey] = chainIPC
return nil
}
// UnpublishBlockchainArgs are the arguments for calling UnpublishBlockchain
type UnpublishBlockchainArgs struct {
BlockchainID string `json:"blockchainID"`
}
// UnpublishBlockchainReply are the results from calling UnpublishBlockchain
type UnpublishBlockchainReply struct {
Success bool `json:"success"`
}
// UnpublishBlockchain closes publishing of a blockchainID
func (ipc *IPCs) UnpublishBlockchain(r *http.Request, args *UnpublishBlockchainArgs, reply *UnpublishBlockchainReply) error {
chainID, err := ipc.chainManager.Lookup(args.BlockchainID)
if err != nil {
ipc.log.Error("unknown blockchainID %s: %s", args.BlockchainID, err)
return err
}
chainIDKey := chainID.Key()
chain, ok := ipc.chains[chainIDKey]
if !ok {
return fmt.Errorf("blockchainID not publishing: %s", chainID)
}
errs := wrappers.Errs{}
errs.Add(
chain.Stop(),
ipc.events.DeregisterChain(chainID, "ipc"),
)
delete(ipc.chains, chainIDKey)
reply.Success = true
return errs.Err
}

View File

@ -0,0 +1,20 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package keystore
import (
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids"
)
// BlockchainKeystore ...
type BlockchainKeystore struct {
blockchainID ids.ID
ks *Keystore
}
// GetDatabase ...
func (bks *BlockchainKeystore) GetDatabase(username, password string) (database.Database, error) {
return bks.ks.GetDatabase(bks.blockchainID, username, password)
}

308
api/keystore/service.go Normal file
View File

@ -0,0 +1,308 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package keystore
import (
"errors"
"fmt"
"net/http"
"sync"
"github.com/gorilla/rpc/v2"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/encdb"
"github.com/ava-labs/gecko/database/prefixdb"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/vms/components/codec"
jsoncodec "github.com/ava-labs/gecko/utils/json"
)
var (
errEmptyUsername = errors.New("username can't be the empty string")
)
// KeyValuePair ...
type KeyValuePair struct {
Key []byte `serialize:"true"`
Value []byte `serialize:"true"`
}
// UserDB describes the full content of a user
type UserDB struct {
User `serialize:"true"`
Data []KeyValuePair `serialize:"true"`
}
// Keystore is the RPC interface for keystore management
type Keystore struct {
lock sync.Mutex
log logging.Logger
codec codec.Codec
// Key: username
// Value: The user with that name
users map[string]*User
// Used to persist users and their data
userDB database.Database
bcDB database.Database
// BaseDB
// / \
// UserDB BlockchainDB
// / | \
// Usr Usr Usr
// / | \
// BID BID BID
}
// Initialize the keystore
func (ks *Keystore) Initialize(log logging.Logger, db database.Database) {
ks.log = log
ks.codec = codec.NewDefault()
ks.users = make(map[string]*User)
ks.userDB = prefixdb.New([]byte("users"), db)
ks.bcDB = prefixdb.New([]byte("bcs"), db)
}
// CreateHandler returns a new service object that can send requests to thisAPI.
func (ks *Keystore) CreateHandler() *common.HTTPHandler {
newServer := rpc.NewServer()
codec := jsoncodec.NewCodec()
newServer.RegisterCodec(codec, "application/json")
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
newServer.RegisterService(ks, "keystore")
return &common.HTTPHandler{LockOptions: common.NoLock, Handler: newServer}
}
// Get the user whose name is [username]
func (ks *Keystore) getUser(username string) (*User, error) {
// If the user is already in memory, return it
usr, exists := ks.users[username]
if exists {
return usr, nil
}
// The user is not in memory; try the database
usrBytes, err := ks.userDB.Get([]byte(username))
if err != nil { // Most likely bc user doesn't exist in database
return nil, err
}
usr = &User{}
return usr, ks.codec.Unmarshal(usrBytes, usr)
}
// CreateUserArgs are arguments for passing into CreateUser requests
type CreateUserArgs struct {
Username string `json:"username"`
Password string `json:"password"`
}
// CreateUserReply is the response from calling CreateUser
type CreateUserReply struct {
Success bool `json:"success"`
}
// CreateUser creates an empty user with the provided username and password
func (ks *Keystore) CreateUser(_ *http.Request, args *CreateUserArgs, reply *CreateUserReply) error {
ks.lock.Lock()
defer ks.lock.Unlock()
ks.log.Verbo("CreateUser called with %s", args.Username)
if args.Username == "" {
return errEmptyUsername
}
if usr, err := ks.getUser(args.Username); err == nil || usr != nil {
return fmt.Errorf("user already exists: %s", args.Username)
}
usr := &User{}
if err := usr.Initialize(args.Password); err != nil {
return err
}
usrBytes, err := ks.codec.Marshal(usr)
if err != nil {
return err
}
if err := ks.userDB.Put([]byte(args.Username), usrBytes); err != nil {
return err
}
ks.users[args.Username] = usr
reply.Success = true
return nil
}
// ListUsersArgs are the arguments to ListUsers
type ListUsersArgs struct{}
// ListUsersReply is the reply from ListUsers
type ListUsersReply struct {
Users []string `json:"users"`
}
// ListUsers lists all the registered usernames
func (ks *Keystore) ListUsers(_ *http.Request, args *ListUsersArgs, reply *ListUsersReply) error {
ks.lock.Lock()
defer ks.lock.Unlock()
ks.log.Verbo("ListUsers called")
reply.Users = []string{}
it := ks.userDB.NewIterator()
defer it.Release()
for it.Next() {
reply.Users = append(reply.Users, string(it.Key()))
}
return it.Error()
}
// ExportUserArgs are the arguments to ExportUser
type ExportUserArgs struct {
Username string `json:"username"`
Password string `json:"password"`
}
// ExportUserReply is the reply from ExportUser
type ExportUserReply struct {
User string `json:"user"`
}
// ExportUser exports a serialized encoding of a user's information complete with encrypted database values
func (ks *Keystore) ExportUser(_ *http.Request, args *ExportUserArgs, reply *ExportUserReply) error {
ks.lock.Lock()
defer ks.lock.Unlock()
ks.log.Verbo("ExportUser called for %s", args.Username)
usr, err := ks.getUser(args.Username)
if err != nil {
return err
}
if !usr.CheckPassword(args.Password) {
return fmt.Errorf("incorrect password for %s", args.Username)
}
userDB := prefixdb.New([]byte(args.Username), ks.bcDB)
userData := UserDB{
User: *usr,
}
it := userDB.NewIterator()
defer it.Release()
for it.Next() {
userData.Data = append(userData.Data, KeyValuePair{
Key: it.Key(),
Value: it.Value(),
})
}
if err := it.Error(); err != nil {
return err
}
b, err := ks.codec.Marshal(&userData)
if err != nil {
return err
}
cb58 := formatting.CB58{Bytes: b}
reply.User = cb58.String()
return nil
}
// ImportUserArgs are arguments for ImportUser
type ImportUserArgs struct {
Username string `json:"username"`
Password string `json:"password"`
User string `json:"user"`
}
// ImportUserReply is the response for ImportUser
type ImportUserReply struct {
Success bool `json:"success"`
}
// ImportUser imports a serialized encoding of a user's information complete with encrypted database values, integrity checks the password, and adds it to the database
func (ks *Keystore) ImportUser(r *http.Request, args *ImportUserArgs, reply *ImportUserReply) error {
ks.lock.Lock()
defer ks.lock.Unlock()
ks.log.Verbo("ImportUser called for %s", args.Username)
if usr, err := ks.getUser(args.Username); err == nil || usr != nil {
return fmt.Errorf("user already exists: %s", args.Username)
}
cb58 := formatting.CB58{}
if err := cb58.FromString(args.User); err != nil {
return err
}
userData := UserDB{}
if err := ks.codec.Unmarshal(cb58.Bytes, &userData); err != nil {
return err
}
usrBytes, err := ks.codec.Marshal(&userData.User)
if err != nil {
return err
}
// TODO: Should add batching to prevent creating a user without importing
// the account
if err := ks.userDB.Put([]byte(args.Username), usrBytes); err != nil {
return err
}
ks.users[args.Username] = &userData.User
userDB := prefixdb.New([]byte(args.Username), ks.bcDB)
batch := userDB.NewBatch()
for _, kvp := range userData.Data {
batch.Put(kvp.Key, kvp.Value)
}
reply.Success = true
return batch.Write()
}
// NewBlockchainKeyStore ...
func (ks *Keystore) NewBlockchainKeyStore(blockchainID ids.ID) *BlockchainKeystore {
return &BlockchainKeystore{
blockchainID: blockchainID,
ks: ks,
}
}
// GetDatabase ...
func (ks *Keystore) GetDatabase(bID ids.ID, username, password string) (database.Database, error) {
ks.lock.Lock()
defer ks.lock.Unlock()
usr, err := ks.getUser(username)
if err != nil {
return nil, err
}
if !usr.CheckPassword(password) {
return nil, fmt.Errorf("incorrect password for user '%s'", username)
}
userDB := prefixdb.New([]byte(username), ks.bcDB)
bcDB := prefixdb.NewNested(bID.Bytes(), userDB)
encDB, err := encdb.New([]byte(password), bcDB)
if err != nil {
return nil, err
}
return encDB, nil
}

View File

@ -0,0 +1,202 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package keystore
import (
"bytes"
"testing"
"github.com/ava-labs/gecko/database/memdb"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/logging"
)
func TestServiceListNoUsers(t *testing.T) {
ks := Keystore{}
ks.Initialize(logging.NoLog{}, memdb.New())
reply := ListUsersReply{}
if err := ks.ListUsers(nil, &ListUsersArgs{}, &reply); err != nil {
t.Fatal(err)
}
if len(reply.Users) != 0 {
t.Fatalf("No users should have been created yet")
}
}
func TestServiceCreateUser(t *testing.T) {
ks := Keystore{}
ks.Initialize(logging.NoLog{}, memdb.New())
{
reply := CreateUserReply{}
if err := ks.CreateUser(nil, &CreateUserArgs{
Username: "bob",
Password: "launch",
}, &reply); err != nil {
t.Fatal(err)
}
if !reply.Success {
t.Fatalf("User should have been created successfully")
}
}
{
reply := ListUsersReply{}
if err := ks.ListUsers(nil, &ListUsersArgs{}, &reply); err != nil {
t.Fatal(err)
}
if len(reply.Users) != 1 {
t.Fatalf("One user should have been created")
}
if user := reply.Users[0]; user != "bob" {
t.Fatalf("'bob' should have been a user that was created")
}
}
}
func TestServiceCreateDuplicate(t *testing.T) {
ks := Keystore{}
ks.Initialize(logging.NoLog{}, memdb.New())
{
reply := CreateUserReply{}
if err := ks.CreateUser(nil, &CreateUserArgs{
Username: "bob",
Password: "launch",
}, &reply); err != nil {
t.Fatal(err)
}
if !reply.Success {
t.Fatalf("User should have been created successfully")
}
}
{
reply := CreateUserReply{}
if err := ks.CreateUser(nil, &CreateUserArgs{
Username: "bob",
Password: "launch!",
}, &reply); err == nil {
t.Fatalf("Should have errored due to the username already existing")
}
}
}
func TestServiceCreateUserNoName(t *testing.T) {
ks := Keystore{}
ks.Initialize(logging.NoLog{}, memdb.New())
reply := CreateUserReply{}
if err := ks.CreateUser(nil, &CreateUserArgs{
Password: "launch",
}, &reply); err == nil {
t.Fatalf("Shouldn't have allowed empty username")
}
}
func TestServiceUseBlockchainDB(t *testing.T) {
ks := Keystore{}
ks.Initialize(logging.NoLog{}, memdb.New())
{
reply := CreateUserReply{}
if err := ks.CreateUser(nil, &CreateUserArgs{
Username: "bob",
Password: "launch",
}, &reply); err != nil {
t.Fatal(err)
}
if !reply.Success {
t.Fatalf("User should have been created successfully")
}
}
{
db, err := ks.GetDatabase(ids.Empty, "bob", "launch")
if err != nil {
t.Fatal(err)
}
if err := db.Put([]byte("hello"), []byte("world")); err != nil {
t.Fatal(err)
}
}
{
db, err := ks.GetDatabase(ids.Empty, "bob", "launch")
if err != nil {
t.Fatal(err)
}
if val, err := db.Get([]byte("hello")); err != nil {
t.Fatal(err)
} else if !bytes.Equal(val, []byte("world")) {
t.Fatalf("Should have read '%s' from the db", "world")
}
}
}
func TestServiceExportImport(t *testing.T) {
ks := Keystore{}
ks.Initialize(logging.NoLog{}, memdb.New())
{
reply := CreateUserReply{}
if err := ks.CreateUser(nil, &CreateUserArgs{
Username: "bob",
Password: "launch",
}, &reply); err != nil {
t.Fatal(err)
}
if !reply.Success {
t.Fatalf("User should have been created successfully")
}
}
{
db, err := ks.GetDatabase(ids.Empty, "bob", "launch")
if err != nil {
t.Fatal(err)
}
if err := db.Put([]byte("hello"), []byte("world")); err != nil {
t.Fatal(err)
}
}
exportReply := ExportUserReply{}
if err := ks.ExportUser(nil, &ExportUserArgs{
Username: "bob",
Password: "launch",
}, &exportReply); err != nil {
t.Fatal(err)
}
newKS := Keystore{}
newKS.Initialize(logging.NoLog{}, memdb.New())
{
reply := ImportUserReply{}
if err := newKS.ImportUser(nil, &ImportUserArgs{
Username: "bob",
Password: "launch",
User: exportReply.User,
}, &reply); err != nil {
t.Fatal(err)
}
if !reply.Success {
t.Fatalf("User should have been imported successfully")
}
}
{
db, err := newKS.GetDatabase(ids.Empty, "bob", "launch")
if err != nil {
t.Fatal(err)
}
if val, err := db.Get([]byte("hello")); err != nil {
t.Fatal(err)
} else if !bytes.Equal(val, []byte("world")) {
t.Fatalf("Should have read '%s' from the db", "world")
}
}
}

35
api/keystore/user.go Normal file
View File

@ -0,0 +1,35 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package keystore
import (
"bytes"
"crypto/rand"
"golang.org/x/crypto/argon2"
)
// User describes a user of the keystore
type User struct {
Password [32]byte `serialize:"true"` // The salted, hashed password
Salt [16]byte `serialize:"true"` // The salt
}
// Initialize ...
func (usr *User) Initialize(password string) error {
_, err := rand.Read(usr.Salt[:])
if err != nil {
return err
}
// pw is the salted, hashed password
pw := argon2.IDKey([]byte(password), usr.Salt[:], 1, 64*1024, 4, 32)
copy(usr.Password[:], pw[:32])
return nil
}
// CheckPassword ...
func (usr *User) CheckPassword(password string) bool {
pw := argon2.IDKey([]byte(password), usr.Salt[:], 1, 64*1024, 4, 32)
return bytes.Equal(pw, usr.Password[:])
}

24
api/keystore/user_test.go Normal file
View File

@ -0,0 +1,24 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package keystore
import (
"testing"
)
func TestUser(t *testing.T) {
usr := User{}
if err := usr.Initialize("heytherepal"); err != nil {
t.Fatal(err)
}
if !usr.CheckPassword("heytherepal") {
t.Fatalf("Should have verified the password")
}
if usr.CheckPassword("heytherepal!") {
t.Fatalf("Shouldn't have verified the password")
}
if usr.CheckPassword("") {
t.Fatalf("Shouldn't have verified the password")
}
}

23
api/metrics/service.go Normal file
View File

@ -0,0 +1,23 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package metrics
import (
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// NewService returns a new prometheus service
func NewService() (*prometheus.Registry, *common.HTTPHandler) {
registerer := prometheus.NewRegistry()
handler := promhttp.InstrumentMetricHandler(
registerer,
promhttp.HandlerFor(
registerer,
promhttp.HandlerOpts{},
),
)
return registerer, &common.HTTPHandler{LockOptions: common.NoLock, Handler: handler}
}

23
api/middleware_handler.go Normal file
View File

@ -0,0 +1,23 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package api
import (
"net/http"
)
type middlewareHandler struct {
before, after func()
handler http.Handler
}
func (mh middlewareHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if mh.before != nil {
mh.before()
}
if mh.after != nil {
defer mh.after()
}
mh.handler.ServeHTTP(writer, request)
}

132
api/router.go Normal file
View File

@ -0,0 +1,132 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package api
import (
"errors"
"fmt"
"net/http"
"sync"
"github.com/gorilla/mux"
)
var (
errUnknownBaseURL = errors.New("unknown base url")
errUnknownEndpoint = errors.New("unknown endpoint")
)
type router struct {
lock sync.RWMutex
router *mux.Router
routeLock sync.Mutex
reservedRoutes map[string]bool // Reserves routes so that there can't be alias that conflict
aliases map[string][]string // Maps a route to a set of reserved routes
routes map[string]map[string]http.Handler // Maps routes to a handler
}
func newRouter() *router {
return &router{
router: mux.NewRouter(),
reservedRoutes: make(map[string]bool),
aliases: make(map[string][]string),
routes: make(map[string]map[string]http.Handler),
}
}
func (r *router) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
r.lock.RLock()
defer r.lock.RUnlock()
r.router.ServeHTTP(writer, request)
}
func (r *router) GetHandler(base, endpoint string) (http.Handler, error) {
r.routeLock.Lock()
defer r.routeLock.Unlock()
urlBase, exists := r.routes[base]
if !exists {
return nil, errUnknownBaseURL
}
handler, exists := urlBase[endpoint]
if !exists {
return nil, errUnknownEndpoint
}
return handler, nil
}
func (r *router) AddRouter(base, endpoint string, handler http.Handler) error {
r.lock.Lock()
defer r.lock.Unlock()
r.routeLock.Lock()
defer r.routeLock.Unlock()
return r.addRouter(base, endpoint, handler)
}
func (r *router) addRouter(base, endpoint string, handler http.Handler) error {
if r.reservedRoutes[base] {
return fmt.Errorf("couldn't route to %s as that route is either aliased or already maps to a handler", base)
}
return r.forceAddRouter(base, endpoint, handler)
}
func (r *router) forceAddRouter(base, endpoint string, handler http.Handler) error {
endpoints := r.routes[base]
if endpoints == nil {
endpoints = make(map[string]http.Handler)
}
url := base + endpoint
if _, exists := endpoints[endpoint]; exists {
return fmt.Errorf("failed to create endpoint as %s already exists", url)
}
endpoints[endpoint] = handler
r.routes[base] = endpoints
r.router.Handle(url, handler)
var err error
if aliases, exists := r.aliases[base]; exists {
for _, alias := range aliases {
if innerErr := r.forceAddRouter(alias, endpoint, handler); err == nil {
err = innerErr
}
}
}
return err
}
func (r *router) AddAlias(base string, aliases ...string) error {
r.lock.Lock()
defer r.lock.Unlock()
r.routeLock.Lock()
defer r.routeLock.Unlock()
for _, alias := range aliases {
if r.reservedRoutes[alias] {
return fmt.Errorf("couldn't alias to %s as that route is either already aliased or already maps to a handler", alias)
}
}
for _, alias := range aliases {
r.reservedRoutes[alias] = true
}
r.aliases[base] = append(r.aliases[base], aliases...)
var err error
if endpoints, exists := r.routes[base]; exists {
for endpoint, handler := range endpoints {
for _, alias := range aliases {
if innerErr := r.forceAddRouter(alias, endpoint, handler); err == nil {
err = innerErr
}
}
}
}
return err
}

69
api/router_test.go Normal file
View File

@ -0,0 +1,69 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package api
import (
"net/http"
"testing"
)
type testHandler struct{}
func (*testHandler) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {}
func TestAliasing(t *testing.T) {
r := newRouter()
if err := r.AddAlias("1", "2", "3"); err != nil {
t.Fatal(err)
}
if err := r.AddAlias("1", "4"); err != nil {
t.Fatal(err)
}
if err := r.AddAlias("5", "1"); err != nil {
t.Fatal(err)
}
if err := r.AddAlias("3", "6"); err != nil {
t.Fatal(err)
}
if err := r.AddAlias("7", "4"); err == nil {
t.Fatalf("Already reserved %s", "4")
}
handler1 := &testHandler{}
if err := r.AddRouter("2", "", handler1); err == nil {
t.Fatalf("Already reserved %s", "2")
}
if err := r.AddRouter("5", "", handler1); err != nil {
t.Fatal(err)
}
if handler, exists := r.routes["5"][""]; !exists {
t.Fatalf("Should have added %s", "5")
} else if handler != handler1 {
t.Fatalf("Registered unknown handler")
}
if err := r.AddAlias("5", "7"); err != nil {
t.Fatal(err)
}
if handler, exists := r.routes["7"][""]; !exists {
t.Fatalf("Should have added %s", "7")
} else if handler != handler1 {
t.Fatalf("Registered unknown handler")
}
}
func TestBlock(t *testing.T) {
r := newRouter()
if err := r.AddAlias("1", "1"); err != nil {
t.Fatal(err)
}
handler1 := &testHandler{}
if err := r.AddRouter("1", "", handler1); err == nil {
t.Fatalf("Permanently locked %s", "1")
}
}

165
api/server.go Normal file
View File

@ -0,0 +1,165 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package api
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"sync"
"github.com/gorilla/handlers"
"github.com/rs/cors"
"github.com/ava-labs/gecko/snow"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/utils/logging"
)
const baseURL = "/ext"
var (
errUnknownLockOption = errors.New("invalid lock options")
)
// Server maintains the HTTP router
type Server struct {
log logging.Logger
factory logging.Factory
router *router
portURL string
}
// Initialize creates the API server at the provided port
func (s *Server) Initialize(log logging.Logger, factory logging.Factory, port uint16) {
s.log = log
s.factory = factory
s.portURL = fmt.Sprintf(":%d", port)
s.router = newRouter()
}
// Dispatch starts the API server
func (s *Server) Dispatch() error {
handler := cors.Default().Handler(s.router)
return http.ListenAndServe(s.portURL, handler)
}
// DispatchTLS starts the API server with the provided TLS certificate
func (s *Server) DispatchTLS(certFile, keyFile string) error {
handler := cors.Default().Handler(s.router)
return http.ListenAndServeTLS(s.portURL, certFile, keyFile, handler)
}
// RegisterChain registers the API endpoints associated with this chain That
// is, add <route, handler> pairs to server so that http calls can be made to
// the vm
func (s *Server) RegisterChain(ctx *snow.Context, vmIntf interface{}) {
vm, ok := vmIntf.(common.VM)
if !ok {
return
}
// all subroutes to a chain begin with "bc/<the chain's ID>"
defaultEndpoint := "bc/" + ctx.ChainID.String()
httpLogger, err := s.factory.MakeChain(ctx.ChainID, "http")
if err != nil {
s.log.Error("Failed to create new http logger: %s", err)
return
}
s.log.Verbo("About to add API endpoints for chain with ID %s", ctx.ChainID)
// Register each endpoint
for extension, service := range vm.CreateHandlers() {
// Validate that the route being added is valid
// e.g. "/foo" and "" are ok but "\n" is not
_, err := url.ParseRequestURI(extension)
if extension != "" && err != nil {
s.log.Warn("could not add route to chain's API handler because route is malformed: %s", extension)
continue
}
s.log.Verbo("adding API endpoint: %s", defaultEndpoint+extension)
if err := s.AddRoute(service, &ctx.Lock, defaultEndpoint, extension, httpLogger); err != nil {
s.log.Error("error adding route: %s", err)
}
}
}
// AddRoute registers the appropriate endpoint for the vm given an endpoint
func (s *Server) AddRoute(handler *common.HTTPHandler, lock *sync.RWMutex, base, endpoint string, log logging.Logger) error {
url := fmt.Sprintf("%s/%s", baseURL, base)
s.log.Info("adding route %s%s", url, endpoint)
h := handlers.CombinedLoggingHandler(log, handler.Handler)
switch handler.LockOptions {
case common.WriteLock:
return s.router.AddRouter(url, endpoint, middlewareHandler{
before: lock.Lock,
after: lock.Unlock,
handler: h,
})
case common.ReadLock:
return s.router.AddRouter(url, endpoint, middlewareHandler{
before: lock.RLock,
after: lock.RUnlock,
handler: h,
})
case common.NoLock:
return s.router.AddRouter(url, endpoint, h)
default:
return errUnknownLockOption
}
}
// AddAliases registers aliases to the server
func (s *Server) AddAliases(endpoint string, aliases ...string) error {
url := fmt.Sprintf("%s/%s", baseURL, endpoint)
endpoints := make([]string, len(aliases))
for i, alias := range aliases {
endpoints[i] = fmt.Sprintf("%s/%s", baseURL, alias)
}
return s.router.AddAlias(url, endpoints...)
}
// AddAliasesWithReadLock registers aliases to the server assuming the http read
// lock is currently held.
func (s *Server) AddAliasesWithReadLock(endpoint string, aliases ...string) error {
// This is safe, as the read lock doesn't actually need to be held once the
// http handler is called. However, it is unlocked later, so this function
// must end with the lock held.
s.router.lock.RUnlock()
defer s.router.lock.RLock()
return s.AddAliases(endpoint, aliases...)
}
// Call ...
func (s *Server) Call(
writer http.ResponseWriter,
method,
base,
endpoint string,
body io.Reader,
headers map[string]string,
) error {
url := fmt.Sprintf("%s/vm/%s", baseURL, base)
handler, err := s.router.GetHandler(url, endpoint)
if err != nil {
return err
}
req, err := http.NewRequest("POST", "*", body)
if err != nil {
return err
}
for key, value := range headers {
req.Header.Set(key, value)
}
handler.ServeHTTP(writer, req)
return nil
}

60
api/server_test.go Normal file
View File

@ -0,0 +1,60 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package api
import (
"bytes"
"net/http"
"net/http/httptest"
"sync"
"testing"
"github.com/gorilla/rpc/v2"
"github.com/gorilla/rpc/v2/json2"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/utils/logging"
)
type Service struct{ called bool }
type Args struct{}
type Reply struct{}
func (s *Service) Call(_ *http.Request, args *Args, reply *Reply) error {
s.called = true
return nil
}
func TestCall(t *testing.T) {
s := Server{}
s.Initialize(logging.NoLog{}, logging.NoFactory{}, 8080)
serv := &Service{}
newServer := rpc.NewServer()
newServer.RegisterCodec(json2.NewCodec(), "application/json")
newServer.RegisterCodec(json2.NewCodec(), "application/json;charset=UTF-8")
newServer.RegisterService(serv, "test")
if err := s.AddRoute(&common.HTTPHandler{Handler: newServer}, new(sync.RWMutex), "vm/lol", "", logging.NoLog{}); err != nil {
t.Fatal(err)
}
buf, err := json2.EncodeClientRequest("test.Call", &Args{})
if err != nil {
t.Fatal(err)
}
writer := httptest.NewRecorder()
body := bytes.NewBuffer(buf)
headers := map[string]string{
"Content-Type": "application/json",
}
s.Call(writer, "POST", "lol", "", body, headers)
if !serv.called {
t.Fatalf("Should have been called")
}
}

41
cache/cache.go vendored Normal file
View File

@ -0,0 +1,41 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package cache
import (
"github.com/ava-labs/gecko/ids"
)
// Cacher acts as a best effort key value store
type Cacher interface {
// Put inserts an element into the cache. If spaced is required, elements will
// be evicted.
Put(key ids.ID, value interface{})
// Get returns the entry in the cache with the key specified, if no value
// exists, false is returned.
Get(key ids.ID) (interface{}, bool)
// Evict removes the specified entry from the cache
Evict(key ids.ID)
// Flush removes all entries from the cache
Flush()
}
// Evictable allows the object to be notified when it is evicted
type Evictable interface {
ID() ids.ID
Evict()
}
// Deduplicator acts as a best effort deduplication service
type Deduplicator interface {
// Deduplicate returns either the provided value, or a previously provided
// value with the same ID that hasn't yet been evicted
Deduplicate(Evictable) Evictable
// Flush removes all entries from the cache
Flush()
}

139
cache/lru_cache.go vendored Normal file
View File

@ -0,0 +1,139 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package cache
import (
"container/list"
"sync"
"github.com/ava-labs/gecko/ids"
)
type entry struct {
Key ids.ID
Value interface{}
}
// LRU is a key value store with bounded size. If the size is attempted to be
// exceeded, then an element is removed from the cache before the insertion is
// done, based on evicting the least recently used value.
type LRU struct {
lock sync.Mutex
entryMap map[[32]byte]*list.Element
entryList *list.List
Size int
}
// Put implements the cache interface
func (c *LRU) Put(key ids.ID, value interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
c.put(key, value)
}
// Get implements the cache interface
func (c *LRU) Get(key ids.ID) (interface{}, bool) {
c.lock.Lock()
defer c.lock.Unlock()
return c.get(key)
}
// Evict implements the cache interface
func (c *LRU) Evict(key ids.ID) {
c.lock.Lock()
defer c.lock.Unlock()
c.evict(key)
}
// Flush implements the cache interface
func (c *LRU) Flush() {
c.lock.Lock()
defer c.lock.Unlock()
c.flush()
}
func (c *LRU) init() {
if c.entryMap == nil {
c.entryMap = make(map[[32]byte]*list.Element)
}
if c.entryList == nil {
c.entryList = list.New()
}
if c.Size <= 0 {
c.Size = 1
}
}
func (c *LRU) resize() {
for c.entryList.Len() > c.Size {
e := c.entryList.Front()
c.entryList.Remove(e)
val := e.Value.(*entry)
delete(c.entryMap, val.Key.Key())
}
}
func (c *LRU) put(key ids.ID, value interface{}) {
c.init()
c.resize()
if e, ok := c.entryMap[key.Key()]; !ok {
if c.entryList.Len() >= c.Size {
e = c.entryList.Front()
c.entryList.MoveToBack(e)
val := e.Value.(*entry)
delete(c.entryMap, val.Key.Key())
val.Key = key
val.Value = value
} else {
e = c.entryList.PushBack(&entry{
Key: key,
Value: value,
})
}
c.entryMap[key.Key()] = e
} else {
c.entryList.MoveToBack(e)
val := e.Value.(*entry)
val.Value = value
}
}
func (c *LRU) get(key ids.ID) (interface{}, bool) {
c.init()
c.resize()
if e, ok := c.entryMap[key.Key()]; ok {
c.entryList.MoveToBack(e)
val := e.Value.(*entry)
return val.Value, true
}
return struct{}{}, false
}
func (c *LRU) evict(key ids.ID) {
c.init()
c.resize()
keyBytes := key.Key()
if e, ok := c.entryMap[keyBytes]; ok {
c.entryList.Remove(e)
delete(c.entryMap, keyBytes)
}
}
func (c *LRU) flush() {
c.init()
c.entryMap = make(map[[32]byte]*list.Element)
c.entryList = list.New()
}

171
cache/lru_cache_test.go vendored Normal file
View File

@ -0,0 +1,171 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package cache
import (
"testing"
"github.com/ava-labs/gecko/ids"
)
func TestLRU(t *testing.T) {
cache := LRU{Size: 1}
id1 := ids.NewID([32]byte{1})
if _, found := cache.Get(id1); found {
t.Fatalf("Retrieved value when none exists")
}
expectedValue1 := 1
cache.Put(id1, expectedValue1)
if value, found := cache.Get(id1); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if value != expectedValue1 {
t.Fatalf("Failed to retrieve correct value when one exists")
}
cache.Put(id1, expectedValue1)
if value, found := cache.Get(id1); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if value != expectedValue1 {
t.Fatalf("Failed to retrieve correct value when one exists")
}
cache.Put(id1, expectedValue1)
if value, found := cache.Get(id1); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if value != expectedValue1 {
t.Fatalf("Failed to retrieve correct value when one exists")
}
id2 := ids.NewID([32]byte{2})
expectedValue2 := 2
cache.Put(id2, expectedValue2)
if _, found := cache.Get(id1); found {
t.Fatalf("Retrieved value when none exists")
}
if value, found := cache.Get(id2); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if value != expectedValue2 {
t.Fatalf("Failed to retrieve correct value when one exists")
}
}
func TestLRUEviction(t *testing.T) {
cache := LRU{Size: 2}
id1 := ids.NewID([32]byte{1})
id2 := ids.NewID([32]byte{2})
id3 := ids.NewID([32]byte{3})
cache.Put(id1, 1)
cache.Put(id2, 2)
if val, found := cache.Get(id1); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 1 {
t.Fatalf("Retrieved wrong value")
} else if val, found := cache.Get(id2); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 2 {
t.Fatalf("Retrieved wrong value")
} else if _, found := cache.Get(id3); found {
t.Fatalf("Retrieve value when none exists")
}
cache.Put(id3, 3)
if _, found := cache.Get(id1); found {
t.Fatalf("Retrieve value when none exists")
} else if val, found := cache.Get(id2); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 2 {
t.Fatalf("Retrieved wrong value")
} else if val, found := cache.Get(id3); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 3 {
t.Fatalf("Retrieved wrong value")
}
cache.Get(id2)
cache.Put(id1, 1)
if val, found := cache.Get(id1); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 1 {
t.Fatalf("Retrieved wrong value")
} else if val, found := cache.Get(id2); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 2 {
t.Fatalf("Retrieved wrong value")
} else if _, found := cache.Get(id3); found {
t.Fatalf("Retrieved value when none exists")
}
cache.Evict(id2)
cache.Put(id3, 3)
if val, found := cache.Get(id1); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 1 {
t.Fatalf("Retrieved wrong value")
} else if _, found := cache.Get(id2); found {
t.Fatalf("Retrieved value when none exists")
} else if val, found := cache.Get(id3); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 3 {
t.Fatalf("Retrieved wrong value")
}
cache.Flush()
if _, found := cache.Get(id1); found {
t.Fatalf("Retrieved value when none exists")
} else if _, found := cache.Get(id2); found {
t.Fatalf("Retrieved value when none exists")
} else if _, found := cache.Get(id3); found {
t.Fatalf("Retrieved value when none exists")
}
}
func TestLRUResize(t *testing.T) {
cache := LRU{Size: 2}
id1 := ids.NewID([32]byte{1})
id2 := ids.NewID([32]byte{2})
cache.Put(id1, 1)
cache.Put(id2, 2)
if val, found := cache.Get(id1); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 1 {
t.Fatalf("Retrieved wrong value")
} else if val, found := cache.Get(id2); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 2 {
t.Fatalf("Retrieved wrong value")
}
cache.Size = 1
if _, found := cache.Get(id1); found {
t.Fatalf("Retrieve value when none exists")
} else if val, found := cache.Get(id2); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 2 {
t.Fatalf("Retrieved wrong value")
}
cache.Size = 0
if _, found := cache.Get(id1); found {
t.Fatalf("Retrieve value when none exists")
} else if val, found := cache.Get(id2); !found {
t.Fatalf("Failed to retrieve value when one exists")
} else if val != 2 {
t.Fatalf("Retrieved wrong value")
}
}

93
cache/unique_cache.go vendored Normal file
View File

@ -0,0 +1,93 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package cache
import (
"container/list"
"sync"
)
// EvictableLRU is an LRU cache that notifies the objects when they are evicted.
type EvictableLRU struct {
lock sync.Mutex
entryMap map[[32]byte]*list.Element
entryList *list.List
Size int
}
// Deduplicate implements the Deduplicator interface
func (c *EvictableLRU) Deduplicate(value Evictable) Evictable {
c.lock.Lock()
defer c.lock.Unlock()
return c.deduplicate(value)
}
// Flush implements the Deduplicator interface
func (c *EvictableLRU) Flush() {
c.lock.Lock()
defer c.lock.Unlock()
c.flush()
}
func (c *EvictableLRU) init() {
if c.entryMap == nil {
c.entryMap = make(map[[32]byte]*list.Element)
}
if c.entryList == nil {
c.entryList = list.New()
}
if c.Size <= 0 {
c.Size = 1
}
}
func (c *EvictableLRU) resize() {
for c.entryList.Len() > c.Size {
e := c.entryList.Front()
c.entryList.Remove(e)
val := e.Value.(Evictable)
delete(c.entryMap, val.ID().Key())
val.Evict()
}
}
func (c *EvictableLRU) deduplicate(value Evictable) Evictable {
c.init()
c.resize()
key := value.ID().Key()
if e, ok := c.entryMap[key]; !ok {
if c.entryList.Len() >= c.Size {
e = c.entryList.Front()
c.entryList.MoveToBack(e)
val := e.Value.(Evictable)
delete(c.entryMap, val.ID().Key())
val.Evict()
e.Value = value
} else {
e = c.entryList.PushBack(value)
}
c.entryMap[key] = e
} else {
c.entryList.MoveToBack(e)
val := e.Value.(Evictable)
value = val
}
return value
}
func (c *EvictableLRU) flush() {
c.init()
size := c.Size
c.Size = 0
c.resize()
c.Size = size
}

62
cache/unique_cache_test.go vendored Normal file
View File

@ -0,0 +1,62 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package cache
import (
"testing"
"github.com/ava-labs/gecko/ids"
)
type evictable struct {
id ids.ID
evicted int
}
func (e *evictable) ID() ids.ID { return e.id }
func (e *evictable) Evict() { e.evicted++ }
func TestEvictableLRU(t *testing.T) {
cache := EvictableLRU{}
expectedValue1 := &evictable{id: ids.NewID([32]byte{1})}
if returnedValue := cache.Deduplicate(expectedValue1).(*evictable); returnedValue != expectedValue1 {
t.Fatalf("Returned unknown value")
} else if expectedValue1.evicted != 0 {
t.Fatalf("Value was evicted unexpectedly")
} else if returnedValue := cache.Deduplicate(expectedValue1).(*evictable); returnedValue != expectedValue1 {
t.Fatalf("Returned unknown value")
} else if expectedValue1.evicted != 0 {
t.Fatalf("Value was evicted unexpectedly")
}
expectedValue2 := &evictable{id: ids.NewID([32]byte{2})}
if returnedValue := cache.Deduplicate(expectedValue2).(*evictable); returnedValue != expectedValue2 {
t.Fatalf("Returned unknown value")
} else if expectedValue1.evicted != 1 {
t.Fatalf("Value should have been evicted")
} else if expectedValue2.evicted != 0 {
t.Fatalf("Value was evicted unexpectedly")
}
cache.Size = 2
expectedValue3 := &evictable{id: ids.NewID([32]byte{2})}
if returnedValue := cache.Deduplicate(expectedValue3).(*evictable); returnedValue != expectedValue2 {
t.Fatalf("Returned unknown value")
} else if expectedValue1.evicted != 1 {
t.Fatalf("Value should have been evicted")
} else if expectedValue2.evicted != 0 {
t.Fatalf("Value was evicted unexpectedly")
}
cache.Flush()
if expectedValue1.evicted != 1 {
t.Fatalf("Value should have been evicted")
} else if expectedValue2.evicted != 1 {
t.Fatalf("Value should have been evicted")
} else if expectedValue3.evicted != 0 {
t.Fatalf("Value was evicted unexpectedly")
}
}

11
chains/awaiter.go Normal file
View File

@ -0,0 +1,11 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package chains
import "github.com/ava-labs/gecko/snow/networking"
// Awaiter can await connections to be connected
type Awaiter interface {
AwaitConnections(awaiting *networking.AwaitingConnections)
}

524
chains/manager.go Normal file
View File

@ -0,0 +1,524 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package chains
import (
"fmt"
"time"
"github.com/ava-labs/gecko/api"
"github.com/ava-labs/gecko/api/keystore"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/prefixdb"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow"
"github.com/ava-labs/gecko/snow/consensus/snowball"
"github.com/ava-labs/gecko/snow/engine/avalanche"
"github.com/ava-labs/gecko/snow/engine/avalanche/state"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/snow/engine/common/queue"
"github.com/ava-labs/gecko/snow/networking"
"github.com/ava-labs/gecko/snow/networking/handler"
"github.com/ava-labs/gecko/snow/networking/router"
"github.com/ava-labs/gecko/snow/networking/sender"
"github.com/ava-labs/gecko/snow/networking/timeout"
"github.com/ava-labs/gecko/snow/triggers"
"github.com/ava-labs/gecko/snow/validators"
"github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/vms"
avacon "github.com/ava-labs/gecko/snow/consensus/avalanche"
avaeng "github.com/ava-labs/gecko/snow/engine/avalanche"
smcon "github.com/ava-labs/gecko/snow/consensus/snowman"
smeng "github.com/ava-labs/gecko/snow/engine/snowman"
)
const (
defaultChannelSize = 1000
requestTimeout = 2 * time.Second
)
// Manager manages the chains running on this node.
// It can:
// * Create a chain
// * Add a registrant. When a chain is created, each registrant calls
// RegisterChain with the new chain as the argument.
// * Get the aliases associated with a given chain.
// * Get the ID of the chain associated with a given alias.
type Manager interface {
// Return the router this Manager is using to route consensus messages to chains
Router() router.Router
// Create a chain in the future
CreateChain(ChainParameters)
// Create a chain now
ForceCreateChain(ChainParameters)
// Add a registrant [r]. Every time a chain is
// created, [r].RegisterChain([new chain]) is called
AddRegistrant(Registrant)
// Given an alias, return the ID of the chain associated with that alias
Lookup(string) (ids.ID, error)
// Given an alias, return the ID of the VM associated with that alias
LookupVM(string) (ids.ID, error)
// Return the aliases associated with a chain
Aliases(ids.ID) []string
// Add an alias to a chain
Alias(ids.ID, string) error
Shutdown()
}
// ChainParameters defines the chain being created
type ChainParameters struct {
ID ids.ID // The ID of the chain being created
SubnetID ids.ID // ID of the subnet that validates this chain
GenesisData []byte // The genesis data of this chain's ledger
VMAlias string // The ID of the vm this chain is running
FxAliases []string // The IDs of the feature extensions this chain is running
CustomBeacons validators.Set // Should only be set if the default beacons can't be used.
}
type manager struct {
// Note: The string representation of a chain's ID is also considered to be an alias of the chain
// That is, [chainID].String() is an alias for the chain, too
ids.Aliaser
log logging.Logger
logFactory logging.Factory
vmManager vms.Manager // Manage mappings from vm ID --> vm
decisionEvents *triggers.EventDispatcher
consensusEvents *triggers.EventDispatcher
db database.Database
chainRouter router.Router // Routes incoming messages to the appropriate chain
sender sender.ExternalSender // Sends consensus messages to other validators
timeoutManager *timeout.Manager // Manages request timeouts when sending messages to other validators
consensusParams avacon.Parameters // The consensus parameters (alpha, beta, etc.) for new chains
validators validators.Manager // Validators validating on this chain
registrants []Registrant // Those notified when a chain is created
nodeID ids.ShortID // The ID of this node
networkID uint32 // ID of the network this node is connected to
awaiter Awaiter // Waits for required connections before running bootstrapping
server *api.Server // Handles HTTP API calls
keystore *keystore.Keystore
unblocked bool
blockedChains []ChainParameters
}
// New returns a new Manager where:
// <db> is this node's database
// <sender> sends messages to other validators
// <validators> validate this chain
// TODO: Make this function take less arguments
func New(
log logging.Logger,
logFactory logging.Factory,
vmManager vms.Manager,
decisionEvents *triggers.EventDispatcher,
consensusEvents *triggers.EventDispatcher,
db database.Database,
router router.Router,
sender sender.ExternalSender,
consensusParams avacon.Parameters,
validators validators.Manager,
nodeID ids.ShortID,
networkID uint32,
awaiter Awaiter,
server *api.Server,
keystore *keystore.Keystore,
) Manager {
timeoutManager := timeout.Manager{}
timeoutManager.Initialize(requestTimeout)
go log.RecoverAndPanic(timeoutManager.Dispatch)
router.Initialize(log, &timeoutManager)
m := &manager{
log: log,
logFactory: logFactory,
vmManager: vmManager,
decisionEvents: decisionEvents,
consensusEvents: consensusEvents,
db: db,
chainRouter: router,
sender: sender,
timeoutManager: &timeoutManager,
consensusParams: consensusParams,
validators: validators,
nodeID: nodeID,
networkID: networkID,
awaiter: awaiter,
server: server,
keystore: keystore,
}
m.Initialize()
return m
}
// Router that this chain manager is using to route consensus messages to chains
func (m *manager) Router() router.Router { return m.chainRouter }
// Create a chain
func (m *manager) CreateChain(chain ChainParameters) {
if !m.unblocked {
m.blockedChains = append(m.blockedChains, chain)
} else {
m.ForceCreateChain(chain)
}
}
// Create a chain
func (m *manager) ForceCreateChain(chain ChainParameters) {
m.log.Info("creating chain:\n"+
" ID: %s\n"+
" VMID:%s",
chain.ID,
chain.VMAlias,
)
// Assert that there isn't already a chain with an alias in [chain].Aliases
// (Recall that the string repr. of a chain's ID is also an alias for a chain)
if alias, isRepeat := m.isChainWithAlias(chain.ID.String()); isRepeat {
m.log.Error("there is already a chain with alias '%s'. Chain not created.", alias)
return
}
vmID, err := m.vmManager.Lookup(chain.VMAlias)
if err != nil {
m.log.Error("error while looking up VM: %s", err)
return
}
// Get a factory for the vm we want to use on our chain
vmFactory, err := m.vmManager.GetVMFactory(vmID)
if err != nil {
m.log.Error("error while getting vmFactory: %s", err)
return
}
// Create the chain
vm := vmFactory.New()
fxs := make([]*common.Fx, len(chain.FxAliases))
for i, fxAlias := range chain.FxAliases {
fxID, err := m.vmManager.Lookup(fxAlias)
if err != nil {
m.log.Error("error while looking up Fx: %s", err)
return
}
// Get a factory for the fx we want to use on our chain
fxFactory, err := m.vmManager.GetVMFactory(fxID)
if err != nil {
m.log.Error("error while getting fxFactory: %s", err)
return
}
// Create the fx
fxs[i] = &common.Fx{
ID: fxID,
Fx: fxFactory.New(),
}
}
// Create the log and context of the chain
chainLog, err := m.logFactory.MakeChain(chain.ID, "")
if err != nil {
m.log.Error("error while creating chain's log %s", err)
return
}
ctx := &snow.Context{
NetworkID: m.networkID,
ChainID: chain.ID,
Log: chainLog,
DecisionDispatcher: m.decisionEvents,
ConsensusDispatcher: m.consensusEvents,
NodeID: m.nodeID,
HTTP: m.server,
Keystore: m.keystore.NewBlockchainKeyStore(chain.ID),
BCLookup: m,
}
consensusParams := m.consensusParams
if alias, err := m.PrimaryAlias(ctx.ChainID); err == nil {
consensusParams.Namespace = fmt.Sprintf("gecko_%s", alias)
} else {
consensusParams.Namespace = fmt.Sprintf("gecko_%s", ctx.ChainID)
}
// The validators of this blockchain
validators, ok := m.validators.GetValidatorSet(ids.Empty) // TODO: Change argument to chain.SubnetID
if !ok {
m.log.Error("couldn't get validator set of subnet with ID %s. The subnet may not exist", chain.SubnetID)
return
}
beacons := validators
if chain.CustomBeacons != nil {
beacons = chain.CustomBeacons
}
switch vm := vm.(type) {
case avalanche.DAGVM:
err := m.createAvalancheChain(
ctx,
chain.GenesisData,
validators,
beacons,
vm,
fxs,
consensusParams,
)
if err != nil {
m.log.Error("error while creating new avalanche vm %s", err)
return
}
case smeng.ChainVM:
err := m.createSnowmanChain(
ctx,
chain.GenesisData,
validators,
beacons,
vm,
fxs,
consensusParams.Parameters,
)
if err != nil {
m.log.Error("error while creating new snowman vm %s", err)
return
}
default:
m.log.Error("the vm should have type avalanche.DAGVM or snowman.ChainVM. Chain not created")
return
}
// Associate the newly created chain with its default alias
m.log.AssertNoError(m.Alias(chain.ID, chain.ID.String()))
// Notify those that registered to be notified when a new chain is created
m.notifyRegistrants(ctx, vm)
}
// Implements Manager.AddRegistrant
func (m *manager) AddRegistrant(r Registrant) { m.registrants = append(m.registrants, r) }
func (m *manager) unblockChains() {
m.unblocked = true
blocked := m.blockedChains
m.blockedChains = nil
for _, chain := range blocked {
m.ForceCreateChain(chain)
}
}
// Create a DAG-based blockchain that uses Avalanche
func (m *manager) createAvalancheChain(
ctx *snow.Context,
genesisData []byte,
validators,
beacons validators.Set,
vm avalanche.DAGVM,
fxs []*common.Fx,
consensusParams avacon.Parameters,
) error {
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
db := prefixdb.New(ctx.ChainID.Bytes(), m.db)
vmDB := prefixdb.New([]byte("vm"), db)
vertexDB := prefixdb.New([]byte("vertex"), db)
vertexBootstrappingDB := prefixdb.New([]byte("vertex_bootstrapping"), db)
txBootstrappingDB := prefixdb.New([]byte("tx_bootstrapping"), db)
vtxBlocker, err := queue.New(vertexBootstrappingDB)
if err != nil {
return err
}
txBlocker, err := queue.New(txBootstrappingDB)
if err != nil {
return err
}
// The channel through which a VM may send messages to the consensus engine
// VM uses this channel to notify engine that a block is ready to be made
msgChan := make(chan common.Message, defaultChannelSize)
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, fxs); err != nil {
return err
}
// Handles serialization/deserialization of vertices and also the
// persistence of vertices
vtxState := &state.Serializer{}
vtxState.Initialize(ctx, vm, vertexDB)
// Passes messages from the consensus engine to the network
sender := sender.Sender{}
sender.Initialize(ctx, m.sender, m.chainRouter, m.timeoutManager)
// The engine handles consensus
engine := avaeng.Transitive{
Config: avaeng.Config{
BootstrapConfig: avaeng.BootstrapConfig{
Config: common.Config{
Context: ctx,
},
},
},
}
engine.Initialize(avaeng.Config{
BootstrapConfig: avaeng.BootstrapConfig{
Config: common.Config{
Context: ctx,
Validators: validators,
Beacons: beacons,
Alpha: (beacons.Len() + 1) / 2,
Sender: &sender,
},
VtxBlocked: vtxBlocker,
TxBlocked: txBlocker,
State: vtxState,
VM: vm,
},
Params: consensusParams,
Consensus: &avacon.Topological{},
})
// Asynchronously passes messages from the network to the consensus engine
handler := &handler.Handler{}
handler.Initialize(&engine, msgChan, defaultChannelSize)
// Allows messages to be routed to the new chain
m.chainRouter.AddChain(handler)
go ctx.Log.RecoverAndPanic(handler.Dispatch)
awaiting := &networking.AwaitingConnections{
Finish: func() {
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
engine.Startup()
},
}
for _, vdr := range beacons.List() {
awaiting.Requested.Add(vdr.ID())
}
awaiting.NumRequired = (3*awaiting.Requested.Len() + 3) / 4 // 75% must be connected to
m.awaiter.AwaitConnections(awaiting)
return nil
}
// Create a linear chain using the Snowman consensus engine
func (m *manager) createSnowmanChain(
ctx *snow.Context,
genesisData []byte,
validators,
beacons validators.Set,
vm smeng.ChainVM,
fxs []*common.Fx,
consensusParams snowball.Parameters,
) error {
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
db := prefixdb.New(ctx.ChainID.Bytes(), m.db)
vmDB := prefixdb.New([]byte("vm"), db)
bootstrappingDB := prefixdb.New([]byte("bootstrapping"), db)
blocked, err := queue.New(bootstrappingDB)
if err != nil {
return err
}
// The channel through which a VM may send messages to the consensus engine
// VM uses this channel to notify engine that a block is ready to be made
msgChan := make(chan common.Message, defaultChannelSize)
// Initialize the VM
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, fxs); err != nil {
return err
}
// Passes messages from the consensus engine to the network
sender := sender.Sender{}
sender.Initialize(ctx, m.sender, m.chainRouter, m.timeoutManager)
// The engine handles consensus
engine := smeng.Transitive{}
engine.Initialize(smeng.Config{
BootstrapConfig: smeng.BootstrapConfig{
Config: common.Config{
Context: ctx,
Validators: validators,
Beacons: beacons,
Alpha: (beacons.Len() + 1) / 2,
Sender: &sender,
},
Blocked: blocked,
VM: vm,
Bootstrapped: m.unblockChains,
},
Params: consensusParams,
Consensus: &smcon.Topological{},
})
// Asynchronously passes messages from the network to the consensus engine
handler := &handler.Handler{}
handler.Initialize(&engine, msgChan, defaultChannelSize)
// Allow incoming messages to be routed to the new chain
m.chainRouter.AddChain(handler)
go ctx.Log.RecoverAndPanic(handler.Dispatch)
awaiting := &networking.AwaitingConnections{
Finish: func() {
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
engine.Startup()
},
}
for _, vdr := range beacons.List() {
awaiting.Requested.Add(vdr.ID())
}
awaiting.NumRequired = (3*awaiting.Requested.Len() + 3) / 4 // 75% must be connected to
m.awaiter.AwaitConnections(awaiting)
return nil
}
// Shutdown stops all the chains
func (m *manager) Shutdown() { m.chainRouter.Shutdown() }
// LookupVM returns the ID of the VM associated with an alias
func (m *manager) LookupVM(alias string) (ids.ID, error) { return m.vmManager.Lookup(alias) }
// Notify registrants [those who want to know about the creation of chains]
// that the specified chain has been created
func (m *manager) notifyRegistrants(ctx *snow.Context, vm interface{}) {
for _, registrant := range m.registrants {
registrant.RegisterChain(ctx, vm)
}
}
// Returns:
// 1) the alias that already exists, or the empty string if there is none
// 2) true iff there exists a chain such that the chain has an alias in [aliases]
func (m *manager) isChainWithAlias(aliases ...string) (string, bool) {
for _, alias := range aliases {
if _, err := m.Lookup(alias); err == nil {
return alias, true
}
}
return "", false
}

13
chains/registrant.go Normal file
View File

@ -0,0 +1,13 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package chains
import (
"github.com/ava-labs/gecko/snow"
)
// Registrant can register the existence of a chain
type Registrant interface {
RegisterChain(ctx *snow.Context, vm interface{})
}

33
database/batch.go Normal file
View File

@ -0,0 +1,33 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// For ease of implementation, our database's interface matches Ethereum's
// database implementation. This was to allow use to use Geth code as is for the
// EVM chain.
package database
// Batch is a write-only database that commits changes to its host database
// when Write is called. A batch cannot be used concurrently.
type Batch interface {
KeyValueWriter
// ValueSize retrieves the amount of data queued up for writing.
ValueSize() int
// Write flushes any accumulated data to disk.
Write() error
// Reset resets the batch for reuse.
Reset()
// Replay replays the batch contents.
Replay(w KeyValueWriter) error
}
// Batcher wraps the NewBatch method of a backing data store.
type Batcher interface {
// NewBatch creates a write-only database that buffers changes to its host db
// until a final write is called.
NewBatch() Batch
}

62
database/database.go Normal file
View File

@ -0,0 +1,62 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// For ease of implementation, our database's interface matches Ethereum's
// database implementation. This was to allow use to use Geth code as is for the
// EVM chain.
package database
import (
"io"
)
// KeyValueReader wraps the Has and Get method of a backing data store.
type KeyValueReader interface {
// Has retrieves if a key is present in the key-value data store.
Has(key []byte) (bool, error)
// Get retrieves the given key if it's present in the key-value data store.
Get(key []byte) ([]byte, error)
}
// KeyValueWriter wraps the Put method of a backing data store.
type KeyValueWriter interface {
// Put inserts the given value into the key-value data store.
Put(key []byte, value []byte) error
// Delete removes the key from the key-value data store.
Delete(key []byte) error
}
// Stater wraps the Stat method of a backing data store.
type Stater interface {
// Stat returns a particular internal stat of the database.
Stat(property string) (string, error)
}
// Compacter wraps the Compact method of a backing data store.
type Compacter interface {
// Compact the underlying DB for the given key range.
// Specifically, deleted and overwritten versions are discarded,
// and the data is rearranged to reduce the cost of operations
// needed to access the data. This operation should typically only
// be invoked by users who understand the underlying implementation.
//
// A nil start is treated as a key before all keys in the DB.
// And a nil limit is treated as a key after all keys in the DB.
// Therefore if both are nil then it will compact entire DB.
Compact(start []byte, limit []byte) error
}
// Database contains all the methods required to allow handling different
// key-value data stores backing the database.
type Database interface {
KeyValueReader
KeyValueWriter
Batcher
Iteratee
Stater
Compacter
io.Closer
}

283
database/encdb/encdb.go Normal file
View File

@ -0,0 +1,283 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package encdb
import (
"crypto/cipher"
"crypto/rand"
"sync"
"golang.org/x/crypto/chacha20poly1305"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/nodb"
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/vms/components/codec"
)
// Database encrypts all values that are provided
type Database struct {
lock sync.RWMutex
codec codec.Codec
cipher cipher.AEAD
db database.Database
}
// New returns a new encrypted database
func New(password []byte, db database.Database) (*Database, error) {
h := hashing.ComputeHash256(password)
aead, err := chacha20poly1305.NewX(h)
if err != nil {
return nil, err
}
return &Database{
codec: codec.NewDefault(),
cipher: aead,
db: db,
}, nil
}
// Has implements the Database interface
func (db *Database) Has(key []byte) (bool, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return false, database.ErrClosed
}
return db.db.Has(key)
}
// Get implements the Database interface
func (db *Database) Get(key []byte) ([]byte, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return nil, database.ErrClosed
}
encVal, err := db.db.Get(key)
if err != nil {
return nil, err
}
return db.decrypt(encVal)
}
// Put implements the Database interface
func (db *Database) Put(key, value []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
encValue, err := db.encrypt(value)
if err != nil {
return err
}
return db.db.Put(key, encValue)
}
// Delete implements the Database interface
func (db *Database) Delete(key []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
return db.db.Delete(key)
}
// NewBatch implements the Database interface
func (db *Database) NewBatch() database.Batch {
return &batch{
Batch: db.db.NewBatch(),
db: db,
}
}
// NewIterator implements the Database interface
func (db *Database) NewIterator() database.Iterator { return db.NewIteratorWithStartAndPrefix(nil, nil) }
// NewIteratorWithStart implements the Database interface
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(start, nil)
}
// NewIteratorWithPrefix implements the Database interface
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(nil, prefix)
}
// NewIteratorWithStartAndPrefix implements the Database interface
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return &nodb.Iterator{Err: database.ErrClosed}
}
return &iterator{
Iterator: db.db.NewIteratorWithStartAndPrefix(start, prefix),
db: db,
}
}
// Stat implements the Database interface
func (db *Database) Stat(stat string) (string, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return "", database.ErrClosed
}
return db.db.Stat(stat)
}
// Compact implements the Database interface
func (db *Database) Compact(start, limit []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
return db.db.Compact(start, limit)
}
// Close implements the Database interface
func (db *Database) Close() error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
db.db = nil
return nil
}
type keyValue struct {
key []byte
value []byte
delete bool
}
type batch struct {
database.Batch
db *Database
writes []keyValue
}
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
encValue, err := b.db.encrypt(value)
if err != nil {
return err
}
return b.Batch.Put(key, encValue)
}
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
return b.Batch.Delete(key)
}
func (b *batch) Write() error {
b.db.lock.Lock()
defer b.db.lock.Unlock()
if b.db.db == nil {
return database.ErrClosed
}
return b.Batch.Write()
}
// Reset resets the batch for reuse.
func (b *batch) Reset() {
b.writes = b.writes[:0]
b.Batch.Reset()
}
// Replay replays the batch contents.
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
}
type iterator struct {
database.Iterator
db *Database
val []byte
err error
}
func (it *iterator) Next() bool {
next := it.Iterator.Next()
if next {
encVal := it.Iterator.Value()
val, err := it.db.decrypt(encVal)
if err != nil {
it.err = err
return false
}
it.val = val
} else {
it.val = nil
}
return next
}
func (it *iterator) Error() error {
if it.err != nil {
return it.err
}
return it.Iterator.Error()
}
func (it *iterator) Value() []byte { return it.val }
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}
type encryptedValue struct {
Ciphertext []byte `serialize:"true"`
Nonce []byte `serialize:"true"`
}
func (db *Database) encrypt(plaintext []byte) ([]byte, error) {
nonce := make([]byte, chacha20poly1305.NonceSizeX)
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
ciphertext := db.cipher.Seal(nil, nonce, plaintext, nil)
return db.codec.Marshal(&encryptedValue{
Ciphertext: ciphertext,
Nonce: nonce,
})
}
func (db *Database) decrypt(ciphertext []byte) ([]byte, error) {
val := encryptedValue{}
if err := db.codec.Unmarshal(ciphertext, &val); err != nil {
return nil, err
}
return db.cipher.Open(nil, val.Nonce, val.Ciphertext, nil)
}

View File

@ -0,0 +1,24 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package encdb
import (
"testing"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/memdb"
)
func TestInterface(t *testing.T) {
pw := "lol totally a secure password"
for _, test := range database.Tests {
unencryptedDB := memdb.New()
db, err := New([]byte(pw), unencryptedDB)
if err != nil {
t.Fatal(err)
}
test(t, db)
}
}

12
database/errors.go Normal file
View File

@ -0,0 +1,12 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package database
import "errors"
// common errors
var (
ErrClosed = errors.New("closed")
ErrNotFound = errors.New("not found")
)

62
database/iterator.go Normal file
View File

@ -0,0 +1,62 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// For ease of implementation, our database's interface matches Ethereum's
// database implementation. This was to allow use to use Geth code as is for the
// EVM chain.
package database
// Iterator iterates over a database's key/value pairs in ascending key order.
//
// When it encounters an error any seek will return false and will yield no key/
// value pairs. The error can be queried by calling the Error method. Calling
// Release is still necessary.
//
// An iterator must be released after use, but it is not necessary to read an
// iterator until exhaustion. An iterator is not safe for concurrent use, but it
// is safe to use multiple iterators concurrently.
type Iterator interface {
// Next moves the iterator to the next key/value pair. It returns whether the
// iterator is exhausted.
Next() bool
// Error returns any accumulated error. Exhausting all the key/value pairs
// is not considered to be an error.
Error() error
// Key returns the key of the current key/value pair, or nil if done. The caller
// should not modify the contents of the returned slice, and its contents may
// change on the next call to Next.
Key() []byte
// Value returns the value of the current key/value pair, or nil if done. The
// caller should not modify the contents of the returned slice, and its contents
// may change on the next call to Next.
Value() []byte
// Release releases associated resources. Release should always succeed and can
// be called multiple times without causing error.
Release()
}
// Iteratee wraps the NewIterator methods of a backing data store.
type Iteratee interface {
// NewIterator creates a binary-alphabetical iterator over the entire keyspace
// contained within the key-value database.
NewIterator() Iterator
// NewIteratorWithStart creates a binary-alphabetical iterator over a subset of
// database content starting at a particular initial key (or after, if it does
// not exist).
NewIteratorWithStart(start []byte) Iterator
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
// of database content with a particular key prefix.
NewIteratorWithPrefix(prefix []byte) Iterator
// NewIteratorWithStartAndPrefix creates a binary-alphabetical iterator over a
// subset of database content with a particular key prefix starting at a
// specified key.
NewIteratorWithStartAndPrefix(start, prefix []byte) Iterator
}

219
database/leveldb/leveldb.go Normal file
View File

@ -0,0 +1,219 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package leveldb
import (
"bytes"
"github.com/ava-labs/gecko/database"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/filter"
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/util"
)
const (
// minBlockCacheSize is the minimum number of bytes to use for block caching
// in leveldb.
minBlockCacheSize = 8 * opt.MiB
// minWriteBufferSize is the minimum number of bytes to use for buffers in
// leveldb.
minWriteBufferSize = 8 * opt.MiB
// minHandleCap is the minimum number of files descriptors to cap levelDB to
// use
minHandleCap = 16
)
// Database is a persistent key-value store. Apart from basic data storage
// functionality it also supports batch writes and iterating over the keyspace
// in binary-alphabetical order.
type Database struct{ *leveldb.DB }
// New returns a wrapped LevelDB object.
func New(file string, blockCacheSize, writeBufferSize, handleCap int) (*Database, error) {
// Enforce minimums
if blockCacheSize < minBlockCacheSize {
blockCacheSize = minBlockCacheSize
}
if writeBufferSize < minWriteBufferSize {
writeBufferSize = minWriteBufferSize
}
if handleCap < minHandleCap {
handleCap = minHandleCap
}
// Open the db and recover any potential corruptions
db, err := leveldb.OpenFile(file, &opt.Options{
OpenFilesCacheCapacity: handleCap,
BlockCacheCapacity: blockCacheSize,
// There are two buffers of size WriteBuffer used.
WriteBuffer: writeBufferSize / 2,
Filter: filter.NewBloomFilter(10),
})
if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
db, err = leveldb.RecoverFile(file, nil)
}
if err != nil {
return nil, err
}
return &Database{DB: db}, nil
}
// Has returns if the key is set in the database
func (db *Database) Has(key []byte) (bool, error) {
has, err := db.DB.Has(key, nil)
return has, updateError(err)
}
// Get returns the value the key maps to in the database
func (db *Database) Get(key []byte) ([]byte, error) {
value, err := db.DB.Get(key, nil)
return value, updateError(err)
}
// Put sets the value of the provided key to the provided value
func (db *Database) Put(key []byte, value []byte) error {
return updateError(db.DB.Put(key, value, nil))
}
// Delete removes the key from the database
func (db *Database) Delete(key []byte) error { return updateError(db.DB.Delete(key, nil)) }
// NewBatch creates a write/delete-only buffer that is atomically committed to
// the database when write is called
func (db *Database) NewBatch() database.Batch { return &batch{db: db.DB} }
// NewIterator creates a lexicographically ordered iterator over the database
func (db *Database) NewIterator() database.Iterator {
return &iter{db.DB.NewIterator(new(util.Range), nil)}
}
// NewIteratorWithStart creates a lexicographically ordered iterator over the
// database starting at the provided key
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
return &iter{db.DB.NewIterator(&util.Range{Start: start}, nil)}
}
// NewIteratorWithPrefix creates a lexicographically ordered iterator over the
// database ignoring keys that do not start with the provided prefix
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
return &iter{db.DB.NewIterator(util.BytesPrefix(prefix), nil)}
}
// NewIteratorWithStartAndPrefix creates a lexicographically ordered iterator
// over the database starting at start and ignoring keys that do not start with
// the provided prefix
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
iterRange := util.BytesPrefix(prefix)
if bytes.Compare(start, prefix) == 1 {
iterRange.Start = start
}
return &iter{db.DB.NewIterator(iterRange, nil)}
}
// Stat returns a particular internal stat of the database.
func (db *Database) Stat(property string) (string, error) {
stat, err := db.DB.GetProperty(property)
return stat, updateError(err)
}
// This comment is basically copy pasted from the underlying levelDB library:
// Compact the underlying DB for the given key range.
// Specifically, deleted and overwritten versions are discarded,
// and the data is rearranged to reduce the cost of operations
// needed to access the data. This operation should typically only
// be invoked by users who understand the underlying implementation.
//
// A nil start is treated as a key before all keys in the DB.
// And a nil limit is treated as a key after all keys in the DB.
// Therefore if both are nil then it will compact entire DB.
func (db *Database) Compact(start []byte, limit []byte) error {
return updateError(db.DB.CompactRange(util.Range{Start: start, Limit: limit}))
}
// Close implements the Database interface
func (db *Database) Close() error { return updateError(db.DB.Close()) }
// batch is a wrapper around a levelDB batch to contain sizes.
type batch struct {
leveldb.Batch
db *leveldb.DB
size int
}
// Put the value into the batch for later writing
func (b *batch) Put(key, value []byte) error {
b.Batch.Put(key, value)
b.size += len(value)
return nil
}
// Delete the key during writing
func (b *batch) Delete(key []byte) error {
b.Batch.Delete(key)
b.size++
return nil
}
// ValueSize retrieves the amount of data queued up for writing.
func (b *batch) ValueSize() int { return b.size }
// Write flushes any accumulated data to disk.
func (b *batch) Write() error { return updateError(b.db.Write(&b.Batch, nil)) }
// Reset resets the batch for reuse.
func (b *batch) Reset() {
b.Batch.Reset()
b.size = 0
}
// Replay the batch contents.
func (b *batch) Replay(w database.KeyValueWriter) error {
replay := &replayer{writer: w}
if err := b.Batch.Replay(replay); err != nil {
// Never actually returns an error, because Replay just returns nil
return updateError(err)
}
return updateError(replay.err)
}
type replayer struct {
writer database.KeyValueWriter
err error
}
func (r *replayer) Put(key, value []byte) {
if r.err != nil {
return
}
r.err = r.writer.Put(key, value)
}
func (r *replayer) Delete(key []byte) {
if r.err != nil {
return
}
r.err = r.writer.Delete(key)
}
type iter struct{ iterator.Iterator }
func (i *iter) Error() error { return updateError(i.Iterator.Error()) }
func updateError(err error) error {
switch err {
case leveldb.ErrClosed:
return database.ErrClosed
case leveldb.ErrNotFound:
return database.ErrNotFound
default:
return err
}
}

View File

@ -0,0 +1,27 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package leveldb
import (
"fmt"
"os"
"testing"
"github.com/ava-labs/gecko/database"
)
func TestInterface(t *testing.T) {
for i, test := range database.Tests {
folder := fmt.Sprintf("db%d", i)
db, err := New(folder, 0, 0, 0)
if err != nil {
t.Fatalf("leveldb.New(%s, 0, 0) errored with %s", folder, err)
}
defer os.RemoveAll(folder)
defer db.Close()
test(t, db)
}
}

258
database/memdb/memdb.go Normal file
View File

@ -0,0 +1,258 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package memdb
import (
"sort"
"strings"
"sync"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/nodb"
)
// DefaultSize is the default initial size of the memory database
const DefaultSize = 1 << 10
// Database is an ephemeral key-value store that implements the Database
// interface.
type Database struct {
lock sync.RWMutex
db map[string][]byte
}
// New returns a map with the Database interface methods implemented.
func New() *Database { return NewWithSize(DefaultSize) }
// NewWithSize returns a map pre-allocated to the provided size with the
// Database interface methods implemented.
func NewWithSize(size int) *Database { return &Database{db: make(map[string][]byte, size)} }
// Close implements the Database interface
func (db *Database) Close() error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
db.db = nil
return nil
}
// Has implements the Database interface
func (db *Database) Has(key []byte) (bool, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return false, database.ErrClosed
}
_, ok := db.db[string(key)]
return ok, nil
}
// Get implements the Database interface
func (db *Database) Get(key []byte) ([]byte, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return nil, database.ErrClosed
}
if entry, ok := db.db[string(key)]; ok {
return copyBytes(entry), nil
}
return nil, database.ErrNotFound
}
// Put implements the Database interface
func (db *Database) Put(key []byte, value []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
db.db[string(key)] = copyBytes(value)
return nil
}
// Delete implements the Database interface
func (db *Database) Delete(key []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
delete(db.db, string(key))
return nil
}
// NewBatch implements the Database interface
func (db *Database) NewBatch() database.Batch { return &batch{db: db} }
// NewIterator implements the Database interface
func (db *Database) NewIterator() database.Iterator { return db.NewIteratorWithStartAndPrefix(nil, nil) }
// NewIteratorWithStart implements the Database interface
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(start, nil)
}
// NewIteratorWithPrefix implements the Database interface
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(nil, prefix)
}
// NewIteratorWithStartAndPrefix implements the Database interface
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return &nodb.Iterator{Err: database.ErrClosed}
}
startString := string(start)
prefixString := string(prefix)
keys := make([]string, 0, len(db.db))
for key := range db.db {
if strings.HasPrefix(key, prefixString) && key >= startString {
keys = append(keys, key)
}
}
sort.Strings(keys) // Keys need to be in sorted order
values := make([][]byte, 0, len(keys))
for _, key := range keys {
values = append(values, db.db[key])
}
return &iterator{
keys: keys,
values: values,
}
}
// Stat implements the Database interface
func (db *Database) Stat(property string) (string, error) { return "", database.ErrNotFound }
// Compact implements the Database interface
func (db *Database) Compact(start []byte, limit []byte) error { return nil }
type keyValue struct {
key []byte
value []byte
delete bool
}
type batch struct {
db *Database
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
}
// ValueSize implements the Batch interface
func (b *batch) ValueSize() int { return b.size }
// Write implements the Batch interface
func (b *batch) Write() error {
b.db.lock.Lock()
defer b.db.lock.Unlock()
if b.db.db == nil {
return database.ErrClosed
}
for _, kv := range b.writes {
key := string(kv.key)
if kv.delete {
delete(b.db.db, key)
} else {
b.db.db[key] = kv.value
}
}
return nil
}
// Reset implements the Batch interface
func (b *batch) Reset() {
b.writes = b.writes[:0]
b.size = 0
}
// Replay implements the Batch interface
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
}
type iterator struct {
initialized bool
keys []string
values [][]byte
}
// Next implements the Iterator interface
func (it *iterator) Next() bool {
// If the iterator was not yet initialized, do it now
if !it.initialized {
it.initialized = true
return len(it.keys) > 0
}
// Iterator already initialize, advance it
if len(it.keys) > 0 {
it.keys = it.keys[1:]
it.values = it.values[1:]
}
return len(it.keys) > 0
}
// Error implements the Iterator interface
func (it *iterator) Error() error { return nil }
// Key implements the Iterator interface
func (it *iterator) Key() []byte {
if len(it.keys) > 0 {
return []byte(it.keys[0])
}
return nil
}
// Value implements the Iterator interface
func (it *iterator) Value() []byte {
if len(it.values) > 0 {
return it.values[0]
}
return nil
}
// Release implements the Iterator interface
func (it *iterator) Release() { it.keys = nil; it.values = nil }
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}

View File

@ -0,0 +1,16 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package memdb
import (
"testing"
"github.com/ava-labs/gecko/database"
)
func TestInterface(t *testing.T) {
for _, test := range database.Tests {
test(t, New())
}
}

137
database/mockdb/mockdb.go Normal file
View File

@ -0,0 +1,137 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package mockdb
import (
"errors"
"github.com/ava-labs/gecko/database"
)
var errNoFunction = errors.New("user didn't specify what value(s) return")
// Database implements database.Database.
// This is a mock database meant to be used in tests.
// You specify the database's return value(s) for a given method call by
// assign value to the corresponding member.
// For example, to specify what should happen when Has is called,
// assign a value to OnHas.
// If no value is assigned to the corresponding member, the method returns an error or nil
// If you
type Database struct {
// Executed when Has is called
OnHas func([]byte) (bool, error)
OnGet func([]byte) ([]byte, error)
OnPut func([]byte) error
OnDelete func([]byte) error
OnNewBatch func() database.Batch
OnNewIterator func() database.Iterator
OnNewIteratorWithStart func([]byte) database.Iterator
OnNewIteratorWithPrefix func([]byte) database.Iterator
OnNewIteratorWithStartAndPrefix func([]byte, []byte) database.Iterator
OnStat func() (string, error)
OnCompact func([]byte, []byte) error
OnClose func() error
}
// Has implements the database.Database interface
func (db *Database) Has(b []byte) (bool, error) {
if db.OnHas == nil {
return false, errNoFunction
}
return db.OnHas(b)
}
// Get implements the database.Database interface
func (db *Database) Get(b []byte) ([]byte, error) {
if db.OnGet == nil {
return nil, errNoFunction
}
return db.OnGet(b)
}
// Put implements the database.Database interface
func (db *Database) Put(b []byte) error {
if db.OnPut == nil {
return errNoFunction
}
return db.OnPut(b)
}
// Delete implements the database.Database interface
func (db *Database) Delete(b []byte) error {
if db.OnDelete == nil {
return errNoFunction
}
return db.OnDelete(b)
}
// NewBatch implements the database.Database interface
func (db *Database) NewBatch() database.Batch {
if db.OnNewBatch == nil {
return nil
}
return db.OnNewBatch()
}
// NewIterator implements the database.Database interface
func (db *Database) NewIterator() database.Iterator {
if db.OnNewIterator == nil {
return nil
}
return db.OnNewIterator()
}
// NewIteratorWithStart implements the database.Database interface
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
if db.OnNewIteratorWithStart == nil {
return nil
}
return db.OnNewIteratorWithStart(start)
}
// NewIteratorWithPrefix implements the database.Database interface
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
if db.OnNewIteratorWithPrefix == nil {
return nil
}
return db.OnNewIteratorWithPrefix(prefix)
}
// NewIteratorWithStartAndPrefix implements the database.Database interface
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
if db.OnNewIteratorWithStartAndPrefix == nil {
return nil
}
return db.OnNewIteratorWithStartAndPrefix(start, prefix)
}
// Stat implements the database.Database interface
func (db *Database) Stat() (string, error) {
if db.OnStat == nil {
return "", errNoFunction
}
return db.OnStat()
}
// Compact implements the database.Database interface
func (db *Database) Compact(start []byte, limit []byte) error {
if db.OnCompact == nil {
return errNoFunction
}
return db.OnCompact(start, limit)
}
// Close implements the database.Database interface
func (db *Database) Close() error {
if db.OnClose == nil {
return errNoFunction
}
return db.OnClose()
}
// New returns a new mock database
func New() *Database {
return &Database{}
}

View File

@ -0,0 +1,75 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package mockdb
import (
"bytes"
"errors"
"testing"
)
// Assert that when no members are assigned values, every method returns nil/error
func TestDefaultError(t *testing.T) {
db := New()
if err := db.Close(); err == nil {
t.Fatal("should have errored")
}
if _, err := db.Has([]byte{}); err == nil {
t.Fatal("should have errored")
}
if _, err := db.Get([]byte{}); err == nil {
t.Fatal("should have errored")
}
if err := db.Put([]byte{}); err == nil {
t.Fatal("should have errored")
}
if err := db.Delete([]byte{}); err == nil {
t.Fatal("should have errored")
}
if batch := db.NewBatch(); batch != nil {
t.Fatal("should have been nil")
}
if iterator := db.NewIterator(); iterator != nil {
t.Fatal("should have errored")
}
if iterator := db.NewIteratorWithPrefix([]byte{}); iterator != nil {
t.Fatal("should have errored")
}
if iterator := db.NewIteratorWithStart([]byte{}); iterator != nil {
t.Fatal("should have errored")
}
if iterator := db.NewIteratorWithStartAndPrefix([]byte{}, []byte{}); iterator != nil {
t.Fatal("should have errored")
}
if err := db.Compact([]byte{}, []byte{}); err == nil {
t.Fatal("should have errored")
}
if _, err := db.Stat(); err == nil {
t.Fatal("should have errored")
}
}
// Assert that mocking works for Get
func TestGet(t *testing.T) {
db := New()
// Mock Has()
db.OnHas = func(b []byte) (bool, error) {
if bytes.Equal(b, []byte{1, 2, 3}) {
return true, nil
}
return false, errors.New("")
}
if has, err := db.Has([]byte{1, 2, 3}); err != nil {
t.Fatal("should not have errored")
} else if has != true {
t.Fatal("has should be true")
}
if _, err := db.Has([]byte{1, 2}); err == nil {
t.Fatal("should have have errored")
}
}

88
database/nodb/nodb.go Normal file
View File

@ -0,0 +1,88 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package nodb
import (
"github.com/ava-labs/gecko/database"
)
// Database is a lightning fast key value store with probabilistic operations.
type Database struct{}
// Has returns false, nil
func (*Database) Has([]byte) (bool, error) { return false, database.ErrClosed }
// Get returns nil, error
func (*Database) Get([]byte) ([]byte, error) { return nil, database.ErrClosed }
// Put returns nil
func (*Database) Put(_ []byte, _ []byte) error { return database.ErrClosed }
// Delete returns nil
func (*Database) Delete([]byte) error { return database.ErrClosed }
// NewBatch returns a new batch
func (*Database) NewBatch() database.Batch { return &Batch{} }
// NewIterator returns a new empty iterator
func (*Database) NewIterator() database.Iterator { return &Iterator{} }
// NewIteratorWithStart returns a new empty iterator
func (*Database) NewIteratorWithStart([]byte) database.Iterator { return &Iterator{} }
// NewIteratorWithPrefix returns a new empty iterator
func (*Database) NewIteratorWithPrefix([]byte) database.Iterator { return &Iterator{} }
// NewIteratorWithStartAndPrefix returns a new empty iterator
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
return &Iterator{}
}
// Stat returns an error
func (*Database) Stat(string) (string, error) { return "", database.ErrClosed }
// Compact returns nil
func (*Database) Compact(_, _ []byte) error { return database.ErrClosed }
// Close returns nil
func (*Database) Close() error { return database.ErrClosed }
// Batch does nothing
type Batch struct{}
// Put returns nil
func (*Batch) Put(_, _ []byte) error { return database.ErrClosed }
// Delete returns nil
func (*Batch) Delete([]byte) error { return database.ErrClosed }
// ValueSize returns 0
func (*Batch) ValueSize() int { return 0 }
// Write returns nil
func (*Batch) Write() error { return database.ErrClosed }
// Reset does nothing
func (*Batch) Reset() {}
// Replay does nothing
func (*Batch) Replay(database.KeyValueWriter) error { return database.ErrClosed }
// Iterator does nothing
type Iterator struct{ Err error }
// Next returns false
func (*Iterator) Next() bool { return false }
// Error returns any errors
func (it *Iterator) Error() error { return it.Err }
// Key returns nil
func (*Iterator) Key() []byte { return nil }
// Value returns nil
func (*Iterator) Value() []byte { return nil }
// Release does nothing
func (*Iterator) Release() {}

View File

@ -0,0 +1,237 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package prefixdb
import (
"sync"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/nodb"
"github.com/ava-labs/gecko/utils/hashing"
)
// Database partitions a database into a sub-database by prefixing all keys with
// a unique value.
type Database struct {
lock sync.RWMutex
dbPrefix []byte
db database.Database
}
// New returns a new prefixed database
func New(prefix []byte, db database.Database) *Database {
if prefixDB, ok := db.(*Database); ok {
simplePrefix := make([]byte, len(prefixDB.dbPrefix)+len(prefix))
copy(simplePrefix, prefixDB.dbPrefix)
copy(simplePrefix[len(prefixDB.dbPrefix):], prefix)
return NewNested(simplePrefix, prefixDB.db)
}
return NewNested(prefix, db)
}
// NewNested returns a new prefixed database without attempting to compress
// prefixes.
func NewNested(prefix []byte, db database.Database) *Database {
return &Database{
dbPrefix: hashing.ComputeHash256(prefix),
db: db,
}
}
// Has implements the Database interface
func (db *Database) Has(key []byte) (bool, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return false, database.ErrClosed
}
return db.db.Has(db.prefix(key))
}
// Get implements the Database interface
func (db *Database) Get(key []byte) ([]byte, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return nil, database.ErrClosed
}
return db.db.Get(db.prefix(key))
}
// Put implements the Database interface
func (db *Database) Put(key, value []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
return db.db.Put(db.prefix(key), value)
}
// Delete implements the Database interface
func (db *Database) Delete(key []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
return db.db.Delete(db.prefix(key))
}
// NewBatch implements the Database interface
func (db *Database) NewBatch() database.Batch {
return &batch{
Batch: db.db.NewBatch(),
db: db,
}
}
// NewIterator implements the Database interface
func (db *Database) NewIterator() database.Iterator { return db.NewIteratorWithStartAndPrefix(nil, nil) }
// NewIteratorWithStart implements the Database interface
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(start, nil)
}
// NewIteratorWithPrefix implements the Database interface
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(nil, prefix)
}
// NewIteratorWithStartAndPrefix implements the Database interface
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return &nodb.Iterator{Err: database.ErrClosed}
}
return &iterator{
Iterator: db.db.NewIteratorWithStartAndPrefix(db.prefix(start), db.prefix(prefix)),
db: db,
}
}
// Stat implements the Database interface
func (db *Database) Stat(stat string) (string, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.db == nil {
return "", database.ErrClosed
}
return db.db.Stat(stat)
}
// Compact implements the Database interface
func (db *Database) Compact(start, limit []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
return db.db.Compact(db.prefix(start), db.prefix(limit))
}
// Close implements the Database interface
func (db *Database) Close() error {
db.lock.Lock()
defer db.lock.Unlock()
if db.db == nil {
return database.ErrClosed
}
db.db = nil
return nil
}
func (db *Database) prefix(key []byte) []byte {
prefixedKey := make([]byte, len(db.dbPrefix)+len(key))
copy(prefixedKey, db.dbPrefix)
copy(prefixedKey[len(db.dbPrefix):], key)
return prefixedKey
}
type keyValue struct {
key []byte
value []byte
delete bool
}
type batch struct {
database.Batch
db *Database
writes []keyValue
}
// Put implements the Batch interface
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
return b.Batch.Put(b.db.prefix(key), value)
}
// Delete implements the Batch interface
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
return b.Batch.Delete(b.db.prefix(key))
}
// Write flushes any accumulated data to the memory database.
func (b *batch) Write() error {
b.db.lock.Lock()
defer b.db.lock.Unlock()
if b.db.db == nil {
return database.ErrClosed
}
return b.Batch.Write()
}
// Reset resets the batch for reuse.
func (b *batch) Reset() {
b.writes = b.writes[:0]
b.Batch.Reset()
}
// Replay replays the batch contents.
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
}
type iterator struct {
database.Iterator
db *Database
}
// Key calls the inner iterators Key and strips the prefix
func (it *iterator) Key() []byte {
key := it.Iterator.Key()
if prefixLen := len(it.db.dbPrefix); len(key) >= prefixLen {
return key[prefixLen:]
}
return key
}
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}

View File

@ -0,0 +1,23 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package prefixdb
import (
"testing"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/memdb"
)
func TestInterface(t *testing.T) {
for _, test := range database.Tests {
db := memdb.New()
test(t, New([]byte("hello"), db))
test(t, New([]byte("world"), db))
test(t, New([]byte("wor"), New([]byte("ld"), db)))
test(t, New([]byte("ld"), New([]byte("wor"), db)))
test(t, NewNested([]byte("wor"), New([]byte("ld"), db)))
test(t, NewNested([]byte("ld"), New([]byte("wor"), db)))
}
}

551
database/test_database.go Normal file
View File

@ -0,0 +1,551 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package database
import (
"bytes"
"testing"
)
var (
// Tests is a list of all database tests
Tests = []func(t *testing.T, db Database){
TestSimpleKeyValue,
TestSimpleKeyValueClosed,
TestBatchPut,
TestBatchDelete,
TestBatchReset,
TestBatchReplay,
TestIterator,
TestIteratorStart,
TestIteratorPrefix,
TestIteratorStartPrefix,
TestIteratorClosed,
TestStatNoPanic,
TestCompactNoPanic,
}
)
// TestSimpleKeyValue ...
func TestSimpleKeyValue(t *testing.T, db Database) {
key := []byte("hello")
value := []byte("world")
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)
} else if v, err := db.Get(key); err != ErrNotFound {
t.Fatalf("Expected %s on db.Get for missing key %s. Returned 0x%x", ErrNotFound, key, v)
} else if err := db.Delete(key); err != nil {
t.Fatalf("Unexpected error on db.Delete: %s", err)
}
if err := db.Put(key, value); err != nil {
t.Fatalf("Unexpected error on db.Put: %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)
}
if err := db.Delete(key); err != nil {
t.Fatalf("Unexpected error on db.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)
} else if v, err := db.Get(key); err != ErrNotFound {
t.Fatalf("Expected %s on db.Get for missing key %s. Returned 0x%x", ErrNotFound, key, v)
} else if err := db.Delete(key); err != nil {
t.Fatalf("Unexpected error on db.Delete: %s", err)
}
}
// TestSimpleKeyValueClosed ...
func TestSimpleKeyValueClosed(t *testing.T, db Database) {
key := []byte("hello")
value := []byte("world")
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)
} else if v, err := db.Get(key); err != ErrNotFound {
t.Fatalf("Expected %s on db.Get for missing key %s. Returned 0x%x", ErrNotFound, key, v)
} else if err := db.Delete(key); err != nil {
t.Fatalf("Unexpected error on db.Delete: %s", err)
}
if err := db.Put(key, value); err != nil {
t.Fatalf("Unexpected error on db.Put: %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)
}
if err := db.Close(); err != nil {
t.Fatalf("Unexpected error on db.Close: %s", err)
}
if _, err := db.Has(key); err != ErrClosed {
t.Fatalf("Expected %s on db.Has after close", ErrClosed)
} else if _, err := db.Get(key); err != ErrClosed {
t.Fatalf("Expected %s on db.Get after close", ErrClosed)
} else if err := db.Put(key, value); err != ErrClosed {
t.Fatalf("Expected %s on db.Put after close", ErrClosed)
} else if err := db.Delete(key); err != ErrClosed {
t.Fatalf("Expected %s on db.Delete after close", ErrClosed)
} else if err := db.Close(); err != ErrClosed {
t.Fatalf("Expected %s on db.Close after close", ErrClosed)
}
}
// TestBatchPut ...
func TestBatchPut(t *testing.T, db Database) {
key := []byte("hello")
value := []byte("world")
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)
} else if size := batch.ValueSize(); size <= 0 {
t.Fatalf("batch.ValueSize: Returned: %d ; Expected: > 0", size)
}
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)
}
if err := db.Delete(key); err != nil {
t.Fatalf("Unexpected error on db.Delete: %s", err)
}
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)
}
db.Close()
if err := batch.Write(); err != ErrClosed {
t.Fatalf("Expected %s on batch.Write", ErrClosed)
}
}
// TestBatchDelete ...
func TestBatchDelete(t *testing.T, db Database) {
key := []byte("hello")
value := []byte("world")
if err := db.Put(key, value); err != nil {
t.Fatalf("Unexpected error on db.Put: %s", err)
}
batch := db.NewBatch()
if batch == nil {
t.Fatalf("db.NewBatch returned nil")
}
if err := batch.Delete(key); err != nil {
t.Fatalf("Unexpected error on batch.Delete: %s", err)
}
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 true on key %s", key)
} else if v, err := db.Get(key); err != ErrNotFound {
t.Fatalf("Expected %s on db.Get for missing key %s. Returned 0x%x", ErrNotFound, key, v)
} else if err := db.Delete(key); err != nil {
t.Fatalf("Unexpected error on db.Delete: %s", err)
}
}
// TestBatchReset ...
func TestBatchReset(t *testing.T, db Database) {
key := []byte("hello")
value := []byte("world")
if err := db.Put(key, value); err != nil {
t.Fatalf("Unexpected error on db.Put: %s", err)
}
batch := db.NewBatch()
if batch == nil {
t.Fatalf("db.NewBatch returned nil")
}
if err := batch.Delete(key); err != nil {
t.Fatalf("Unexpected error on batch.Delete: %s", err)
}
batch.Reset()
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 ...
func TestBatchReplay(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)
} else if err := batch.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
secondBatch := db.NewBatch()
if secondBatch == nil {
t.Fatalf("db.NewBatch returned nil")
}
if err := batch.Replay(secondBatch); err != nil {
t.Fatalf("Unexpected error on batch.Replay: %s", err)
}
if err := secondBatch.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 false on key %s", key1)
} else if v, err := db.Get(key1); err != nil {
t.Fatalf("Unexpected error on db.Get: %s", err)
} else if !bytes.Equal(value1, v) {
t.Fatalf("db.Get: Returned: 0x%x ; Expected: 0x%x", v, value1)
}
thirdBatch := db.NewBatch()
if thirdBatch == nil {
t.Fatalf("db.NewBatch returned nil")
}
if err := thirdBatch.Delete(key1); err != nil {
t.Fatalf("Unexpected error on batch.Delete: %s", err)
} else if err := thirdBatch.Delete(key2); err != nil {
t.Fatalf("Unexpected error on batch.Delete: %s", err)
}
if err := db.Close(); err != nil {
t.Fatalf("Unexpected error on db.Close: %s", err)
}
if err := batch.Replay(db); err != ErrClosed {
t.Fatalf("Expected %s on batch.Replay", ErrClosed)
} else if err := thirdBatch.Replay(db); err != ErrClosed {
t.Fatalf("Expected %s on batch.Replay", ErrClosed)
}
}
// TestIterator ...
func TestIterator(t *testing.T, db Database) {
key1 := []byte("hello1")
value1 := []byte("world1")
key2 := []byte("hello2")
value2 := []byte("world2")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
iterator := db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key1) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key1)
} else if value := iterator.Value(); !bytes.Equal(value, value1) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key2) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key2)
} else if value := iterator.Value(); !bytes.Equal(value, value2) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value2)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
}
// TestIteratorStart ...
func TestIteratorStart(t *testing.T, db Database) {
key1 := []byte("hello1")
value1 := []byte("world1")
key2 := []byte("hello2")
value2 := []byte("world2")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
iterator := db.NewIteratorWithStart(key2)
if iterator == nil {
t.Fatalf("db.NewIteratorWithStart returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key2) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key2)
} else if value := iterator.Value(); !bytes.Equal(value, value2) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value2)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
}
// TestIteratorPrefix ...
func TestIteratorPrefix(t *testing.T, db Database) {
key1 := []byte("hello")
value1 := []byte("world1")
key2 := []byte("goodbye")
value2 := []byte("world2")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
iterator := db.NewIteratorWithPrefix([]byte("h"))
if iterator == nil {
t.Fatalf("db.NewIteratorWithPrefix returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key1) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key1)
} else if value := iterator.Value(); !bytes.Equal(value, value1) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
}
// TestIteratorStartPrefix ...
func TestIteratorStartPrefix(t *testing.T, db Database) {
key1 := []byte("hello1")
value1 := []byte("world1")
key2 := []byte("z")
value2 := []byte("world2")
key3 := []byte("hello3")
value3 := []byte("world3")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key3, value3); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
iterator := db.NewIteratorWithStartAndPrefix(key1, []byte("h"))
if iterator == nil {
t.Fatalf("db.NewIteratorWithStartAndPrefix returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key1) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key1)
} else if value := iterator.Value(); !bytes.Equal(value, value1) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key3) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key3)
} else if value := iterator.Value(); !bytes.Equal(value, value3) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value3)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
}
// TestIteratorClosed ...
func TestIteratorClosed(t *testing.T, db Database) {
key1 := []byte("hello1")
value1 := []byte("world1")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
if err := db.Close(); err != nil {
t.Fatalf("Unexpected error on db.Close: %s", err)
}
iterator := db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != ErrClosed {
t.Fatalf("Expected %s on iterator.Error", ErrClosed)
}
}
// TestStatNoPanic ...
func TestStatNoPanic(t *testing.T, db Database) {
key1 := []byte("hello1")
value1 := []byte("world1")
key2 := []byte("z")
value2 := []byte("world2")
key3 := []byte("hello3")
value3 := []byte("world3")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key3, value3); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
db.Stat("")
if err := db.Close(); err != nil {
t.Fatalf("Unexpected error on db.Close: %s", err)
}
db.Stat("")
}
// TestCompactNoPanic ...
func TestCompactNoPanic(t *testing.T, db Database) {
key1 := []byte("hello1")
value1 := []byte("world1")
key2 := []byte("z")
value2 := []byte("world2")
key3 := []byte("hello3")
value3 := []byte("world3")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key3, value3); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
db.Compact(nil, nil)
if err := db.Close(); err != nil {
t.Fatalf("Unexpected error on db.Close: %s", err)
}
db.Compact(nil, nil)
}

View File

@ -0,0 +1,393 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package versiondb
import (
"sort"
"strings"
"sync"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/memdb"
"github.com/ava-labs/gecko/database/nodb"
)
// Database implements the Database interface by living on top of another
// database, writing changes to the underlying database only when commit is
// called.
type Database struct {
lock sync.RWMutex
mem map[string]valueDelete
db database.Database
}
type valueDelete struct {
value []byte
delete bool
}
// New returns a new prefixed database
func New(db database.Database) *Database {
return &Database{
mem: make(map[string]valueDelete, memdb.DefaultSize),
db: db,
}
}
// Has implements the database.Database interface
func (db *Database) Has(key []byte) (bool, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.mem == nil {
return false, database.ErrClosed
}
if val, has := db.mem[string(key)]; has {
return !val.delete, nil
}
return db.db.Has(key)
}
// Get implements the database.Database interface
func (db *Database) Get(key []byte) ([]byte, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.mem == nil {
return nil, database.ErrClosed
}
if val, has := db.mem[string(key)]; has {
if val.delete {
return nil, database.ErrNotFound
}
return copyBytes(val.value), nil
}
return db.db.Get(key)
}
// Put implements the database.Database interface
func (db *Database) Put(key, value []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.mem == nil {
return database.ErrClosed
}
db.mem[string(key)] = valueDelete{value: value}
return nil
}
// Delete implements the database.Database interface
func (db *Database) Delete(key []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.mem == nil {
return database.ErrClosed
}
db.mem[string(key)] = valueDelete{delete: true}
return nil
}
// NewBatch implements the database.Database interface
func (db *Database) NewBatch() database.Batch { return &batch{db: db} }
// NewIterator implements the database.Database interface
func (db *Database) NewIterator() database.Iterator { return db.NewIteratorWithStartAndPrefix(nil, nil) }
// NewIteratorWithStart implements the database.Database interface
func (db *Database) NewIteratorWithStart(start []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(start, nil)
}
// NewIteratorWithPrefix implements the database.Database interface
func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator {
return db.NewIteratorWithStartAndPrefix(nil, prefix)
}
// NewIteratorWithStartAndPrefix implements the database.Database interface
func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator {
db.lock.RLock()
defer db.lock.RUnlock()
if db.mem == nil {
return &nodb.Iterator{Err: database.ErrClosed}
}
startString := string(start)
prefixString := string(prefix)
keys := make([]string, 0, len(db.mem))
for key := range db.mem {
if strings.HasPrefix(key, prefixString) && key >= startString {
keys = append(keys, key)
}
}
sort.Strings(keys) // Keys need to be in sorted order
values := make([]valueDelete, 0, len(keys))
for _, key := range keys {
values = append(values, db.mem[key])
}
return &iterator{
Iterator: db.db.NewIteratorWithStartAndPrefix(start, prefix),
keys: keys,
values: values,
}
}
// Stat implements the database.Database interface
func (db *Database) Stat(stat string) (string, error) {
db.lock.RLock()
defer db.lock.RUnlock()
if db.mem == nil {
return "", database.ErrClosed
}
return db.db.Stat(stat)
}
// Compact implements the database.Database interface
func (db *Database) Compact(start, limit []byte) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.mem == nil {
return database.ErrClosed
}
return db.db.Compact(start, limit)
}
// SetDatabase changes the underlying database to the specified database
func (db *Database) SetDatabase(newDB database.Database) error {
db.lock.Lock()
defer db.lock.Unlock()
if db.mem == nil {
return database.ErrClosed
}
db.db = newDB
return nil
}
// GetDatabase returns the underlying database
func (db *Database) GetDatabase() database.Database {
db.lock.RLock()
defer db.lock.RUnlock()
return db.db
}
// Commit writes all the operations of this database to the underlying database
func (db *Database) Commit() error {
db.lock.Lock()
defer db.lock.Unlock()
if db.mem == nil {
return database.ErrClosed
}
if len(db.mem) == 0 {
return nil
}
batch := db.db.NewBatch()
for key, value := range db.mem {
if value.delete {
if err := batch.Delete([]byte(key)); err != nil {
return err
}
} else if err := batch.Put([]byte(key), value.value); err != nil {
return err
}
}
if err := batch.Write(); err != nil {
return err
}
db.mem = make(map[string]valueDelete, memdb.DefaultSize)
return nil
}
// Close implements the database.Database interface
func (db *Database) Close() error {
db.lock.Lock()
defer db.lock.Unlock()
if db.mem == nil {
return database.ErrClosed
}
db.mem = nil
db.db = nil
return nil
}
type keyValue struct {
key []byte
value []byte
delete bool
}
type batch struct {
db *Database
writes []keyValue
size int
}
// Put implements the Database interface
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
}
// Delete implements the Database interface
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
b.size++
return nil
}
// ValueSize implements the Database interface
func (b *batch) ValueSize() int { return b.size }
// Write implements the Database interface
func (b *batch) Write() error {
b.db.lock.Lock()
defer b.db.lock.Unlock()
if b.db.mem == nil {
return database.ErrClosed
}
for _, kv := range b.writes {
b.db.mem[string(kv.key)] = valueDelete{
value: kv.value,
delete: kv.delete,
}
}
return nil
}
// Reset implements the Database interface
func (b *batch) Reset() {
b.writes = b.writes[:0]
b.size = 0
}
// Replay implements the Database interface
func (b *batch) Replay(w database.KeyValueWriter) error {
for _, kv := range b.writes {
if kv.delete {
if err := w.Delete(kv.key); err != nil {
return err
}
} else if err := w.Put(kv.key, kv.value); err != nil {
return err
}
}
return nil
}
// iterator walks over both the in memory database and the underlying database
// at the same time.
type iterator struct {
database.Iterator
key, value []byte
keys []string
values []valueDelete
initialized, exhausted bool
}
// Next moves the iterator to the next key/value pair. It returns whether the
// iterator is exhausted. We must pay careful attention to set the proper values
// based on if the in memory db or the underlying db should be read next
func (it *iterator) Next() bool {
if !it.initialized {
it.exhausted = !it.Iterator.Next()
it.initialized = true
}
for {
switch {
case it.exhausted && len(it.keys) == 0:
it.key = nil
it.value = nil
return false
case it.exhausted:
nextKey := it.keys[0]
nextValue := it.values[0]
it.keys = it.keys[1:]
it.values = it.values[1:]
if !nextValue.delete {
it.key = []byte(nextKey)
it.value = nextValue.value
return true
}
case len(it.keys) == 0:
it.key = it.Iterator.Key()
it.value = it.Iterator.Value()
it.exhausted = !it.Iterator.Next()
return true
default:
memKey := it.keys[0]
memValue := it.values[0]
dbKey := it.Iterator.Key()
dbStringKey := string(dbKey)
switch {
case memKey < dbStringKey:
it.keys = it.keys[1:]
it.values = it.values[1:]
if !memValue.delete {
it.key = []byte(memKey)
it.value = memValue.value
return true
}
case dbStringKey < memKey:
it.key = dbKey
it.value = it.Iterator.Value()
it.exhausted = !it.Iterator.Next()
return true
default:
it.keys = it.keys[1:]
it.values = it.values[1:]
it.exhausted = !it.Iterator.Next()
if !memValue.delete {
it.key = []byte(memKey)
it.value = memValue.value
return true
}
}
}
}
}
// Key implements the Iterator interface
func (it *iterator) Key() []byte { return it.key }
// Value implements the Iterator interface
func (it *iterator) Value() []byte { return it.value }
// Release implements the Iterator interface
func (it *iterator) Release() {
it.key = nil
it.value = nil
it.keys = nil
it.values = nil
it.Iterator.Release()
}
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}

View File

@ -0,0 +1,299 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package versiondb
import (
"bytes"
"testing"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/memdb"
)
func TestInterface(t *testing.T) {
for _, test := range database.Tests {
baseDB := memdb.New()
test(t, New(baseDB))
}
}
func TestIterate(t *testing.T) {
baseDB := memdb.New()
db := New(baseDB)
key1 := []byte("hello1")
value1 := []byte("world1")
key2 := []byte("z")
value2 := []byte("world2")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on db.Put: %s", err)
}
if err := db.Commit(); err != nil {
t.Fatalf("Unexpected error on db.Commit: %s", err)
}
iterator := db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key1) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key1)
} else if value := iterator.Value(); !bytes.Equal(value, value1) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on database.Put: %s", err)
}
iterator = db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key1) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key1)
} else if value := iterator.Value(); !bytes.Equal(value, value1) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key2) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key2)
} else if value := iterator.Value(); !bytes.Equal(value, value2) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value2)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
if err := db.Delete(key1); err != nil {
t.Fatalf("Unexpected error on database.Delete: %s", err)
}
iterator = db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key2) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key2)
} else if value := iterator.Value(); !bytes.Equal(value, value2) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value2)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
if err := db.Commit(); err != nil {
t.Fatalf("Unexpected error on database.Commit: %s", err)
} else if err := db.Put(key2, value1); err != nil {
t.Fatalf("Unexpected error on database.Put: %s", err)
}
iterator = db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key2) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key2)
} else if value := iterator.Value(); !bytes.Equal(value, value1) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
if err := db.Commit(); err != nil {
t.Fatalf("Unexpected error on database.Commit: %s", err)
} else if err := db.Put(key1, value2); err != nil {
t.Fatalf("Unexpected error on database.Put: %s", err)
}
iterator = db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key1) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key1)
} else if value := iterator.Value(); !bytes.Equal(value, value2) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value2)
} else if !iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", false, true)
} else if key := iterator.Key(); !bytes.Equal(key, key2) {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: 0x%x", key, key2)
} else if value := iterator.Value(); !bytes.Equal(value, value1) {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if iterator.Next() {
t.Fatalf("iterator.Next Returned: %v ; Expected: %v", true, false)
} else if key := iterator.Key(); key != nil {
t.Fatalf("iterator.Key Returned: 0x%x ; Expected: nil", key)
} else if value := iterator.Value(); value != nil {
t.Fatalf("iterator.Value Returned: 0x%x ; Expected: nil", value)
} else if err := iterator.Error(); err != nil {
t.Fatalf("iterator.Error Returned: %s ; Expected: nil", err)
}
}
func TestCommit(t *testing.T) {
baseDB := memdb.New()
db := New(baseDB)
if err := db.Commit(); err != nil {
t.Fatalf("Unexpected error on db.Commit: %s", err)
}
key1 := []byte("hello1")
value1 := []byte("world1")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on db.Put: %s", err)
}
if err := db.Commit(); err != nil {
t.Fatalf("Unexpected error on db.Commit: %s", err)
}
if value, err := db.Get(key1); err != nil {
t.Fatalf("Unexpected error on db.Get: %s", err)
} else if !bytes.Equal(value, value1) {
t.Fatalf("db.Get Returned: 0x%x ; Expected: 0x%x", value, value1)
} else if value, err := baseDB.Get(key1); err != nil {
t.Fatalf("Unexpected error on db.Get: %s", err)
} else if !bytes.Equal(value, value1) {
t.Fatalf("db.Get Returned: 0x%x ; Expected: 0x%x", value, value1)
}
}
func TestCommitClosed(t *testing.T) {
baseDB := memdb.New()
db := New(baseDB)
key1 := []byte("hello1")
value1 := []byte("world1")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on db.Put: %s", err)
} else if err := db.Close(); err != nil {
t.Fatalf("Unexpected error on db.Close: %s", err)
} else if err := db.Commit(); err != database.ErrClosed {
t.Fatalf("Expected %s on db.Commit", database.ErrClosed)
}
}
func TestCommitClosedWrite(t *testing.T) {
baseDB := memdb.New()
db := New(baseDB)
key1 := []byte("hello1")
value1 := []byte("world1")
baseDB.Close()
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on db.Put: %s", err)
} else if err := db.Commit(); err != database.ErrClosed {
t.Fatalf("Expected %s on db.Commit", database.ErrClosed)
}
}
func TestCommitClosedDelete(t *testing.T) {
baseDB := memdb.New()
db := New(baseDB)
key1 := []byte("hello1")
baseDB.Close()
if err := db.Delete(key1); err != nil {
t.Fatalf("Unexpected error on db.Delete: %s", err)
} else if err := db.Commit(); err != database.ErrClosed {
t.Fatalf("Expected %s on db.Commit", database.ErrClosed)
}
}
func TestSetDatabase(t *testing.T) {
baseDB := memdb.New()
newDB := memdb.New()
db := New(baseDB)
key1 := []byte("hello1")
value1 := []byte("world1")
if err := db.SetDatabase(newDB); err != nil {
t.Fatalf("Unexpected error on db.SetDatabase: %s", err)
}
if db.GetDatabase() != newDB {
t.Fatalf("Unexpected database from db.GetDatabase")
} else if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on db.Put: %s", err)
} else if err := db.Commit(); err != nil {
t.Fatalf("Unexpected error on db.Commit: %s", err)
} else if has, err := baseDB.Has(key1); err != nil {
t.Fatalf("Unexpected error on db.Has: %s", err)
} else if has {
t.Fatalf("db.Has Returned: %v ; Expected: %v", has, false)
} else if has, err := newDB.Has(key1); err != nil {
t.Fatalf("Unexpected error on db.Has: %s", err)
} else if !has {
t.Fatalf("db.Has Returned: %v ; Expected: %v", has, true)
}
}
func TestSetDatabaseClosed(t *testing.T) {
baseDB := memdb.New()
db := New(baseDB)
if err := db.Close(); err != nil {
t.Fatalf("Unexpected error on db.Close: %s", err)
} else if err := db.SetDatabase(memdb.New()); err != database.ErrClosed {
t.Fatalf("Expected %s on db.SetDatabase", database.ErrClosed)
} else if db.GetDatabase() != nil {
t.Fatalf("Unexpected database from db.GetDatabase")
}
}

513
genesis/genesis.go Normal file
View File

@ -0,0 +1,513 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package genesis
// TODO: Move this to a separate repo and leave only a byte array
import (
"fmt"
"math"
"regexp"
"strconv"
"strings"
"github.com/ava-labs/gecko/ids"
"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/spchainvm"
"github.com/ava-labs/gecko/vms/spdagvm"
"github.com/ava-labs/gecko/vms/timestampvm"
)
// Note that 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, etc.), defining the genesis
// state of the Platform Chain is the same as defining the genesis
// state of the network.
// Hardcoded network IDs
const (
MainnetID uint32 = 1
TestnetID uint32 = 2
BorealisID uint32 = 2
LocalID uint32 = 12345
MainnetName = "mainnet"
TestnetName = "testnet"
BorealisName = "borealis"
LocalName = "local"
)
var (
validNetworkName = regexp.MustCompile(`network-[0-9]+`)
)
// Hard coded genesis constants
var (
// Give special names to the mainnet and testnet
NetworkIDToNetworkName = map[uint32]string{
MainnetID: MainnetName,
TestnetID: BorealisName,
LocalID: LocalName,
}
NetworkNameToNetworkID = map[string]uint32{
MainnetName: MainnetID,
TestnetName: TestnetID,
BorealisName: BorealisID,
LocalName: LocalID,
}
Keys = []string{
"ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN",
}
Addresses = []string{
"6Y3kysjF9jnHnYkdS9yGAuoHyae2eNmeV",
}
ParsedAddresses = []ids.ShortID{}
StakerIDs = []string{
"7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg",
"MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ",
"NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN",
"GWPcbFJZFfZreETSoWjPimr846mXEKCtu",
"P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
}
ParsedStakerIDs = []ids.ShortID{}
)
func init() {
for _, addrStr := range Addresses {
addr, err := ids.ShortFromString(addrStr)
if err != nil {
panic(err)
}
ParsedAddresses = append(ParsedAddresses, addr)
}
for _, stakerIDStr := range StakerIDs {
stakerID, err := ids.ShortFromString(stakerIDStr)
if err != nil {
panic(err)
}
ParsedStakerIDs = append(ParsedStakerIDs, stakerID)
}
}
// NetworkName returns a human readable name for the network with
// ID [networkID]
func NetworkName(networkID uint32) string {
if name, exists := NetworkIDToNetworkName[networkID]; exists {
return name
}
return fmt.Sprintf("network-%d", networkID)
}
// NetworkID returns the ID of the network with name [networkName]
func NetworkID(networkName string) (uint32, error) {
networkName = strings.ToLower(networkName)
if id, exists := NetworkNameToNetworkID[networkName]; exists {
return id, nil
}
if id, err := strconv.ParseUint(networkName, 10, 0); err == nil {
if id > math.MaxUint32 {
return 0, fmt.Errorf("NetworkID %s not in [0, 2^32)", networkName)
}
return uint32(id), nil
}
if validNetworkName.MatchString(networkName) {
if id, err := strconv.Atoi(networkName[8:]); err == nil {
if id > math.MaxUint32 {
return 0, fmt.Errorf("NetworkID %s not in [0, 2^32)", networkName)
}
return uint32(id), nil
}
}
return 0, fmt.Errorf("Failed to parse %s as a network name", networkName)
}
// Aliases returns the default aliases based on the network ID
func Aliases(networkID uint32) (generalAliases map[string][]string, chainAliases map[[32]byte][]string, vmAliases map[[32]byte][]string) {
generalAliases = map[string][]string{
"vm/" + platformvm.ID.String(): []string{"vm/platform"},
"vm/" + avm.ID.String(): []string{"vm/avm"},
"vm/" + evm.ID.String(): []string{"vm/evm"},
"vm/" + spdagvm.ID.String(): []string{"vm/spdag"},
"vm/" + spchainvm.ID.String(): []string{"vm/spchain"},
"vm/" + timestampvm.ID.String(): []string{"vm/timestamp"},
"bc/" + ids.Empty.String(): []string{"P", "platform", "bc/P", "bc/platform"},
}
chainAliases = map[[32]byte][]string{
ids.Empty.Key(): []string{"P", "platform"},
}
vmAliases = map[[32]byte][]string{
platformvm.ID.Key(): []string{"platform"},
avm.ID.Key(): []string{"avm"},
evm.ID.Key(): []string{"evm"},
spdagvm.ID.Key(): []string{"spdag"},
spchainvm.ID.Key(): []string{"spchain"},
timestampvm.ID.Key(): []string{"timestamp"},
}
genesisBytes := Genesis(networkID)
genesis := &platformvm.Genesis{} // TODO let's not re-create genesis to do aliasing
platformvm.Codec.Unmarshal(genesisBytes, genesis) // TODO check for error
genesis.Initialize()
for _, chain := range genesis.Chains {
switch {
case avm.ID.Equals(chain.VMID):
generalAliases["bc/"+chain.ID().String()] = []string{"X", "avm", "bc/X", "bc/avm"}
chainAliases[chain.ID().Key()] = []string{"X", "avm"}
case evm.ID.Equals(chain.VMID):
generalAliases["bc/"+chain.ID().String()] = []string{"C", "evm", "bc/C", "bc/evm"}
chainAliases[chain.ID().Key()] = []string{"C", "evm"}
case spdagvm.ID.Equals(chain.VMID):
generalAliases["bc/"+chain.ID().String()] = []string{"bc/spdag"}
chainAliases[chain.ID().Key()] = []string{"spdag"}
case spchainvm.ID.Equals(chain.VMID):
generalAliases["bc/"+chain.ID().String()] = []string{"bc/spchain"}
chainAliases[chain.ID().Key()] = []string{"spchain"}
case timestampvm.ID.Equals(chain.VMID):
generalAliases["bc/"+chain.ID().String()] = []string{"bc/timestamp"}
chainAliases[chain.ID().Key()] = []string{"timestamp"}
}
}
return
}
// Genesis returns the genesis data of the Platform Chain.
// Since the Platform Chain causes the creation of all other
// chains, this function returns the genesis data of the entire network.
// The ID of the new network is [networkID].
func Genesis(networkID uint32) []byte {
if networkID != LocalID {
panic("unknown network ID provided")
}
return []byte{
0x00, 0x00, 0x00, 0x01, 0x3c, 0xb7, 0xd3, 0x84,
0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1,
0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x05, 0xde, 0x31, 0xb4, 0xd8, 0xb2, 0x29, 0x91,
0xd5, 0x1a, 0xa6, 0xaa, 0x1f, 0xc7, 0x33, 0xf2,
0x3a, 0x85, 0x1a, 0x8c, 0x94, 0x00, 0x00, 0x12,
0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x5d, 0xbb, 0x75, 0x80, 0x00, 0x00, 0x00,
0x00, 0x5f, 0x9c, 0xa9, 0x00, 0x00, 0x00, 0x30,
0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xaa, 0x18,
0xd3, 0x99, 0x1c, 0xf6, 0x37, 0xaa, 0x6c, 0x16,
0x2f, 0x5e, 0x95, 0xcf, 0x16, 0x3f, 0x69, 0xcd,
0x82, 0x91, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbb,
0x75, 0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9c,
0xa9, 0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb7,
0xd3, 0x84, 0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd,
0x09, 0xf1, 0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1,
0xb2, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x05, 0xe9, 0x09, 0x4f, 0x73, 0x69,
0x80, 0x02, 0xfd, 0x52, 0xc9, 0x08, 0x19, 0xb4,
0x57, 0xb9, 0xfb, 0xc8, 0x66, 0xab, 0x80, 0x00,
0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x5d, 0xbb, 0x75, 0x80, 0x00,
0x00, 0x00, 0x00, 0x5f, 0x9c, 0xa9, 0x00, 0x00,
0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0xb7, 0xd3, 0x84, 0x2e,
0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe,
0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0x47, 0x9f, 0x66, 0xc8, 0xbe, 0x89, 0x58, 0x30,
0x54, 0x7e, 0x70, 0xb4, 0xb2, 0x98, 0xca, 0xfd,
0x43, 0x3d, 0xba, 0x6e, 0x00, 0x00, 0x12, 0x30,
0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5d, 0xbb, 0x75, 0x80, 0x00, 0x00, 0x00, 0x00,
0x5f, 0x9c, 0xa9, 0x00, 0x00, 0x00, 0x30, 0x39,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee, 0x6a,
0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f, 0x68,
0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0xf2, 0x9b, 0xce,
0x5f, 0x34, 0xa7, 0x43, 0x01, 0xeb, 0x0d, 0xe7,
0x16, 0xd5, 0x19, 0x4e, 0x4a, 0x4a, 0xea, 0x5d,
0x7a, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbb, 0x75,
0x80, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x9c, 0xa9,
0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xb7, 0xd3,
0x84, 0x2e, 0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09,
0xf1, 0xfe, 0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2,
0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x41, 0x56, 0x4d, 0x61, 0x76, 0x6d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x73,
0x65, 0x63, 0x70, 0x32, 0x35, 0x36, 0x6b, 0x31,
0x66, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x01, 0x00,
0x03, 0x41, 0x56, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x41, 0x56, 0x41, 0x00, 0x03, 0x41,
0x56, 0x41, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x04, 0x00, 0x9f, 0xdf, 0x42, 0xf6,
0xe4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e,
0x8c, 0xee, 0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe,
0x88, 0x4f, 0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x41, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x65, 0x76,
0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0xc9, 0x7b, 0x22,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x3a,
0x7b, 0x22, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49,
0x64, 0x22, 0x3a, 0x34, 0x33, 0x31, 0x31, 0x30,
0x2c, 0x22, 0x68, 0x6f, 0x6d, 0x65, 0x73, 0x74,
0x65, 0x61, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x22, 0x3a, 0x30, 0x2c, 0x22, 0x64, 0x61, 0x6f,
0x46, 0x6f, 0x72, 0x6b, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x64, 0x61,
0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x75, 0x70,
0x70, 0x6f, 0x72, 0x74, 0x22, 0x3a, 0x74, 0x72,
0x75, 0x65, 0x2c, 0x22, 0x65, 0x69, 0x70, 0x31,
0x35, 0x30, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22,
0x3a, 0x30, 0x2c, 0x22, 0x65, 0x69, 0x70, 0x31,
0x35, 0x30, 0x48, 0x61, 0x73, 0x68, 0x22, 0x3a,
0x22, 0x30, 0x78, 0x32, 0x30, 0x38, 0x36, 0x37,
0x39, 0x39, 0x61, 0x65, 0x65, 0x62, 0x65, 0x61,
0x65, 0x31, 0x33, 0x35, 0x63, 0x32, 0x34, 0x36,
0x63, 0x36, 0x35, 0x30, 0x32, 0x31, 0x63, 0x38,
0x32, 0x62, 0x34, 0x65, 0x31, 0x35, 0x61, 0x32,
0x63, 0x34, 0x35, 0x31, 0x33, 0x34, 0x30, 0x39,
0x39, 0x33, 0x61, 0x61, 0x63, 0x66, 0x64, 0x32,
0x37, 0x35, 0x31, 0x38, 0x38, 0x36, 0x35, 0x31,
0x34, 0x66, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x69,
0x70, 0x31, 0x35, 0x35, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x65, 0x69,
0x70, 0x31, 0x35, 0x38, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x62, 0x79,
0x7a, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x3a, 0x30, 0x2c,
0x22, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e,
0x74, 0x69, 0x6e, 0x6f, 0x70, 0x6c, 0x65, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x3a, 0x30, 0x2c,
0x22, 0x70, 0x65, 0x74, 0x65, 0x72, 0x73, 0x62,
0x75, 0x72, 0x67, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x22, 0x3a, 0x30, 0x7d, 0x2c, 0x22, 0x6e, 0x6f,
0x6e, 0x63, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x78,
0x30, 0x22, 0x2c, 0x22, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x78,
0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x22,
0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x22, 0x2c,
0x22, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69,
0x74, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x35, 0x66,
0x35, 0x65, 0x31, 0x30, 0x30, 0x22, 0x2c, 0x22,
0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c,
0x74, 0x79, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x22, 0x2c, 0x22, 0x6d, 0x69, 0x78, 0x48, 0x61,
0x73, 0x68, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22,
0x2c, 0x22, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61,
0x73, 0x65, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22,
0x2c, 0x22, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x22,
0x3a, 0x7b, 0x22, 0x37, 0x35, 0x31, 0x61, 0x30,
0x62, 0x39, 0x36, 0x65, 0x31, 0x30, 0x34, 0x32,
0x62, 0x65, 0x65, 0x37, 0x38, 0x39, 0x34, 0x35,
0x32, 0x65, 0x63, 0x62, 0x32, 0x30, 0x32, 0x35,
0x33, 0x66, 0x62, 0x61, 0x34, 0x30, 0x64, 0x62,
0x65, 0x38, 0x35, 0x22, 0x3a, 0x7b, 0x22, 0x62,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x3a,
0x22, 0x30, 0x78, 0x33, 0x33, 0x62, 0x32, 0x65,
0x33, 0x63, 0x39, 0x66, 0x64, 0x30, 0x38, 0x30,
0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x22, 0x7d, 0x7d, 0x2c, 0x22, 0x6e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x67, 0x61,
0x73, 0x55, 0x73, 0x65, 0x64, 0x22, 0x3a, 0x22,
0x30, 0x78, 0x30, 0x22, 0x2c, 0x22, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68,
0x22, 0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x7d, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x53, 0x69,
0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x41, 0x47,
0x20, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74,
0x73, 0x73, 0x70, 0x64, 0x61, 0x67, 0x76, 0x6d,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x12, 0x30, 0x9c, 0xe5, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15,
0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43,
0x68, 0x61, 0x69, 0x6e, 0x20, 0x50, 0x61, 0x79,
0x6d, 0x65, 0x6e, 0x74, 0x73, 0x73, 0x70, 0x63,
0x68, 0x61, 0x69, 0x6e, 0x76, 0x6d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x01, 0x3c, 0xb7, 0xd3, 0x84, 0x2e, 0x8c, 0xee,
0x6a, 0x0e, 0xbd, 0x09, 0xf1, 0xfe, 0x88, 0x4f,
0x68, 0x61, 0xe1, 0xb2, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
0x30, 0x9c, 0xe5, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x17, 0x53, 0x69, 0x6d, 0x70,
0x6c, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5d, 0xbb, 0x75, 0x80,
}
}
// VMGenesis ...
func VMGenesis(networkID uint32, vmID ids.ID) *platformvm.CreateChainTx {
genesisBytes := Genesis(networkID)
genesis := platformvm.Genesis{}
platformvm.Codec.Unmarshal(genesisBytes, &genesis)
for _, chain := range genesis.Chains {
if chain.VMID.Equals(vmID) {
return chain
}
}
return nil
}

114
genesis/genesis_test.go Normal file
View File

@ -0,0 +1,114 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package genesis
import (
"testing"
"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/spchainvm"
"github.com/ava-labs/gecko/vms/spdagvm"
)
func TestNetworkName(t *testing.T) {
if name := NetworkName(MainnetID); name != MainnetName {
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, MainnetName)
}
if name := NetworkName(TestnetID); name != BorealisName {
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, BorealisName)
}
if name := NetworkName(BorealisID); name != BorealisName {
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, BorealisName)
}
if name := NetworkName(4294967295); name != "network-4294967295" {
t.Fatalf("NetworkID was incorrectly named. Result: %s ; Expected: %s", name, "network-4294967295")
}
}
func TestNetworkID(t *testing.T) {
id, err := NetworkID(MainnetName)
if err != nil {
t.Fatal(err)
}
if id != MainnetID {
t.Fatalf("Returned wrong network. Expected: %d ; Returned %d", MainnetID, id)
}
id, err = NetworkID(TestnetName)
if err != nil {
t.Fatal(err)
}
if id != TestnetID {
t.Fatalf("Returned wrong network. Expected: %d ; Returned %d", TestnetID, id)
}
id, err = NetworkID(BorealisName)
if err != nil {
t.Fatal(err)
}
if id != TestnetID {
t.Fatalf("Returned wrong network. Expected: %d ; Returned %d", TestnetID, id)
}
id, err = NetworkID("bOrEaLiS")
if err != nil {
t.Fatal(err)
}
if id != TestnetID {
t.Fatalf("Returned wrong network. Expected: %d ; Returned %d", TestnetID, id)
}
id, err = NetworkID("network-4294967295")
if err != nil {
t.Fatal(err)
}
if id != 4294967295 {
t.Fatalf("Returned wrong network. Expected: %d ; Returned %d", 4294967295, id)
}
id, err = NetworkID("4294967295")
if err != nil {
t.Fatal(err)
}
if id != 4294967295 {
t.Fatalf("Returned wrong network. Expected: %d ; Returned %d", 4294967295, id)
}
if _, err := NetworkID("network-4294967296"); err == nil {
t.Fatalf("Should have errored due to the network being too large.")
}
if _, err := NetworkID("4294967296"); err == nil {
t.Fatalf("Should have errored due to the network being too large.")
}
if _, err := NetworkID("asdcvasdc-252"); err == nil {
t.Fatalf("Should have errored due to the invalid input string.")
}
}
func TestAliases(t *testing.T) {
generalAliases, _, _ := Aliases(LocalID)
if _, exists := generalAliases["vm/"+platformvm.ID.String()]; !exists {
t.Fatalf("Should have a custom alias from the vm")
} else if _, exists := generalAliases["vm/"+avm.ID.String()]; !exists {
t.Fatalf("Should have a custom alias from the vm")
} else if _, exists := generalAliases["vm/"+evm.ID.String()]; !exists {
t.Fatalf("Should have a custom alias from the vm")
} else if _, exists := generalAliases["vm/"+spdagvm.ID.String()]; !exists {
t.Fatalf("Should have a custom alias from the vm")
} else if _, exists := generalAliases["vm/"+spchainvm.ID.String()]; !exists {
t.Fatalf("Should have a custom alias from the vm")
}
}
func TestGenesis(t *testing.T) {
genesisBytes := Genesis(LocalID)
genesis := platformvm.Genesis{}
if err := platformvm.Codec.Unmarshal(genesisBytes, &genesis); err != nil {
t.Fatal(err)
}
}

54
ids/aliases.go Normal file
View File

@ -0,0 +1,54 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"fmt"
)
// Aliaser allows one to give an ID aliases and lookup the aliases given to an
// ID. An ID can have arbitrarily many aliases; two IDs may not have the same
// alias.
type Aliaser struct {
dealias map[string]ID
aliases map[[32]byte][]string
}
// Initialize the aliaser to have no aliases
func (a *Aliaser) Initialize() {
a.dealias = make(map[string]ID)
a.aliases = make(map[[32]byte][]string)
}
// Lookup returns the ID associated with alias
func (a *Aliaser) Lookup(alias string) (ID, error) {
if ID, ok := a.dealias[alias]; ok {
return ID, nil
}
return ID{}, fmt.Errorf("there is no ID with alias %s", alias)
}
// Aliases returns the aliases of an ID
func (a Aliaser) Aliases(id ID) []string { return a.aliases[id.Key()] }
// PrimaryAlias returns the first alias of [id]
func (a Aliaser) PrimaryAlias(id ID) (string, error) {
aliases, exists := a.aliases[id.Key()]
if !exists || len(aliases) == 0 {
return "", fmt.Errorf("there is no alias for ID %s", id)
}
return aliases[0], nil
}
// Alias gives [id] the alias [alias]
func (a Aliaser) Alias(id ID, alias string) error {
if _, exists := a.dealias[alias]; exists {
return fmt.Errorf("%s is already used as an alias for an ID", alias)
}
key := id.Key()
a.dealias[alias] = id
a.aliases[key] = append(a.aliases[key], alias)
return nil
}

134
ids/bag.go Normal file
View File

@ -0,0 +1,134 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"fmt"
"strings"
)
// Bag is a multiset of IDs.
//
// A bag has the ability to split and filter on it's bits for ease of use for
// binary voting.
type Bag struct {
counts map[[32]byte]int
size int
mode ID
modeFreq int
threshold int
metThreshold Set
}
func (b *Bag) init() {
if b.counts == nil {
b.counts = make(map[[32]byte]int)
}
}
// SetThreshold sets the number of times an ID must be added to be contained in
// the threshold set.
func (b *Bag) SetThreshold(threshold int) {
if b.threshold == threshold {
return
}
b.threshold = threshold
b.metThreshold.Clear()
for vote, count := range b.counts {
if count >= threshold {
b.metThreshold.Add(NewID(vote))
}
}
}
// Add increases the number of times each id has been seen by one.
func (b *Bag) Add(ids ...ID) {
for _, id := range ids {
b.AddCount(id, 1)
}
}
// AddCount increases the nubmer of times the id has been seen by count.
//
// count must be >= 1
func (b *Bag) AddCount(id ID, count int) {
b.init()
totalCount := b.counts[*id.ID] + count
b.counts[*id.ID] = totalCount
b.size += count
if totalCount > b.modeFreq {
b.mode = id
b.modeFreq = totalCount
}
if totalCount >= b.threshold {
b.metThreshold.Add(id)
}
}
// Count returns the number of times the id has been added.
func (b *Bag) Count(id ID) int { return b.counts[*id.ID] }
// Len returns the number of times an id has been added.
func (b *Bag) Len() int { return b.size }
// List returns a list of all ids that have been added.
func (b *Bag) List() []ID {
idList := []ID(nil)
for id := range b.counts {
idList = append(idList, NewID(id))
}
return idList
}
// Mode returns the id that has been seen the most and the number of times it
// has been seen. Ties are broken by the first id to be seen the reported number
// of times.
func (b *Bag) Mode() (ID, int) { return b.mode, b.modeFreq }
// Threshold returns the ids that have been seen at least threshold times.
func (b *Bag) Threshold() Set { return b.metThreshold }
// Filter returns the bag of ids with the same counts as this bag, except all
// the ids in the returned bag must have the same bits in the range [start, end]
// as id.
func (b *Bag) Filter(start, end int, id ID) Bag {
newBag := Bag{}
for vote, count := range b.counts {
voteID := NewID(vote)
if EqualSubset(start, end, id, voteID) {
newBag.AddCount(voteID, count)
}
}
return newBag
}
// Split returns the bags of ids with the same counts a this bag, except all ids
// in the 0th index have a 0 at bit [index], and all ids in the 1st index have a
// 1 at bit [index].
func (b *Bag) Split(index uint) [2]Bag {
splitVotes := [2]Bag{}
for vote, count := range b.counts {
voteID := NewID(vote)
bit := voteID.Bit(index)
splitVotes[bit].AddCount(voteID, count)
}
return splitVotes
}
func (b *Bag) String() string {
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf("Bag: (Size = %d)", b.Len()))
for idBytes, count := range b.counts {
id := NewID(idBytes)
sb.WriteString(fmt.Sprintf("\n ID[%s]: Count = %d", id, count))
}
return sb.String()
}

201
ids/bag_test.go Normal file
View File

@ -0,0 +1,201 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"testing"
)
func TestBagAdd(t *testing.T) {
id0 := Empty
id1 := NewID([32]byte{1})
bag := Bag{}
if count := bag.Count(id0); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if count := bag.Count(id1); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if size := bag.Len(); size != 0 {
t.Fatalf("Bag.Len returned %d expected %d", count, 0)
} else if list := bag.List(); list != nil {
t.Fatalf("Bag.List returned %v expected %v", list, nil)
} else if mode, freq := bag.Mode(); !mode.IsZero() {
t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, ID{})
} else if freq != 0 {
t.Fatalf("Bag.Mode[1] returned %d expected %d", freq, 0)
} else if threshold := bag.Threshold(); threshold.Len() != 0 {
t.Fatalf("Bag.Threshold returned %s expected %s", threshold, Set{})
}
bag.Add(id0)
if count := bag.Count(id0); count != 1 {
t.Fatalf("Bag.Count returned %d expected %d", count, 1)
} else if count := bag.Count(id1); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if size := bag.Len(); size != 1 {
t.Fatalf("Bag.Len returned %d expected %d", count, 1)
} else if list := bag.List(); len(list) != 1 {
t.Fatalf("Bag.List returned %d expected %d", len(list), 1)
} else if mode, freq := bag.Mode(); !mode.Equals(id0) {
t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, id0)
} else if freq != 1 {
t.Fatalf("Bag.Mode[1] returned %d expected %d", freq, 1)
} else if threshold := bag.Threshold(); threshold.Len() != 1 {
t.Fatalf("Bag.Threshold returned %d expected %d", len(threshold), 1)
}
bag.Add(id0)
if count := bag.Count(id0); count != 2 {
t.Fatalf("Bag.Count returned %d expected %d", count, 2)
} else if count := bag.Count(id1); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if size := bag.Len(); size != 2 {
t.Fatalf("Bag.Len returned %d expected %d", count, 2)
} else if list := bag.List(); len(list) != 1 {
t.Fatalf("Bag.List returned %d expected %d", len(list), 1)
} else if mode, freq := bag.Mode(); !mode.Equals(id0) {
t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, id0)
} else if freq != 2 {
t.Fatalf("Bag.Mode[1] returned %d expected %d", freq, 2)
} else if threshold := bag.Threshold(); threshold.Len() != 1 {
t.Fatalf("Bag.Threshold returned %d expected %d", len(threshold), 1)
}
bag.AddCount(id1, 3)
if count := bag.Count(id0); count != 2 {
t.Fatalf("Bag.Count returned %d expected %d", count, 2)
} else if count := bag.Count(id1); count != 3 {
t.Fatalf("Bag.Count returned %d expected %d", count, 3)
} else if size := bag.Len(); size != 5 {
t.Fatalf("Bag.Len returned %d expected %d", count, 5)
} else if list := bag.List(); len(list) != 2 {
t.Fatalf("Bag.List returned %d expected %d", len(list), 2)
} else if mode, freq := bag.Mode(); !mode.Equals(id1) {
t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, id1)
} else if freq != 3 {
t.Fatalf("Bag.Mode[1] returned %d expected %d", freq, 3)
} else if threshold := bag.Threshold(); threshold.Len() != 2 {
t.Fatalf("Bag.Threshold returned %d expected %d", len(threshold), 2)
}
}
func TestBagSetThreshold(t *testing.T) {
id0 := Empty
id1 := NewID([32]byte{1})
bag := Bag{}
bag.AddCount(id0, 2)
bag.AddCount(id1, 3)
bag.SetThreshold(0)
if count := bag.Count(id0); count != 2 {
t.Fatalf("Bag.Count returned %d expected %d", count, 2)
} else if count := bag.Count(id1); count != 3 {
t.Fatalf("Bag.Count returned %d expected %d", count, 3)
} else if size := bag.Len(); size != 5 {
t.Fatalf("Bag.Len returned %d expected %d", count, 5)
} else if list := bag.List(); len(list) != 2 {
t.Fatalf("Bag.List returned %d expected %d", len(list), 2)
} else if mode, freq := bag.Mode(); !mode.Equals(id1) {
t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, id1)
} else if freq != 3 {
t.Fatalf("Bag.Mode[1] returned %d expected %d", freq, 3)
} else if threshold := bag.Threshold(); threshold.Len() != 2 {
t.Fatalf("Bag.Threshold returned %d expected %d", len(threshold), 2)
}
bag.SetThreshold(3)
if count := bag.Count(id0); count != 2 {
t.Fatalf("Bag.Count returned %d expected %d", count, 2)
} else if count := bag.Count(id1); count != 3 {
t.Fatalf("Bag.Count returned %d expected %d", count, 3)
} else if size := bag.Len(); size != 5 {
t.Fatalf("Bag.Len returned %d expected %d", count, 5)
} else if list := bag.List(); len(list) != 2 {
t.Fatalf("Bag.List returned %d expected %d", len(list), 2)
} else if mode, freq := bag.Mode(); !mode.Equals(id1) {
t.Fatalf("Bag.Mode[0] returned %s expected %s", mode, id1)
} else if freq != 3 {
t.Fatalf("Bag.Mode[1] returned %d expected %d", freq, 3)
} else if threshold := bag.Threshold(); threshold.Len() != 1 {
t.Fatalf("Bag.Threshold returned %d expected %d", len(threshold), 1)
} else if !threshold.Contains(id1) {
t.Fatalf("Bag.Threshold doesn't contain %s", id1)
}
}
func TestBagFilter(t *testing.T) {
id0 := Empty
id1 := NewID([32]byte{1})
id2 := NewID([32]byte{2})
bag := Bag{}
bag.AddCount(id0, 1)
bag.AddCount(id1, 3)
bag.AddCount(id2, 5)
even := bag.Filter(0, 1, id0)
if count := even.Count(id0); count != 1 {
t.Fatalf("Bag.Count returned %d expected %d", count, 1)
} else if count := even.Count(id1); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if count := even.Count(id2); count != 5 {
t.Fatalf("Bag.Count returned %d expected %d", count, 5)
}
}
func TestBagSplit(t *testing.T) {
id0 := Empty
id1 := NewID([32]byte{1})
id2 := NewID([32]byte{2})
bag := Bag{}
bag.AddCount(id0, 1)
bag.AddCount(id1, 3)
bag.AddCount(id2, 5)
bags := bag.Split(0)
evens := bags[0]
odds := bags[1]
if count := evens.Count(id0); count != 1 {
t.Fatalf("Bag.Count returned %d expected %d", count, 1)
} else if count := evens.Count(id1); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if count := evens.Count(id2); count != 5 {
t.Fatalf("Bag.Count returned %d expected %d", count, 5)
} else if count := odds.Count(id0); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
} else if count := odds.Count(id1); count != 3 {
t.Fatalf("Bag.Count returned %d expected %d", count, 3)
} else if count := odds.Count(id2); count != 0 {
t.Fatalf("Bag.Count returned %d expected %d", count, 0)
}
}
func TestBagString(t *testing.T) {
id0 := Empty
bag := Bag{}
bag.AddCount(id0, 1337)
expected := "Bag: (Size = 1337)\n" +
" ID[11111111111111111111111111111111LpoYY]: Count = 1337"
if bagString := bag.String(); bagString != expected {
t.Fatalf("Bag.String:\nReturned:\n%s\nExpected:\n%s", bagString, expected)
}
}

39
ids/bit_set.go Normal file
View File

@ -0,0 +1,39 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"fmt"
"math/bits"
)
// BitSet is a set that can contain uints in the range [0, 64). All functions
// are O(1). The zero value is the empty set.
type BitSet uint64
// Add [i] to the set of ints
func (bs *BitSet) Add(i uint) { *bs |= 1 << i }
// Union adds all the elements in [s] to this set
func (bs *BitSet) Union(s BitSet) { *bs |= s }
// Intersection takes the intersection of [s] with this set
func (bs *BitSet) Intersection(s BitSet) { *bs &= s }
// Difference removes all the elements in [s] from this set
func (bs *BitSet) Difference(s BitSet) { *bs &^= s }
// Remove [i] from the set of ints
func (bs *BitSet) Remove(i uint) { *bs &^= 1 << i }
// Clear removes all elements from this set
func (bs *BitSet) Clear() { *bs = 0 }
// Contains returns true if [i] was previously added to this set
func (bs BitSet) Contains(i uint) bool { return bs&(1<<i) != 0 }
// Len returns the number of elements in this set
func (bs BitSet) Len() int { return bits.OnesCount64(uint64(bs)) }
func (bs BitSet) String() string { return fmt.Sprintf("%016x", uint64(bs)) }

157
ids/bit_set_test.go Normal file
View File

@ -0,0 +1,157 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import "testing"
func TestBitSet(t *testing.T) {
var bs1 BitSet
if bs1.Len() != 0 {
t.Fatalf("Empty set's len should be 0")
}
bs1.Add(5)
if bs1.Len() != 1 {
t.Fatalf("Wrong set length")
} else if !bs1.Contains(5) {
t.Fatalf("Set should contain element")
}
bs1.Add(10)
if bs1.Len() != 2 {
t.Fatalf("Wrong set length")
} else if !bs1.Contains(5) {
t.Fatalf("Set should contain element")
} else if !bs1.Contains(10) {
t.Fatalf("Set should contain element")
}
bs1.Add(10)
if bs1.Len() != 2 {
t.Fatalf("Wrong set length")
} else if !bs1.Contains(5) {
t.Fatalf("Set should contain element")
} else if !bs1.Contains(10) {
t.Fatalf("Set should contain element")
}
var bs2 BitSet
bs2.Add(0)
if bs2.Len() != 1 {
t.Fatalf("Wrong set length")
} else if !bs2.Contains(0) {
t.Fatalf("Set should contain element")
}
bs2.Union(bs1)
if bs1.Len() != 2 {
t.Fatalf("Wrong set length")
} else if !bs1.Contains(5) {
t.Fatalf("Set should contain element")
} else if !bs1.Contains(10) {
t.Fatalf("Set should contain element")
} else if bs2.Len() != 3 {
t.Fatalf("Wrong set length")
} else if !bs2.Contains(0) {
t.Fatalf("Set should contain element")
} else if !bs2.Contains(5) {
t.Fatalf("Set should contain element")
} else if !bs2.Contains(10) {
t.Fatalf("Set should contain element")
}
bs1.Clear()
if bs1.Len() != 0 {
t.Fatalf("Wrong set length")
} else if bs2.Len() != 3 {
t.Fatalf("Wrong set length")
} else if !bs2.Contains(0) {
t.Fatalf("Set should contain element")
} else if !bs2.Contains(5) {
t.Fatalf("Set should contain element")
} else if !bs2.Contains(10) {
t.Fatalf("Set should contain element")
}
bs1.Add(63)
if bs1.Len() != 1 {
t.Fatalf("Wrong set length")
} else if !bs1.Contains(63) {
t.Fatalf("Set should contain element")
}
bs1.Add(1)
if bs1.Len() != 2 {
t.Fatalf("Wrong set length")
} else if !bs1.Contains(1) {
t.Fatalf("Set should contain element")
} else if !bs1.Contains(63) {
t.Fatalf("Set should contain element")
}
bs1.Remove(63)
if bs1.Len() != 1 {
t.Fatalf("Wrong set length")
} else if !bs1.Contains(1) {
t.Fatalf("Set should contain element")
}
var bs3 BitSet
bs3.Add(0)
bs3.Add(2)
bs3.Add(5)
var bs4 BitSet
bs4.Add(2)
bs4.Add(5)
bs3.Intersection(bs4)
if bs3.Len() != 2 {
t.Fatalf("Wrong set length")
} else if !bs3.Contains(2) {
t.Fatalf("Set should contain element")
} else if !bs3.Contains(5) {
t.Fatalf("Set should contain element")
} else if bs4.Len() != 2 {
t.Fatalf("Wrong set length")
}
var bs5 BitSet
bs5.Add(7)
bs5.Add(11)
bs5.Add(9)
var bs6 BitSet
bs6.Add(9)
bs6.Add(11)
bs5.Difference(bs6)
if bs5.Len() != 1 {
t.Fatalf("Wrong set length")
} else if !bs5.Contains(7) {
t.Fatalf("Set should contain element")
} else if bs6.Len() != 2 {
t.Fatalf("Wrong set length")
}
}
func TestBitSetString(t *testing.T) {
var bs BitSet
bs.Add(17)
expected := "0000000000020000"
if bsString := bs.String(); bsString != expected {
t.Fatalf("BitSet.String returned %s expected %s", bsString, expected)
}
}

134
ids/bits.go Normal file
View File

@ -0,0 +1,134 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"bytes"
"math/bits"
)
// NumBits is the number of bits this patricia tree manages
const NumBits = 256
// BitsPerByte is the number of bits per byte
const BitsPerByte = 8
// EqualSubset takes in two indices and two ids and returns if the ids are
// equal from bit start to bit end (non-inclusive). Bit indices are defined as:
// [7 6 5 4 3 2 1 0] [15 14 13 12 11 10 9 8] ... [255 254 253 252 251 250 249 248]
// Where index 7 is the MSB of byte 0.
func EqualSubset(start, stop int, id1, id2 ID) bool {
stop--
if start > stop || stop < 0 {
return true
}
if stop >= NumBits {
return false
}
id1Bytes := id1.Bytes()
id2Bytes := id2.Bytes()
startIndex := start / BitsPerByte
stopIndex := stop / BitsPerByte
// If there is a series of bytes between the first byte and the last byte, they must be equal
if startIndex+1 < stopIndex && !bytes.Equal(id1Bytes[startIndex+1:stopIndex], id2Bytes[startIndex+1:stopIndex]) {
return false
}
startBit := uint(start % BitsPerByte) // Index in the byte that the first bit is at
stopBit := uint(stop % BitsPerByte) // Index in the byte that the last bit is at
startMask := -1 << startBit // 111...0... The number of 0s is equal to startBit
stopMask := (1 << (stopBit + 1)) - 1 // 000...1... The number of 1s is equal to stopBit+1
if startIndex == stopIndex {
// If we are looking at the same byte, both masks need to be applied
mask := startMask & stopMask
// The index here could be startIndex or stopIndex, as they are equal
b1 := mask & int(id1Bytes[startIndex])
b2 := mask & int(id2Bytes[startIndex])
return b1 == b2
}
start1 := startMask & int(id1Bytes[startIndex])
start2 := startMask & int(id2Bytes[startIndex])
stop1 := stopMask & int(id1Bytes[stopIndex])
stop2 := stopMask & int(id2Bytes[stopIndex])
return start1 == start2 && stop1 == stop2
}
// FirstDifferenceSubset takes in two indices and two ids and returns the index
// of the first difference between the ids inside bit start to bit end
// (non-inclusive). Bit indices are defined above
func FirstDifferenceSubset(start, stop int, id1, id2 ID) (int, bool) {
stop--
if start > stop || stop < 0 || stop >= NumBits {
return 0, false
}
id1Bytes := id1.Bytes()
id2Bytes := id2.Bytes()
startIndex := start / BitsPerByte
stopIndex := stop / BitsPerByte
startBit := uint(start % BitsPerByte) // Index in the byte that the first bit is at
stopBit := uint(stop % BitsPerByte) // Index in the byte that the last bit is at
startMask := -1 << startBit // 111...0... The number of 0s is equal to startBit
stopMask := (1 << (stopBit + 1)) - 1 // 000...1... The number of 1s is equal to stopBit+1
if startIndex == stopIndex {
// If we are looking at the same byte, both masks need to be applied
mask := startMask & stopMask
// The index here could be startIndex or stopIndex, as they are equal
b1 := mask & int(id1Bytes[startIndex])
b2 := mask & int(id2Bytes[startIndex])
if b1 == b2 {
return 0, false
}
bd := b1 ^ b2
return bits.TrailingZeros8(uint8(bd)) + startIndex*BitsPerByte, true
}
// Check the first byte, may have some bits masked
start1 := startMask & int(id1Bytes[startIndex])
start2 := startMask & int(id2Bytes[startIndex])
if start1 != start2 {
bd := start1 ^ start2
return bits.TrailingZeros8(uint8(bd)) + startIndex*BitsPerByte, true
}
// Check all the interior bits
for i := startIndex + 1; i < stopIndex; i++ {
b1 := int(id1Bytes[i])
b2 := int(id2Bytes[i])
if b1 != b2 {
bd := b1 ^ b2
return bits.TrailingZeros8(uint8(bd)) + i*BitsPerByte, true
}
}
// Check the last byte, may have some bits masked
stop1 := stopMask & int(id1Bytes[stopIndex])
stop2 := stopMask & int(id2Bytes[stopIndex])
if stop1 != stop2 {
bd := stop1 ^ stop2
return bits.TrailingZeros8(uint8(bd)) + stopIndex*BitsPerByte, true
}
// No difference was found
return 0, false
}

181
ids/bits_test.go Normal file
View File

@ -0,0 +1,181 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"fmt"
"math"
"strings"
"testing"
"github.com/ava-labs/gecko/utils/random"
)
func flip(b uint8) uint8 {
b = b>>4 | b<<4
b = (b&0xCC)>>2 | (b&0x33)<<2
b = (b&0xAA)>>1 | (b&0x55)<<1
return b
}
func BitString(id ID) string {
sb := strings.Builder{}
for _, b := range id.Bytes() {
sb.WriteString(fmt.Sprintf("%08b", flip(b)))
}
return sb.String()
}
func Check(start, stop int, id1, id2 ID) bool {
s1 := BitString(id1)
s2 := BitString(id2)
shorts1 := s1[start:stop]
shorts2 := s2[start:stop]
return shorts1 == shorts2
}
func TestEqualSubsetEarlyStop(t *testing.T) {
id1 := NewID([32]byte{0xf0, 0x0f})
id2 := NewID([32]byte{0xf0, 0x1f})
if !EqualSubset(0, 12, id1, id2) {
t.Fatalf("Should have passed: %08b %08b == %08b %08b", id1.Bytes()[0], id1.Bytes()[1], id2.Bytes()[0], id2.Bytes()[1])
} else if EqualSubset(0, 13, id1, id2) {
t.Fatalf("Should not have passed: %08b %08b == %08b %08b", id1.Bytes()[0], id1.Bytes()[1], id2.Bytes()[0], id2.Bytes()[1])
}
}
func TestEqualSubsetLateStart(t *testing.T) {
id1 := NewID([32]byte{0x1f, 0xf8})
id2 := NewID([32]byte{0x10, 0x08})
if !EqualSubset(4, 12, id1, id2) {
t.Fatalf("Should have passed: %08b %08b == %08b %08b", id1.Bytes()[0], id1.Bytes()[1], id2.Bytes()[0], id2.Bytes()[1])
}
}
func TestEqualSubsetSameByte(t *testing.T) {
id1 := NewID([32]byte{0x18})
id2 := NewID([32]byte{0xfc})
if !EqualSubset(3, 5, id1, id2) {
t.Fatalf("Should have passed: %08b == %08b", id1.Bytes()[0], id2.Bytes()[0])
}
}
func TestEqualSubsetBadMiddle(t *testing.T) {
id1 := NewID([32]byte{0x18, 0xe8, 0x55})
id2 := NewID([32]byte{0x18, 0x8e, 0x55})
if EqualSubset(0, 8*3, id1, id2) {
t.Fatalf("Should not have passed: %08b == %08b", id1.Bytes()[1], id2.Bytes()[1])
}
}
func TestEqualSubsetAll3Bytes(t *testing.T) {
seed := random.Rand(0, math.MaxInt64)
id1 := NewID([32]byte{}).Prefix(uint64(seed))
bytes1 := id1.Bytes()
for i := 0; i < BitsPerByte; i++ {
for j := i; j < BitsPerByte; j++ {
for k := j; k < BitsPerByte; k++ {
id2 := NewID([32]byte{uint8(i), uint8(j), uint8(k)})
bytes2 := id2.Bytes()
for start := 0; start < BitsPerByte*3; start++ {
for end := start; end <= BitsPerByte*3; end++ {
if EqualSubset(start, end, id1, id2) != Check(start, end, id1, id2) {
t.Fatalf("Subset failed on seed %d:\ns = %d\ne = %d\n%08b %08b %08b == %08b %08b %08b",
seed, start, end,
bytes1[0], bytes1[1], bytes1[2],
bytes2[0], bytes2[1], bytes2[2])
}
}
}
}
}
}
}
func TestEqualSubsetOutOfBounds(t *testing.T) {
id1 := NewID([32]byte{0x18, 0xe8, 0x55})
id2 := NewID([32]byte{0x18, 0x8e, 0x55})
if EqualSubset(0, math.MaxInt32, id1, id2) {
t.Fatalf("Should not have passed")
}
}
func TestFirstDifferenceSubsetEarlyStop(t *testing.T) {
id1 := NewID([32]byte{0xf0, 0x0f})
id2 := NewID([32]byte{0xf0, 0x1f})
if _, found := FirstDifferenceSubset(0, 12, id1, id2); found {
t.Fatalf("Shouldn't have found a difference: %08b %08b == %08b %08b", id1.Bytes()[0], id1.Bytes()[1], id2.Bytes()[0], id2.Bytes()[1])
} else if index, found := FirstDifferenceSubset(0, 13, id1, id2); !found {
t.Fatalf("Should have found a difference: %08b %08b == %08b %08b", id1.Bytes()[0], id1.Bytes()[1], id2.Bytes()[0], id2.Bytes()[1])
} else if index != 12 {
t.Fatalf("Found a difference at index %d expected %d: %08b %08b == %08b %08b", index, 12, id1.Bytes()[0], id1.Bytes()[1], id2.Bytes()[0], id2.Bytes()[1])
}
}
func TestFirstDifferenceEqualByte4(t *testing.T) {
id1 := NewID([32]byte{0x10})
id2 := NewID([32]byte{0x00})
if _, found := FirstDifferenceSubset(0, 4, id1, id2); found {
t.Fatalf("Shouldn't have found a difference: %08b == %08b", id1.Bytes()[0], id2.Bytes()[0])
} else if index, found := FirstDifferenceSubset(0, 5, id1, id2); !found {
t.Fatalf("Should have found a difference: %08b == %08b", id1.Bytes()[0], id2.Bytes()[0])
} else if index != 4 {
t.Fatalf("Found a difference at index %d expected %d: %08b == %08b", index, 4, id1.Bytes()[0], id2.Bytes()[0])
}
}
func TestFirstDifferenceEqualByte5(t *testing.T) {
id1 := NewID([32]byte{0x20})
id2 := NewID([32]byte{0x00})
if _, found := FirstDifferenceSubset(0, 5, id1, id2); found {
t.Fatalf("Shouldn't have found a difference: %08b == %08b", id1.Bytes()[0], id2.Bytes()[0])
} else if index, found := FirstDifferenceSubset(0, 6, id1, id2); !found {
t.Fatalf("Should have found a difference: %08b == %08b", id1.Bytes()[0], id2.Bytes()[0])
} else if index != 5 {
t.Fatalf("Found a difference at index %d expected %d: %08b == %08b", index, 5, id1.Bytes()[0], id2.Bytes()[0])
}
}
func TestFirstDifferenceSubsetMiddle(t *testing.T) {
id1 := NewID([32]byte{0xf0, 0x0f, 0x11})
id2 := NewID([32]byte{0xf0, 0x1f, 0xff})
if index, found := FirstDifferenceSubset(0, 24, id1, id2); !found {
t.Fatalf("Should have found a difference: %08b %08b %08b == %08b %08b %08b", id1.Bytes()[0], id1.Bytes()[1], id1.Bytes()[2], id2.Bytes()[0], id2.Bytes()[1], id2.Bytes()[2])
} else if index != 12 {
t.Fatalf("Found a difference at index %d expected %d: %08b %08b %08b == %08b %08b %08b", index, 12, id1.Bytes()[0], id1.Bytes()[1], id1.Bytes()[2], id2.Bytes()[0], id2.Bytes()[1], id2.Bytes()[2])
}
}
func TestFirstDifferenceStartMiddle(t *testing.T) {
id1 := NewID([32]byte{0x1f, 0x0f, 0x11})
id2 := NewID([32]byte{0x0f, 0x1f, 0xff})
if index, found := FirstDifferenceSubset(0, 24, id1, id2); !found {
t.Fatalf("Should have found a difference: %08b %08b %08b == %08b %08b %08b", id1.Bytes()[0], id1.Bytes()[1], id1.Bytes()[2], id2.Bytes()[0], id2.Bytes()[1], id2.Bytes()[2])
} else if index != 4 {
t.Fatalf("Found a difference at index %d expected %d: %08b %08b %08b == %08b %08b %08b", index, 4, id1.Bytes()[0], id1.Bytes()[1], id1.Bytes()[2], id2.Bytes()[0], id2.Bytes()[1], id2.Bytes()[2])
}
}
func TestFirstDifferenceVacuous(t *testing.T) {
id1 := NewID([32]byte{0xf0, 0x0f, 0x11})
id2 := NewID([32]byte{0xf0, 0x1f, 0xff})
if _, found := FirstDifferenceSubset(0, 0, id1, id2); found {
t.Fatalf("Shouldn't have found a difference")
}
}

155
ids/id.go Normal file
View File

@ -0,0 +1,155 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"bytes"
"encoding/hex"
"sort"
"github.com/ava-labs/gecko/utils"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/utils/wrappers"
)
// Empty is a useful all zero value
var Empty = ID{ID: &[32]byte{}}
// ID wraps a 32 byte hash as an identifier
// Internal field [ID] should never be modified
// from outside ids package
type ID struct {
ID *[32]byte `serialize:"true"`
}
// NewID creates an identifer from a 32 byte hash
func NewID(id [32]byte) ID { return ID{ID: &id} }
// ToID attempt to convert a byte slice into an id
func ToID(bytes []byte) (ID, error) {
addrHash, err := hashing.ToHash256(bytes)
return NewID(addrHash), err
}
// FromString is the inverse of ID.String()
func FromString(idStr string) (ID, error) {
cb58 := formatting.CB58{}
err := cb58.FromString(idStr)
if err != nil {
return ID{}, err
}
return ToID(cb58.Bytes)
}
// MarshalJSON ...
func (id ID) MarshalJSON() ([]byte, error) {
if id.IsZero() {
return []byte("null"), nil
}
cb58 := formatting.CB58{Bytes: id.ID[:]}
return cb58.MarshalJSON()
}
// UnmarshalJSON ...
func (id *ID) UnmarshalJSON(b []byte) error {
if string(b) == "null" {
return nil
}
cb58 := formatting.CB58{}
if err := cb58.UnmarshalJSON(b); err != nil {
return err
}
newID, err := ToID(cb58.Bytes)
if err != nil {
return err
}
*id = newID
return nil
}
// IsZero returns true if the value has not been initialized
func (id ID) IsZero() bool { return id.ID == nil }
// Key returns a 32 byte hash that this id represents. This is useful to allow
// for this id to be used as keys in maps.
func (id ID) Key() [32]byte { return *id.ID }
// Prefix this id to create a more selective id. This can be used to store
// multiple values under the same key. For example:
// prefix1(id) -> confidence
// prefix2(id) -> vertex
// This will return a new id and not modify the original id.
func (id ID) Prefix(prefixes ...uint64) ID {
packer := wrappers.Packer{
Bytes: make([]byte, len(prefixes)*wrappers.LongLen+hashing.HashLen),
}
for _, prefix := range prefixes {
packer.PackLong(prefix)
}
packer.PackFixedBytes(id.Bytes())
return NewID(hashing.ComputeHash256Array(packer.Bytes))
}
// Equals returns true if the ids have the same byte representation
func (id ID) Equals(oID ID) bool {
return id.ID == oID.ID ||
(id.ID != nil && oID.ID != nil && bytes.Equal(id.Bytes(), oID.Bytes()))
}
// Bytes returns the 32 byte hash as a slice. It is assumed this slice is not
// modified.
func (id ID) Bytes() []byte { return id.ID[:] }
// Bit returns the bit value at the ith index of the byte array. Returns 0 or 1
func (id ID) Bit(i uint) int {
byteIndex := i / BitsPerByte
bitIndex := i % BitsPerByte
bytes := id.Bytes()
b := bytes[byteIndex]
// b = [7, 6, 5, 4, 3, 2, 1, 0]
b = b >> bitIndex
// b = [0, ..., bitIndex + 1, bitIndex]
// 1 = [0, 0, 0, 0, 0, 0, 0, 1]
b = b & 1
// b = [0, 0, 0, 0, 0, 0, 0, bitIndex]
return int(b)
}
// Hex returns a hex encoded string of this id.
func (id ID) Hex() string { return hex.EncodeToString(id.Bytes()) }
func (id ID) String() string {
if id.IsZero() {
return "nil"
}
bytes := id.Bytes()
cb58 := formatting.CB58{Bytes: bytes}
return cb58.String()
}
type sortIDData []ID
func (ids sortIDData) Less(i, j int) bool {
return bytes.Compare(
ids[i].Bytes(),
ids[j].Bytes()) == -1
}
func (ids sortIDData) Len() int { return len(ids) }
func (ids sortIDData) Swap(i, j int) { ids[j], ids[i] = ids[i], ids[j] }
// SortIDs sorts the ids lexicographically
func SortIDs(ids []ID) { sort.Sort(sortIDData(ids)) }
// IsSortedAndUniqueIDs returns true if the ids are sorted and unique
func IsSortedAndUniqueIDs(ids []ID) bool { return utils.IsSortedAndUnique(sortIDData(ids)) }

81
ids/id_test.go Normal file
View File

@ -0,0 +1,81 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"bytes"
"testing"
)
func TestID(t *testing.T) {
hash := [32]byte{24}
id := NewID(hash)
if key := id.Key(); !bytes.Equal(hash[:], key[:]) {
t.Fatalf("ID.Key returned wrong bytes")
}
prefixed := id.Prefix(0)
if key := id.Key(); !bytes.Equal(hash[:], key[:]) {
t.Fatalf("ID.Prefix mutated the ID")
}
if nextPrefix := id.Prefix(0); !prefixed.Equals(nextPrefix) {
t.Fatalf("ID.Prefix not consistant")
}
if b := id.Bytes(); !bytes.Equal(hash[:], b) {
t.Fatalf("ID.Bytes returned wrong bytes")
}
if str := id.String(); str != "Ba3mm8Ra8JYYebeZ9p7zw1ayorDbeD1euwxhgzSLsncKqGoNt" {
t.Fatalf("ID.String returned wrong string: %s", str)
}
}
func TestIDBit(t *testing.T) {
id0 := NewID([32]byte{1 << 0})
id1 := NewID([32]byte{1 << 1})
id2 := NewID([32]byte{1 << 2})
id3 := NewID([32]byte{1 << 3})
id4 := NewID([32]byte{1 << 4})
id5 := NewID([32]byte{1 << 5})
id6 := NewID([32]byte{1 << 6})
id7 := NewID([32]byte{1 << 7})
id8 := NewID([32]byte{0, 1 << 0})
if id0.Bit(0) != 1 {
t.Fatalf("Wrong bit")
} else if id1.Bit(1) != 1 {
t.Fatalf("Wrong bit")
} else if id2.Bit(2) != 1 {
t.Fatalf("Wrong bit")
} else if id3.Bit(3) != 1 {
t.Fatalf("Wrong bit")
} else if id4.Bit(4) != 1 {
t.Fatalf("Wrong bit")
} else if id5.Bit(5) != 1 {
t.Fatalf("Wrong bit")
} else if id6.Bit(6) != 1 {
t.Fatalf("Wrong bit")
} else if id7.Bit(7) != 1 {
t.Fatalf("Wrong bit")
} else if id8.Bit(8) != 1 {
t.Fatalf("Wrong bit")
}
}
func TestFromString(t *testing.T) {
key := [32]byte{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}
id := NewID(key)
idStr := id.String()
id2, err := FromString(idStr)
if err != nil {
t.Fatal(err)
}
if id.Key() != id2.Key() {
t.Fatal("Expected FromString to be inverse of String but it wasn't")
}
}

52
ids/queue.go Normal file
View File

@ -0,0 +1,52 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"container/list"
)
// QueueSet is a set of IDs stored in fifo order
type QueueSet struct {
idList *list.List
}
func (qs *QueueSet) init() {
if qs.idList == nil {
qs.idList = list.New()
}
}
// SetHead ...
func (qs *QueueSet) SetHead(id ID) {
qs.init()
for qs.idList.Len() > 0 {
element := qs.idList.Front()
head := element.Value.(ID)
if head.Equals(id) {
return
}
qs.idList.Remove(element)
}
qs.idList.PushFront(id)
}
// Append ...
func (qs *QueueSet) Append(id ID) {
qs.init()
qs.idList.PushBack(id)
}
// GetTail ...
func (qs *QueueSet) GetTail() ID {
qs.init()
if qs.idList.Len() == 0 {
return ID{}
}
return qs.idList.Back().Value.(ID)
}

107
ids/set.go Normal file
View File

@ -0,0 +1,107 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"strings"
)
// Set is a set of IDs
type Set map[[32]byte]bool
func (ids *Set) init(size int) {
if *ids == nil {
*ids = make(map[[32]byte]bool, size)
}
}
// Add all the ids to this set, if the id is already in the set, nothing happens
func (ids *Set) Add(idList ...ID) {
ids.init(2 * len(idList))
for _, id := range idList {
(*ids)[*id.ID] = true
}
}
// Union adds all the ids from the provided sets to this set.
func (ids *Set) Union(set Set) {
ids.init(2 * set.Len())
for id := range set {
(*ids)[id] = true
}
}
// Contains returns true if the set contains this id, false otherwise
func (ids *Set) Contains(id ID) bool {
ids.init(1)
return (*ids)[*id.ID]
}
// Overlaps returns true if the intersection of the set is non-empty
func (ids *Set) Overlaps(big Set) bool {
small := *ids
if small.Len() > big.Len() {
small = big
big = *ids
}
for _, id := range small.List() {
if big.Contains(id) {
return true
}
}
return false
}
// Len returns the number of ids in this set
func (ids Set) Len() int { return len(ids) }
// Remove all the id from this set, if the id isn't in the set, nothing happens
func (ids *Set) Remove(idList ...ID) {
ids.init(1)
for _, id := range idList {
delete(*ids, *id.ID)
}
}
// Clear empties this set
func (ids *Set) Clear() { *ids = nil }
// List converts this set into a list
func (ids Set) List() []ID {
idList := []ID(nil)
for id := range ids {
idList = append(idList, NewID(id))
}
return idList
}
// Equals returns true if the sets contain the same elements
func (ids Set) Equals(oIDs Set) bool {
if ids.Len() != oIDs.Len() {
return false
}
for key := range oIDs {
if !ids[key] {
return false
}
}
return true
}
// String returns the string representation of a set
func (ids Set) String() string {
sb := strings.Builder{}
sb.WriteString("{")
first := true
for idBytes := range ids {
if !first {
sb.WriteString(", ")
}
first = false
sb.WriteString(NewID(idBytes).String())
}
sb.WriteString("}")
return sb.String()
}

57
ids/set_test.go Normal file
View File

@ -0,0 +1,57 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"testing"
)
func TestSet(t *testing.T) {
id1 := NewID([32]byte{1})
ids := Set{}
ids.Add(id1)
if !ids.Contains(id1) {
t.Fatalf("Initial value not set correctly")
}
ids.Remove(id1)
if ids.Contains(id1) {
t.Fatalf("Value not removed correctly")
}
ids.Add(id1)
if !ids.Contains(id1) {
t.Fatalf("Initial value not set correctly")
} else if ids.Len() != 1 {
t.Fatalf("Bad set size")
} else if list := ids.List(); len(list) != 1 {
t.Fatalf("Bad list size")
} else if !list[0].Equals(id1) {
t.Fatalf("Set value not correct")
}
ids.Clear()
if ids.Contains(id1) {
t.Fatalf("Value not removed correctly")
}
ids.Add(id1)
ids2 := Set{}
if ids.Overlaps(ids2) {
t.Fatalf("Empty set shouldn't overlap")
}
ids2.Union(ids)
if !ids2.Contains(id1) {
t.Fatalf("Value not union added correctly")
}
if !ids.Overlaps(ids2) {
t.Fatalf("Sets overlap")
}
}

121
ids/short.go Normal file
View File

@ -0,0 +1,121 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"bytes"
"encoding/hex"
"sort"
"github.com/ava-labs/gecko/utils"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/hashing"
)
// ShortEmpty is a useful all zero value
var ShortEmpty = ShortID{ID: &[20]byte{}}
// ShortID wraps a 20 byte hash as an identifier
type ShortID struct {
ID *[20]byte `serialize:"true"`
}
// NewShortID creates an identifer from a 20 byte hash
func NewShortID(id [20]byte) ShortID { return ShortID{ID: &id} }
// ToShortID attempt to convert a byte slice into an id
func ToShortID(bytes []byte) (ShortID, error) {
addrHash, err := hashing.ToHash160(bytes)
return NewShortID(addrHash), err
}
// ShortFromString is the inverse of ShortID.String()
func ShortFromString(idStr string) (ShortID, error) {
cb58 := formatting.CB58{}
err := cb58.FromString(idStr)
if err != nil {
return ShortID{}, err
}
return ToShortID(cb58.Bytes)
}
// MarshalJSON ...
func (id ShortID) MarshalJSON() ([]byte, error) {
if id.IsZero() {
return []byte("null"), nil
}
cb58 := formatting.CB58{Bytes: id.ID[:]}
return cb58.MarshalJSON()
}
// UnmarshalJSON ...
func (id *ShortID) UnmarshalJSON(b []byte) error {
if string(b) == "null" {
return nil
}
cb58 := formatting.CB58{}
if err := cb58.UnmarshalJSON(b); err != nil {
return err
}
newID, err := ToShortID(cb58.Bytes)
if err != nil {
return err
}
*id = newID
return nil
}
// IsZero returns true if the value has not been initialized
func (id ShortID) IsZero() bool { return id.ID == nil }
// LongID returns a 32 byte identifier from this id
func (id ShortID) LongID() ID {
dest := [32]byte{}
copy(dest[:], id.ID[:])
return NewID(dest)
}
// Key returns a 20 byte hash that this id represents. This is useful to allow
// for this id to be used as keys in maps.
func (id ShortID) Key() [20]byte { return *id.ID }
// Equals returns true if the ids have the same byte representation
func (id ShortID) Equals(oID ShortID) bool {
return id.ID == oID.ID ||
(id.ID != nil && oID.ID != nil && bytes.Equal(id.Bytes(), oID.Bytes()))
}
// Bytes returns the 20 byte hash as a slice. It is assumed this slice is not
// modified.
func (id ShortID) Bytes() []byte { return id.ID[:] }
// Hex returns a hex encoded string of this id.
func (id ShortID) Hex() string { return hex.EncodeToString(id.Bytes()) }
func (id ShortID) String() string {
if id.IsZero() {
return "nil"
}
bytes := id.Bytes()
cb58 := formatting.CB58{Bytes: bytes}
return cb58.String()
}
type sortShortIDData []ShortID
func (ids sortShortIDData) Less(i, j int) bool {
return bytes.Compare(
ids[i].Bytes(),
ids[j].Bytes()) == -1
}
func (ids sortShortIDData) Len() int { return len(ids) }
func (ids sortShortIDData) Swap(i, j int) { ids[j], ids[i] = ids[i], ids[j] }
// SortShortIDs sorts the ids lexicographically
func SortShortIDs(ids []ShortID) { sort.Sort(sortShortIDData(ids)) }
// IsSortedAndUniqueShortIDs returns true if the ids are sorted and unique
func IsSortedAndUniqueShortIDs(ids []ShortID) bool {
return utils.IsSortedAndUnique(sortShortIDData(ids))
}

102
ids/short_set.go Normal file
View File

@ -0,0 +1,102 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import "strings"
// ShortSet is a set of ShortIDs
type ShortSet map[[20]byte]bool
func (ids *ShortSet) init(size int) {
if *ids == nil {
*ids = make(map[[20]byte]bool, size)
}
}
// Add all the ids to this set, if the id is already in the set, nothing happens
func (ids *ShortSet) Add(idList ...ShortID) {
ids.init(2 * len(idList))
for _, id := range idList {
(*ids)[id.Key()] = true
}
}
// Union adds all the ids from the provided sets to this set.
func (ids *ShortSet) Union(idSet ShortSet) {
ids.init(2 * idSet.Len())
for id := range idSet {
(*ids)[id] = true
}
}
// Contains returns true if the set contains this id, false otherwise
func (ids *ShortSet) Contains(id ShortID) bool {
ids.init(1)
return (*ids)[id.Key()]
}
// Len returns the number of ids in this set
func (ids ShortSet) Len() int { return len(ids) }
// Remove all the id from this set, if the id isn't in the set, nothing happens
func (ids *ShortSet) Remove(idList ...ShortID) {
ids.init(1)
for _, id := range idList {
delete(*ids, id.Key())
}
}
// Clear empties this set
func (ids *ShortSet) Clear() { *ids = nil }
// CappedList returns a list of length at most [size]. Size should be >= 0
func (ids ShortSet) CappedList(size int) []ShortID {
idList := make([]ShortID, size)[:0]
for id := range ids {
if size <= 0 {
break
}
size--
idList = append(idList, NewShortID(id))
}
return idList
}
// List converts this set into a list
func (ids ShortSet) List() []ShortID {
idList := make([]ShortID, len(ids))[:0]
for id := range ids {
idList = append(idList, NewShortID(id))
}
return idList
}
// Equals returns true if the sets contain the same elements
func (ids ShortSet) Equals(oIDs ShortSet) bool {
if ids.Len() != oIDs.Len() {
return false
}
for key := range oIDs {
if !ids[key] {
return false
}
}
return true
}
// String returns the string representation of a set
func (ids ShortSet) String() string {
sb := strings.Builder{}
sb.WriteString("{")
first := true
for idBytes := range ids {
if !first {
sb.WriteString(", ")
}
first = false
sb.WriteString(NewShortID(idBytes).String())
}
sb.WriteString("}")
return sb.String()
}

239
ids/short_set_test.go Normal file
View File

@ -0,0 +1,239 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"strings"
"testing"
)
func TestShortSetContains(t *testing.T) {
set := ShortSet{}
id0 := NewShortID([20]byte{0})
id1 := NewShortID([20]byte{1})
switch {
case set.Contains(id0):
t.Fatalf("Sets shouldn't contain %s", id0)
case set.Contains(id1):
t.Fatalf("Sets shouldn't contain %s", id1)
}
set.Add(id0)
switch {
case !set.Contains(id0):
t.Fatalf("Set should contain %s", id0)
case set.Contains(id1):
t.Fatalf("Set shouldn't contain %s", id1)
}
set.Add(id1)
switch {
case !set.Contains(id0):
t.Fatalf("Set should contain %s", id0)
case !set.Contains(id1):
t.Fatalf("Set should contain %s", id1)
}
set.Remove(id0)
switch {
case set.Contains(id0):
t.Fatalf("Sets shouldn't contain %s", id0)
case !set.Contains(id1):
t.Fatalf("Set should contain %s", id1)
}
set.Add(id0)
switch {
case !set.Contains(id0):
t.Fatalf("Set should contain %s", id0)
case !set.Contains(id1):
t.Fatalf("Set should contain %s", id1)
}
}
func TestShortSetUnion(t *testing.T) {
set := ShortSet{}
unionSet := ShortSet{}
id0 := NewShortID([20]byte{0})
id1 := NewShortID([20]byte{1})
unionSet.Add(id0)
set.Union(unionSet)
switch {
case !set.Contains(id0):
t.Fatalf("Set should contain %s", id0)
case set.Contains(id1):
t.Fatalf("Set shouldn't contain %s", id1)
}
unionSet.Add(id1)
set.Union(unionSet)
switch {
case !set.Contains(id0):
t.Fatalf("Set should contain %s", id0)
case !set.Contains(id1):
t.Fatalf("Set should contain %s", id1)
}
set.Remove(id0)
switch {
case set.Contains(id0):
t.Fatalf("Sets shouldn't contain %s", id0)
case !set.Contains(id1):
t.Fatalf("Set should contain %s", id1)
}
set.Clear()
set.Union(unionSet)
switch {
case !set.Contains(id0):
t.Fatalf("Set should contain %s", id0)
case !set.Contains(id1):
t.Fatalf("Set should contain %s", id1)
}
}
func TestShortSetEquals(t *testing.T) {
set := ShortSet{}
otherSet := ShortSet{}
if !set.Equals(otherSet) {
t.Fatal("Empty sets should be equal")
}
if !otherSet.Equals(set) {
t.Fatal("Empty sets should be equal")
}
set.Add(NewShortID([20]byte{1, 2, 3, 4, 5}))
if set.Equals(otherSet) {
t.Fatal("Sets should be unequal")
}
if otherSet.Equals(set) {
t.Fatal("Sets should be unequal")
}
otherSet.Add(NewShortID([20]byte{1, 2, 3, 4, 5}))
if !set.Equals(otherSet) {
t.Fatal("sets should be equal")
}
if !otherSet.Equals(set) {
t.Fatal("sets should be equal")
}
otherSet.Add(NewShortID([20]byte{6, 7, 8, 9, 10}))
if set.Equals(otherSet) {
t.Fatal("Sets should be unequal")
}
if otherSet.Equals(set) {
t.Fatal("Sets should be unequal")
}
set.Add(NewShortID([20]byte{6, 7, 8, 9, 10}))
if !set.Equals(otherSet) {
t.Fatal("sets should be equal")
}
if !otherSet.Equals(set) {
t.Fatal("sets should be equal")
}
otherSet.Add(NewShortID([20]byte{11, 12, 13, 14, 15}))
if set.Equals(otherSet) {
t.Fatal("Sets should be unequal")
}
if otherSet.Equals(set) {
t.Fatal("Sets should be unequal")
}
set.Add(NewShortID([20]byte{11, 12, 13, 14, 16}))
if set.Equals(otherSet) {
t.Fatal("Sets should be unequal")
}
if otherSet.Equals(set) {
t.Fatal("Sets should be unequal")
}
}
func TestShortSetList(t *testing.T) {
set := ShortSet{}
otherSet := ShortSet{}
id0 := NewShortID([20]byte{0})
id1 := NewShortID([20]byte{1})
set.Add(id0)
otherSet.Add(set.List()...)
if !set.Equals(otherSet) {
t.Fatalf("Sets should be equal but are:\n%s\n%s", set, otherSet)
}
set.Add(id1)
otherSet.Clear()
otherSet.Add(set.List()...)
if !set.Equals(otherSet) {
t.Fatalf("Sets should be equal but are:\n%s\n%s", set, otherSet)
}
}
func TestShortSetCappedList(t *testing.T) {
set := ShortSet{}
id := ShortEmpty
if list := set.CappedList(0); len(list) != 0 {
t.Fatalf("List should have been empty but was %v", list)
}
set.Add(id)
if list := set.CappedList(0); len(list) != 0 {
t.Fatalf("List should have been empty but was %v", list)
} else if list := set.CappedList(1); len(list) != 1 {
t.Fatalf("List should have had length %d but had %d", 1, len(list))
} else if returnedID := list[0]; !id.Equals(returnedID) {
t.Fatalf("List should have been %s but was %s", id, returnedID)
} else if list := set.CappedList(2); len(list) != 1 {
t.Fatalf("List should have had length %d but had %d", 1, len(list))
} else if returnedID := list[0]; !id.Equals(returnedID) {
t.Fatalf("List should have been %s but was %s", id, returnedID)
}
}
func TestShortSetString(t *testing.T) {
set := ShortSet{}
id0 := NewShortID([20]byte{0})
id1 := NewShortID([20]byte{1})
if str := set.String(); str != "{}" {
t.Fatalf("Set should have been %s but was %s", "{}", str)
}
set.Add(id0)
if str := set.String(); str != "{111111111111111111116DBWJs}" {
t.Fatalf("Set should have been %s but was %s", "{111111111111111111116DBWJs}", str)
}
set.Add(id1)
if str := set.String(); !strings.Contains(str, "111111111111111111116DBWJs") {
t.Fatalf("Set should have contained %s", "111111111111111111116DBWJs")
} else if !strings.Contains(str, "6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt") {
t.Fatalf("Set should have contained %s", "6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt")
} else if count := strings.Count(str, ","); count != 1 {
t.Fatalf("Should only have one %s in %s", ",", str)
}
}

94
ids/unique_bag.go Normal file
View File

@ -0,0 +1,94 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"fmt"
"strings"
)
// UniqueBag ...
type UniqueBag map[[32]byte]BitSet
func (b *UniqueBag) init() {
if *b == nil {
*b = make(map[[32]byte]BitSet)
}
}
// Add ...
func (b *UniqueBag) Add(setID uint, idSet ...ID) {
bs := BitSet(0)
bs.Add(setID)
for _, id := range idSet {
b.UnionSet(id, bs)
}
}
// UnionSet ...
func (b *UniqueBag) UnionSet(id ID, set BitSet) {
b.init()
key := id.Key()
previousSet := (*b)[key]
previousSet.Union(set)
(*b)[key] = previousSet
}
// DifferenceSet ...
func (b *UniqueBag) DifferenceSet(id ID, set BitSet) {
b.init()
key := id.Key()
previousSet := (*b)[key]
previousSet.Difference(set)
(*b)[key] = previousSet
}
// Difference ...
func (b *UniqueBag) Difference(diff *UniqueBag) {
b.init()
for key, previousSet := range *b {
if previousSetDiff, exists := (*diff)[key]; exists {
previousSet.Difference(previousSetDiff)
}
(*b)[key] = previousSet
}
}
// GetSet ...
func (b *UniqueBag) GetSet(id ID) BitSet { return (*b)[*id.ID] }
// List ...
func (b *UniqueBag) List() []ID {
idList := []ID(nil)
for id := range *b {
idList = append(idList, NewID(id))
}
return idList
}
// Bag ...
func (b *UniqueBag) Bag(alpha int) Bag {
bag := Bag{}
bag.SetThreshold(alpha)
for id, bs := range *b {
bag.AddCount(NewID(id), bs.Len())
}
return bag
}
func (b *UniqueBag) String() string {
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf("UniqueBag: (Size = %d)", len(*b)))
for idBytes, set := range *b {
id := NewID(idBytes)
sb.WriteString(fmt.Sprintf("\n ID[%s]: Members = %s", id, set))
}
return sb.String()
}

106
ids/unique_bag_test.go Normal file
View File

@ -0,0 +1,106 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"testing"
)
func TestUniqueBag(t *testing.T) {
var ub1 UniqueBag
ub1.init()
if ub1 == nil {
t.Fatalf("Unique Bag still nil after initialized")
} else if len(ub1.List()) != 0 {
t.Fatalf("Unique Bag should be empty")
}
id1 := Empty.Prefix(1)
id2 := Empty.Prefix(2)
ub2 := make(UniqueBag)
ub2.Add(1, id1, id2)
if !ub2.GetSet(id1).Contains(1) {
t.Fatalf("Set missing element")
} else if !ub2.GetSet(id2).Contains(1) {
t.Fatalf("Set missing element")
}
var bs1 BitSet
bs1.Add(2)
bs1.Add(4)
ub3 := make(UniqueBag)
ub3.UnionSet(id1, bs1)
bs1.Clear()
bs1 = ub3.GetSet(id1)
if bs1.Len() != 2 {
t.Fatalf("Incorrect length of set")
} else if !bs1.Contains(2) {
t.Fatalf("Set missing element")
} else if !bs1.Contains(4) {
t.Fatalf("Set missing element")
}
// Difference test
bs1.Clear()
ub4 := make(UniqueBag)
ub4.Add(1, id1)
ub4.Add(2, id1)
ub4.Add(5, id2)
ub4.Add(8, id2)
ub5 := make(UniqueBag)
ub5.Add(5, id2)
ub5.Add(5, id1)
ub4.Difference(&ub5)
if len(ub5.List()) != 2 {
t.Fatalf("Incorrect number of ids in Unique Bag")
}
ub4id1 := ub4.GetSet(id1)
if ub4id1.Len() != 2 {
t.Fatalf("Set of Unique Bag has incorrect length")
} else if !ub4id1.Contains(1) {
t.Fatalf("Set of Unique Bag missing element")
} else if !ub4id1.Contains(2) {
t.Fatalf("Set of Unique Bag missing element")
}
ub4id2 := ub4.GetSet(id2)
if ub4id2.Len() != 1 {
t.Fatalf("Set of Unique Bag has incorrect length")
} else if !ub4id2.Contains(8) {
t.Fatalf("Set of Unique Bag missing element")
}
// DifferenceSet test
ub6 := make(UniqueBag)
ub6.Add(1, id1)
ub6.Add(2, id1)
ub6.Add(7, id1)
diffBitSet := BitSet(0)
diffBitSet.Add(1)
diffBitSet.Add(7)
ub6.DifferenceSet(id1, diffBitSet)
ub6id1 := ub6.GetSet(id1)
if ub6id1.Len() != 1 {
t.Fatalf("Set of Unique Bag missing element")
} else if !ub6id1.Contains(2) {
t.Fatalf("Set of Unique Bag missing element")
}
}

5
keys/keys1/genCA.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/rootCA.key 4096
openssl req -x509 -new -nodes -key `dirname "$0"`/rootCA.key -sha256 -days 365250 -out `dirname "$0"`/rootCA.crt

6
keys/keys1/genStaker.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/staker.key 4096
openssl req -new -sha256 -key `dirname "$0"`/staker.key -subj "/C=US/ST=NY/O=Avalabs/CN=ava" -out `dirname "$0"`/staker.csr
openssl x509 -req -in `dirname "$0"`/staker.csr -CA `dirname "$0"`/rootCA.crt -CAkey `dirname "$0"`/rootCA.key -CAcreateserial -out `dirname "$0"`/staker.crt -days 365250 -sha256

34
keys/keys1/rootCA.crt Normal file
View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
DB8IRfWqBx2nWw==
-----END CERTIFICATE-----

51
keys/keys1/rootCA.key Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

1
keys/keys1/rootCA.srl Normal file
View File

@ -0,0 +1 @@
BAF3B5C5C6D0D14A

30
keys/keys1/staker.crt Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV
UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi
czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT
c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMTVaGA8zMDE5MDcxMDE2
MTIxNVowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs
YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDKYSRw/W0YpYH/MTQhiFrR0m89l6yTuzLpDtjudr/5RnhIPvtqk7YIGm/m9l29
xwR4J5r7SZGs+70yBetkbS+h7PwJ2rmWDwbrdyJKvVBhqf8kSn+VU2LePSIcJj19
3LDyWhV1H4lqNkUkcAR76Fh9qjMvA2p0vJ66+eDLXlph/RYapQx9HgOj/0BmAKMr
YCyo5BhRih+Ougg8aK4G9PQTIA5G2wTWW2QkHxM/QppFjZd/XwQeJ2H6ubWMFc5f
ttf6AzpJvFIDBu/JDCKWiCu5m8t4GL8w2OrIx8Js19lF4YYE2eojCreqgPi64S3o
cqwKsDoySTw6/5iKQ5BUYwUXX3z7EXOqD8SMHefUKeczj4WvAaZLzR27qXm55EgR
YQAIX4fhmY7NfSop3Wh0Eo62+JHoM/1g+UgOXlbnWpY95Mgd7/fwDSWLu4IxE0/u
q8VufIbfC4yrY8qlTVfAffI1ldRdvJjPJBPiQ0CNrOl60LVptpkGc9shH7wZ2bP0
bEnYKTgLAfOzD8Ut71O2AOIa80A1GNFl4Yle/MSNJOcQOSpgtWdREzIUoenAjfuz
M4OeTr4cRg4+VYTAo9KHKriN1DuewNzGd8WjKAVHmcIMjqISLTlzMhdsdm+OmfQ6
OvyX7v0GTOBbhP09NGcww5A0gCzXN18FS5oxnxe6OG9D0wIDAQABMA0GCSqGSIb3
DQEBCwUAA4ICAQAqL1TWI1PTMm3JaXkhdTBe8tsk7+FsHAFzTcBVBsB8dkJNGhxb
dlu7XIm+AyGUn0j8siz8qojKbO+rEPV/ImTH5W7Q36rXSdgvNUWpKrKIC5S8PUF5
T4pH+lpYIlQHnTaKMuqH3nO3I40IhEhPaa2wAwy2kDlz46fJcr6aMzj6Zg43J5UK
Zid+BQsiWAUau5V7CpC7GMCx4YdOZWWsT3dAsug9hvwTe81kK1JoTH0juwPTBH0t
xUgUVIWyuweM1UwYF3n8Hmwq6B46YmujhMDKT+3lgqZt7eZ1XvieLdBRlVQWzOa/
6QYTkrqwPZioKIStrxVGYjk40qECNodCSCIwRDgbnQubRWrdslxiIyc5blJNuOV+
jgv5d2EeUpwUjvpZuEV7FqPKGRgiG0jfl6Psms9gYUXd+y3ytG9HeoDNmLTSTBE4
nCQXX935P2/xOuok6CpiGpP89DX7t8yiwk8LFNnY3rvv50nVy8kerVdnfHTmoMZ9
/IBgojSIKov4lmPKdgzFfimzhbssVCa4DO/LIhTF7bQbH1ut/Oq7npdOpMjLYIBE
9lagvRVTVFwT/uwrCcXHCb21b/puwV94SNXVwt7BheFTFBdtxJrR4jjr2T5odLkX
6nQcY8V2OT7KOxn0KVc6pl3saJTLmL+H/3CtAao9NtmuUDapKINRSVNyvg==
-----END CERTIFICATE-----

27
keys/keys1/staker.csr Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEfzCCAmcCAQAwOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQK
DAdBdmFsYWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQDKYSRw/W0YpYH/MTQhiFrR0m89l6yTuzLpDtjudr/5RnhIPvtqk7YI
Gm/m9l29xwR4J5r7SZGs+70yBetkbS+h7PwJ2rmWDwbrdyJKvVBhqf8kSn+VU2Le
PSIcJj193LDyWhV1H4lqNkUkcAR76Fh9qjMvA2p0vJ66+eDLXlph/RYapQx9HgOj
/0BmAKMrYCyo5BhRih+Ougg8aK4G9PQTIA5G2wTWW2QkHxM/QppFjZd/XwQeJ2H6
ubWMFc5fttf6AzpJvFIDBu/JDCKWiCu5m8t4GL8w2OrIx8Js19lF4YYE2eojCreq
gPi64S3ocqwKsDoySTw6/5iKQ5BUYwUXX3z7EXOqD8SMHefUKeczj4WvAaZLzR27
qXm55EgRYQAIX4fhmY7NfSop3Wh0Eo62+JHoM/1g+UgOXlbnWpY95Mgd7/fwDSWL
u4IxE0/uq8VufIbfC4yrY8qlTVfAffI1ldRdvJjPJBPiQ0CNrOl60LVptpkGc9sh
H7wZ2bP0bEnYKTgLAfOzD8Ut71O2AOIa80A1GNFl4Yle/MSNJOcQOSpgtWdREzIU
oenAjfuzM4OeTr4cRg4+VYTAo9KHKriN1DuewNzGd8WjKAVHmcIMjqISLTlzMhds
dm+OmfQ6OvyX7v0GTOBbhP09NGcww5A0gCzXN18FS5oxnxe6OG9D0wIDAQABoAAw
DQYJKoZIhvcNAQELBQADggIBAE7VplAZTEGHpYwXZvhlVg0qDsb/7IQj77eNteSU
33Dq6u7QLgS+Ea04Xv5BHnhnBoWRtrNR8WLTw64cuj6p/sqXiQsSNDgxNDPuPG+g
1FFi6wjgtoIJnx/QrITuUyO/MRy1awKLHlGfbY6yXSdLCC9bqLSIRm0tx+E+jo5C
0r5+ZOcLK8ZXWq9uHjmekX0hoN4qzsbQ0J5IeMh9ag+698aqzBSEDljLHg614yiK
FxtpD+23O0XfAdgqFgXRLLg3tt8AkVuys7r/uwHoz9du+nwW2U5nsMIYBXLV2mq3
1KbpXDTlVwaSoA2LP8dpmvbyTgNbXsjPdS91Rrzd7fcsammcSV0aWPiXmIbTLtn8
61ZRR0uj+jB68cRjSvegnheifsGyq6alr8OSUMdeWVyiPy2O7N6fUVj+Fmyzl5Ph
fl9UPZTmt/zOZrcSBoWjtZfmQVfw29SfMYwlNKALN4eOT6XwBLDK4uu4UXSoXwi+
V8evUUfBWcrcXHMTIFhoZbW/b7gjhnv148XWYI0ta8pjt/akzlPLtf4ETPqfECNN
4+p2w9+R5ktzCLeceXQc8eN+ZwjIt31zG48J7Sl1wJB13VR0jPy6zDsyUIswIVfe
7gp7GHg8R0lzDpEYCvU+R7RUWK6xcpjt7+mTHM70csnnOg7uPqnXqOdtVplr0y+R
pmqJ
-----END CERTIFICATE REQUEST-----

51
keys/keys1/staker.key Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

5
keys/keys2/genCA.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/rootCA.key 4096
openssl req -x509 -new -nodes -key `dirname "$0"`/rootCA.key -sha256 -days 365250 -out `dirname "$0"`/rootCA.crt

6
keys/keys2/genStaker.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/staker.key 4096
openssl req -new -sha256 -key `dirname "$0"`/staker.key -subj "/C=US/ST=NY/O=Avalabs/CN=ava" -out `dirname "$0"`/staker.csr
openssl x509 -req -in `dirname "$0"`/staker.csr -CA `dirname "$0"`/rootCA.crt -CAkey `dirname "$0"`/rootCA.key -CAcreateserial -out `dirname "$0"`/staker.crt -days 365250 -sha256

34
keys/keys2/rootCA.crt Normal file
View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
DB8IRfWqBx2nWw==
-----END CERTIFICATE-----

51
keys/keys2/rootCA.key Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

1
keys/keys2/rootCA.srl Normal file
View File

@ -0,0 +1 @@
BAF3B5C5C6D0D14A

30
keys/keys2/staker.crt Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV
UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi
czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT
c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMTlaGA8zMDE5MDcxMDE2
MTIxOVowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs
YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDdToR60na6NuR9iSAUMyzPXJNMWVQbLyT5/iZCiJ3BB4YWMBhfxpJWJiWXcM+z
nDgpJuyCEeh5Dp6ZY3Fe7k6Hht6FmFpDjwnjpQmdkEKUg00G+ElPTp/UsmsPL+JA
swPqBZWpMBS3dsXQNunMMtMGlrf5S0l6XX4y7kc/GTxYgveWZ9JtR/m2KNer+wjg
BHqJ4rPqnHB30sDYPZg91Cz1Ak8Bb2w2I108zQVgKK6eIqNKXJJ/4pizSZdU4920
wMxYBpnfDAchnxei9U/v3QbT7eKUI2fGr+hOWTIWU80+VeOBt8a6P4sS9AQh5/6G
8qwmAqO3YQ9dxN82iu/H3+N+GGa/M0r5rEWrzwIuFhwKvyQcpPRBm2yQnBnhL9G5
kN6n4OBM0KsgZ3CYlHZSg4eWcNgBt1WCFsQc7vfUFaJnr8QP3pF4V/4Bok7wTO5H
N0A1EYEVYuX53NGnrKVe+Fg9+xMOgXPWkUNqdvpI9ZbV3Z0S5866qF3/vBZrhgCr
Kc5E/vMexBRe8Ki4wKqONVhi9WGUcRHvFEikc+7VrPj0YaG6zVLd+uOAJN81fKOP
Yo4X4sZrMyPYl3OjGtMhfV4KvCaLEr1duOklqO6cCvGQ8iAlLVy3VJyW5GJ0D0Ky
iAir4VNdAJKo1ZgiGivJLWulTfjUifCN9o115AiqJxiqwwIDAQABMA0GCSqGSIb3
DQEBCwUAA4ICAQCQOdwD7eRIxBvbQHUc+m0TRzEa17BCfck1Y2WwN3TZXDGSkPVE
0uujA8SL3qi8/CTLGRqI9U3gRZJf+tJPBF/P021PEmyaFTS4htxcDxTxuZv2jCo9
+XhUEyvRWitTmoy1esq3mkotVQHeTmQvwCsQJAhctVA/hRdJwmMPs1B8QxOUI6Bq
SOBHa9CsXIzVOFv8FqE91PZA2ns30sKQYrrnbH99apfF5WglLUoyPwxf2e3AACh7
beEdk45ivvKwi5Jk8nr85KDHYPlqkr0bd9Ehl8xplaNBdMPeRufqBDlztjcLJ3wo
mnrt95gQMeSoLHY3UNsIRjbj43zImu7q9v/DD9ppQpu26aRDRmBNgLZA9GM5XnbZ
RFi3VxLyqasGcSzaHwz5c7vOBOkOdlqcQzISRvWDxiN1HkAL+hkiQCuMchgORAgM
wzPooa8rfWtLIpOXMpwuVGb/8rGNLEPovoCK9z6c+WZ+zkRo4+3TQkOMY66Xht7r
Ahly3ler+Tyg6a5jXT92WKC/MXBYAy2ZQNoy204kNKevcH7R2cSkxITd3n5EacNy
5MAtCNIk7JweLCh9rLrLUBt+i4n44sP+LVhfWHemngA8CoF4n6eQ0pp0ixZTen0j
4uN0G2Nf+JeGMlqoObLWdIOdH/pbDppXGoZaKKDd7+bA74Fle5Uh7+1e3A==
-----END CERTIFICATE-----

27
keys/keys2/staker.csr Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEfzCCAmcCAQAwOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQK
DAdBdmFsYWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQDdToR60na6NuR9iSAUMyzPXJNMWVQbLyT5/iZCiJ3BB4YWMBhfxpJW
JiWXcM+znDgpJuyCEeh5Dp6ZY3Fe7k6Hht6FmFpDjwnjpQmdkEKUg00G+ElPTp/U
smsPL+JAswPqBZWpMBS3dsXQNunMMtMGlrf5S0l6XX4y7kc/GTxYgveWZ9JtR/m2
KNer+wjgBHqJ4rPqnHB30sDYPZg91Cz1Ak8Bb2w2I108zQVgKK6eIqNKXJJ/4piz
SZdU4920wMxYBpnfDAchnxei9U/v3QbT7eKUI2fGr+hOWTIWU80+VeOBt8a6P4sS
9AQh5/6G8qwmAqO3YQ9dxN82iu/H3+N+GGa/M0r5rEWrzwIuFhwKvyQcpPRBm2yQ
nBnhL9G5kN6n4OBM0KsgZ3CYlHZSg4eWcNgBt1WCFsQc7vfUFaJnr8QP3pF4V/4B
ok7wTO5HN0A1EYEVYuX53NGnrKVe+Fg9+xMOgXPWkUNqdvpI9ZbV3Z0S5866qF3/
vBZrhgCrKc5E/vMexBRe8Ki4wKqONVhi9WGUcRHvFEikc+7VrPj0YaG6zVLd+uOA
JN81fKOPYo4X4sZrMyPYl3OjGtMhfV4KvCaLEr1duOklqO6cCvGQ8iAlLVy3VJyW
5GJ0D0KyiAir4VNdAJKo1ZgiGivJLWulTfjUifCN9o115AiqJxiqwwIDAQABoAAw
DQYJKoZIhvcNAQELBQADggIBAM2IHKsQsebxTD50QQXtSNbyRzG/GpMZuZXn/QYO
QGW0ThJwtcmx6cqQvuyBovH5WhB9QUBFjiKkR7Qef7HUsgxU1cJA75gBfb2GMUru
Q+T37xOxtr6S2TcKOq/LvdJaTYmAHmW9V7vwEcrMRa9lWVTEmJIKTuxiUubpXtup
8OB8WLIvDikVtKtegvl6VCaTApCkUfuLhf7DERQ6sGLXWz6dVQcfvbfcXK2fn1Ik
Koxqy1SSz/rPb4u9NEk1yqvJQdpgnbTM3drTPHiIHCA7F6SjMu5tekHtVQkFOd6c
B0geEwyxY97zqnFv5YXiukXEaAnCRAlOuIZXRqtK6GFthTWo33YpB2KaRUtJ7IuP
og4Q/zjDs8DEc/qbbUbhyulExz6uoyRKO4j/gG3ESC6j09j7Eungt1LDhyt8p3wD
pytIIPkTseykO0CcEpEcGbES6d3u4PrFJ75XWxMkNZVK8mC3faxx2kJLfS1+4Fg8
A0zbcN6qwm1ezGq2vGQcyVKyFVWJQAEAFuSO8sjW6dk3ClfE+MNGUvxTQMe96V14
jGRICCp9aJrJXA3u0iQaUX0cXmlhegAYk7Ho/Ef3k/PcP8DzZ8Ck839oRHBw4pPv
tKbyiKnOcet7AFGwsiM2t5VLrj4jovhRLEiaXrCaxNe6j4xs63TEb+8uTCzKyktC
4BFq
-----END CERTIFICATE REQUEST-----

51
keys/keys2/staker.key Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

5
keys/keys3/genCA.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/rootCA.key 4096
openssl req -x509 -new -nodes -key `dirname "$0"`/rootCA.key -sha256 -days 365250 -out `dirname "$0"`/rootCA.crt

6
keys/keys3/genStaker.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/staker.key 4096
openssl req -new -sha256 -key `dirname "$0"`/staker.key -subj "/C=US/ST=NY/O=Avalabs/CN=ava" -out `dirname "$0"`/staker.csr
openssl x509 -req -in `dirname "$0"`/staker.csr -CA `dirname "$0"`/rootCA.crt -CAkey `dirname "$0"`/rootCA.key -CAcreateserial -out `dirname "$0"`/staker.crt -days 365250 -sha256

34
keys/keys3/rootCA.crt Normal file
View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
DB8IRfWqBx2nWw==
-----END CERTIFICATE-----

51
keys/keys3/rootCA.key Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

1
keys/keys3/rootCA.srl Normal file
View File

@ -0,0 +1 @@
BAF3B5C5C6D0D14A

30
keys/keys3/staker.crt Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFNzCCAx8CCQC687XFxtDRSjANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJV
UzELMAkGA1UECAwCTlkxDzANBgNVBAcMBkl0aGFjYTEQMA4GA1UECgwHQXZhbGFi
czEOMAwGA1UECwwFR2Vja28xDDAKBgNVBAMMA2F2YTEiMCAGCSqGSIb3DQEJARYT
c3RlcGhlbkBhdmFsYWJzLm9yZzAgFw0xOTA3MDIxNjEyMjJaGA8zMDE5MDcxMDE2
MTIyMlowOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQKDAdBdmFs
YWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQC8mVDToHbkUF2gRdVfpydZLNKeQ38d6HZFkUM3U1dWLZFSZNvagN8hlQvY/tQu
3A40p19WgKbzWZre3tg1Akw8Jztdz9gl4RMn142IIO3CiwIptkE0JopbZhmG5fAC
2n/MXQtfieI3hzeR04LW4JgLKzf3Nn8xZdlBgJfBmL5qUUnE7O7IbJGGma6gSD3e
wetE6KQZtNtf0xRIv08doZKYwTl6ItkdGK76ufqq098GVwWvA1wSune4+MFgs9N4
eFJj6Jyt85fiK/cwPx7KRdgYgBzrZQ4EPshRnwWrBTieOOaJvAA2RMxMEYzKRrJA
AsYI1zxtNyqIUaBTcxmaz+NXUGW+wHwITic0Gp/XQm2Lwr/lxIV6OnAlL3CgbSXi
rSnoG+eHQ+vDzBAcRDkTAgv/GUIzlfqT2StTK02uIBgJYzvFTG4plHitccRfy8wx
sh5Z8xG99lmPQQtLsnlQAV+Li06Cb8CH4hUVoiWiVs5QAahqWmv5fpoX0Es26RyU
HXGbjE202pyMMA7jUerUVKMijOoGZtcH6zB4p/dJ0TtToRwOgrA7NCI9AYVtqVXr
XG/udj8ur2r1bTVwIbHsOeTEP3gY0mHRWm2E/bLjt9vbYIRUxR8xWnLkbeBziNTw
g+36jdDF+6gu3cUz/nbSn8YY+Y1jjXuM3lqF8iMaAobhuwIDAQABMA0GCSqGSIb3
DQEBCwUAA4ICAQAe2kC0HjKZU+dlnU2RlfBpB4QgzzrFE5N9A8F1MlE4vV3AzCg1
RVdHPvniXzdNhDiiflK0l/cnrFv2X1TzYMrrA677/usHf2Bw0xjm/ipHOt5V+4TN
mZAIA4IPl09gP28IZLc9xSuq4FoHeM8OTxhttOlINhqpG9P5d6bPezW6ZzI3CdPP
CF69xK4GFlj/NQnAoFogid4ojYYNTj/cM4PYQU2KbrlzLyPuUk/CgwefXLMH87/H
e3kPDev80Tjv2Pm5nD937fZfgrEoyolKxiRVcfZVMxR7qhPhizjueD0DAkfQIs7L
YVSyx/qjEv2bBYaim5RQakUeHR1Xu5Xj/k5zr33t979ede50byQrcWm4H5JxnEpD
JxJnFfDOU6o14SKGHSrao5Z4C3dI55DM84WLASnlMI5BK4XtS3notLNzG8dfWWhT
9m0Hcry+wPNDcGr8Mtj1los/0bMDqMHC4jcFW1hrXCUUs9RYzE+N/xoqwCQSgN1P
E73uXTySWj5ovMR5TPF6PhcftLB/OziqO7FverEBpvGGHUAnUT61JtjodjXPbEdj
0VgyMOBY2y53HTXnx3dxeFZkUdRX/VZYy8tMK3MTY+7UIU5cWYnCZAo5LNcc0ukR
S6WS9+6eaQ6XRjhfNUjx9a7FzqapWdtTedpipmBP1Njap3g29iUuVnLQeg==
-----END CERTIFICATE-----

27
keys/keys3/staker.csr Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEfzCCAmcCAQAwOjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMRAwDgYDVQQK
DAdBdmFsYWJzMQwwCgYDVQQDDANhdmEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQC8mVDToHbkUF2gRdVfpydZLNKeQ38d6HZFkUM3U1dWLZFSZNvagN8h
lQvY/tQu3A40p19WgKbzWZre3tg1Akw8Jztdz9gl4RMn142IIO3CiwIptkE0Jopb
ZhmG5fAC2n/MXQtfieI3hzeR04LW4JgLKzf3Nn8xZdlBgJfBmL5qUUnE7O7IbJGG
ma6gSD3ewetE6KQZtNtf0xRIv08doZKYwTl6ItkdGK76ufqq098GVwWvA1wSune4
+MFgs9N4eFJj6Jyt85fiK/cwPx7KRdgYgBzrZQ4EPshRnwWrBTieOOaJvAA2RMxM
EYzKRrJAAsYI1zxtNyqIUaBTcxmaz+NXUGW+wHwITic0Gp/XQm2Lwr/lxIV6OnAl
L3CgbSXirSnoG+eHQ+vDzBAcRDkTAgv/GUIzlfqT2StTK02uIBgJYzvFTG4plHit
ccRfy8wxsh5Z8xG99lmPQQtLsnlQAV+Li06Cb8CH4hUVoiWiVs5QAahqWmv5fpoX
0Es26RyUHXGbjE202pyMMA7jUerUVKMijOoGZtcH6zB4p/dJ0TtToRwOgrA7NCI9
AYVtqVXrXG/udj8ur2r1bTVwIbHsOeTEP3gY0mHRWm2E/bLjt9vbYIRUxR8xWnLk
beBziNTwg+36jdDF+6gu3cUz/nbSn8YY+Y1jjXuM3lqF8iMaAobhuwIDAQABoAAw
DQYJKoZIhvcNAQELBQADggIBAEWU13T1alCni4R36J65TrGfIljW8LGhmWRo5xoV
YW7HxxZ/WTAFSwAv0yHCGq+H/tebRZhvua+c+jP16YBDoAp5neGWW57gLDg+35H5
guLo73p/qM6hyaUGSfyO9D1nS1QX8R0r70TQYbIrVB4uQeTM2pEYR6NYO7bjPEWr
WwC6RnbtnsNGTeoH+LwiM+uY//VB/tUe1u2y6U8HkIXJo7j4+NqUL1xXmYmC6Rph
PNI3MAZUL40z1VX7fn/Vp7+rc0CBUsFMOLfLmSgL8jsQeKuyVAQKA4xzWQ2qeuGV
Bv24rHbnSxYSu8tMs31LZPn+fsvNWB9iU7MEiTUr+8nAPEAANNaBwaD1EUkzC1WC
OcCUpMgkhVuzfHq+eXWnw3cGVvEZ8A4DzOyl1ZFofxBX7IOOv0lmpDQSeEQlmKPF
LdWI2JJM76BjeXI7l5HbOmRJv1kYFwBq/biDxCSmmNX8uHvAj1EgDNXvr/pRw7rT
6yKOLtk1GSCCrrYQijCXRx2u276+j8MtC5i6FVcUoaSYD+nx2+ApOnZlYR7xsJYw
5ECaeDagjHP472FY/fuhy/UwAIFm5gCcVFs3A2M/Iyn+vsAW5WEdh+fwGiWxfw49
Y5KRT8u7BD0R5T5UYxYwzYekEzxsoD3bvQGx/4tboSUxkOd7pVymbuGzIsQ18heI
78pG
-----END CERTIFICATE REQUEST-----

51
keys/keys3/staker.key Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

5
keys/keys4/genCA.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/rootCA.key 4096
openssl req -x509 -new -nodes -key `dirname "$0"`/rootCA.key -sha256 -days 365250 -out `dirname "$0"`/rootCA.crt

6
keys/keys4/genStaker.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -ex
openssl genrsa -out `dirname "$0"`/staker.key 4096
openssl req -new -sha256 -key `dirname "$0"`/staker.key -subj "/C=US/ST=NY/O=Avalabs/CN=ava" -out `dirname "$0"`/staker.csr
openssl x509 -req -in `dirname "$0"`/staker.csr -CA `dirname "$0"`/rootCA.crt -CAkey `dirname "$0"`/rootCA.key -CAcreateserial -out `dirname "$0"`/staker.crt -days 365250 -sha256

34
keys/keys4/rootCA.crt Normal file
View File

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
DB8IRfWqBx2nWw==
-----END CERTIFICATE-----

51
keys/keys4/rootCA.key Normal file
View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

1
keys/keys4/rootCA.srl Normal file
View File

@ -0,0 +1 @@
BAF3B5C5C6D0D14A

Some files were not shown because too many files have changed in this diff Show More