mirror of https://github.com/poanetwork/gecko.git
init repo
This commit is contained in:
commit
b9e34e3b15
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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.*
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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`
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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[:])
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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{})
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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{}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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() {}
|
|
@ -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
|
||||
}
|
|
@ -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)))
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)) }
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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)) }
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
DB8IRfWqBx2nWw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
BAF3B5C5C6D0D14A
|
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
|
@ -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-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKAIBAAKCAgEAymEkcP1tGKWB/zE0IYha0dJvPZesk7sy6Q7Y7na/+UZ4SD77
|
||||
apO2CBpv5vZdvccEeCea+0mRrPu9MgXrZG0voez8Cdq5lg8G63ciSr1QYan/JEp/
|
||||
lVNi3j0iHCY9fdyw8loVdR+JajZFJHAEe+hYfaozLwNqdLyeuvngy15aYf0WGqUM
|
||||
fR4Do/9AZgCjK2AsqOQYUYofjroIPGiuBvT0EyAORtsE1ltkJB8TP0KaRY2Xf18E
|
||||
Hidh+rm1jBXOX7bX+gM6SbxSAwbvyQwilogruZvLeBi/MNjqyMfCbNfZReGGBNnq
|
||||
Iwq3qoD4uuEt6HKsCrA6Mkk8Ov+YikOQVGMFF198+xFzqg/EjB3n1CnnM4+FrwGm
|
||||
S80du6l5ueRIEWEACF+H4ZmOzX0qKd1odBKOtviR6DP9YPlIDl5W51qWPeTIHe/3
|
||||
8A0li7uCMRNP7qvFbnyG3wuMq2PKpU1XwH3yNZXUXbyYzyQT4kNAjazpetC1abaZ
|
||||
BnPbIR+8Gdmz9GxJ2Ck4CwHzsw/FLe9TtgDiGvNANRjRZeGJXvzEjSTnEDkqYLVn
|
||||
URMyFKHpwI37szODnk6+HEYOPlWEwKPShyq4jdQ7nsDcxnfFoygFR5nCDI6iEi05
|
||||
czIXbHZvjpn0Ojr8l+79BkzgW4T9PTRnMMOQNIAs1zdfBUuaMZ8XujhvQ9MCAwEA
|
||||
AQKCAgEAuUM4Mt8r8bYBTPVj/ZZvXUjAYKfqacqijkrzN0kp8C4cijZtvWC+8KgS
|
||||
7GF36vS3GK9Y5tSwMKS6y4IzvFlfk2H4T6UU41OaSA9lKvonDWCrmjNAnBgbl8pq
|
||||
4U34WLGgohrpLbDTAJHxtat9z1ghOdiGxnDgEUFiJVP9/u2+25jtlTKmPhstxgEy
|
||||
mK3YsSp3d5xmzq4cuXF/fJ1vQhsXHDLqHt78jKZZA+AWpIB57VXy67y1bk0rGnTK
|
||||
xxRnOaOODubJgxqMEQ1WkLs1Jow9Sspd9vDghPzt4SNMzorB8YDESMib17xF6iXq
|
||||
jFj6x6HB8H7mp4X3RyMYJuo2w6lpzBsEncUYpKhqMabF0I/giI5VdpSDvkCCOFen
|
||||
nWZLV9Ai/x7tTq/0F+cVM69Mgfe8iYymqlfd6WRZITKfViNHALlG/Pq9yHJsz7Ng
|
||||
S8BKODt/sj4Q0xLtFDT/DmpP50iq7SiS14obcKcQr8FAjM/sOY/Ulg4M8MA7EugS
|
||||
pDJwLl6XDoIMMCNwZ1HGsDstzmx5Mf50bS4tbK4iZzcpPX5RBTlVdo9MTSgnFizp
|
||||
Ii1NjHLuVVCSLb1OjoTgu0cQFiWEBCkC1XuoR8RCY6iWVrUH4Gezni7ckt2mJaNA
|
||||
pd6/87dFKE3jh5T6jZeJMJg5skTZHSozJDuaj9pMK/JONSD06sECggEBAPq2lEmd
|
||||
g1hpMIqa7ey1uoLd1zFFzlWrxTJLlu38N69mYDOHrV/zqRGOpZB+1nH7tQJIT/L1
|
||||
xLN33mFVqCrN8yUmZ+iUWioaI5JZ1jzCgemVGeBgodwP9MOZfxxrDp17oTdabaEq
|
||||
7ZaBYnY8xK/4bCxu/B4mFiF3Za8ZTd/+2yev7JM+E3MorWc7rrKm1ApflfxytdhO
|
||||
JLBiqOcqobI3dgHyzesVb8cT4XCpoRhdrFwort0JI7ryfddd49vMJ3ElRbnN/h4F
|
||||
f24cWY/sQPq/nfDmec28Z7nVza1D4rszNylYDvzdjF0Q1mL5dFVntWbZA1CNurVw
|
||||
nTfwuyQ8RF9YnYMCggEBAM6lpNeqaiG9ixKSr65pYOKtByUI3/eTT4vBnrDtYF+8
|
||||
ohiKgIymG/vJsSdrynKfwJObEy2dBYhCGF3h9z2nc9KJQD/su7wxCsdmBs7YoDiM
|
||||
uzNPlRAmI0QAFILPCk48z/lUQk3r/Mzu0YzRv7fI4WSpIGAefVPDqy1uXsATDoDJ
|
||||
arcEkND5Lib89Lx7r02EevJJTdhTJM8mBdRl6wpNV3xBdwis67uSyunFZYpSiMw7
|
||||
WWjIRhzhLIvpgD78UvNvuJi0UGVEjTqnxvuW3Y6sLfIk80KSR24USinT27t//x7z
|
||||
yzNko75avF2hm1f8Y/EpcHHAax8NAQF5uuV9xBNvv3ECggEAdS/sRjCK2UNpvg/G
|
||||
0FLtWAgrcsuHM4IzjVvJs3ml6aV3p/5uKqBw0VUUzGKNCAA4TlXQkOcRxzVrS6HH
|
||||
FiLn2OCHxy24q19Gazz0p7ffE3hu/PMOFRecN+VChd0AmtnTtFTfU2sGXMgjZtLm
|
||||
uL3siiRiUhFJXOE7NUolnWK5u2Y+tWBZpQVJcCx0busNx7+AEtznZLC583xaKJtD
|
||||
s1K7JRQB7jU55xrC0G9pbkMysm0NtyFzgwmfipBHVlCpyvg6DCxd8FhvhN9Zea1b
|
||||
fhkc0SJZorHC5hkqpydJDmlVCk0vzEAeQM4C94ZUOytbnjQnmXp14CNASYqLXteQ
|
||||
ueRo0wKCAQAG0F10IxFm1WotjZqvZJgmQVBX/0frUPcxg4vpB5rC7WRm7MI6YQvR
|
||||
LKBjzWEakHv4Igfq3B+fk5ZcGiRd6xSdn5r3wKWcGf3h/1JAJdJ6quFNWtVud+N3
|
||||
zYzfl1YeqFCvRwD8ssheNY3BV/U7aStNd2oy4S5+wZf2YopLSRWUV4/mQwdHbMAB
|
||||
1xt2z5lDNBgdvx8LAArZrcZJb6blaxF0bnAvYAxR3hBEzxZ/DiOmoFpdYyU0tJQU
|
||||
dPmemhFeJ5PtrRxtimohwgCEsT/TAYhuUJuY2VvznEWpxWucbicKbT2JD0t67mEB
|
||||
sV9+8jqVbCliBtdBadtbohjwkkoR3gBxAoIBAG3cZuNkIWpELEbeICKouSOKN06r
|
||||
Fs/UXU8roNThPR7vPtjeD1NDMmUHJr1FG4SJrSigdD8qNBg8w/G3nI0Iw7eFskk5
|
||||
8mNm21CpDzON36ZO7IDMj5uyBlj2t+Ixl/uJYhYSpuNXyUTMm+rkFJ0vdSV4fjLd
|
||||
J2m30juYnMiBBJf7dz5M95+T0xicGWyV24zVYYBbSo0NHEGxqeRhikNqZNPkod6f
|
||||
kfOJZGalh2KaK5RMpZpFFhZ/kW9xRWNJZyCWgkIoYkdilMuISBu3lCrk8rdMpAL0
|
||||
wHEcq8xwcgYCS2qk8HwjtmVd3gpB1y9UshMr3qnuH1wMpU5C+nM2oy3vSko=
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
DB8IRfWqBx2nWw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
BAF3B5C5C6D0D14A
|
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
|
@ -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-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
DB8IRfWqBx2nWw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
BAF3B5C5C6D0D14A
|
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
|
@ -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-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJJwIBAAKCAgEAvJlQ06B25FBdoEXVX6cnWSzSnkN/Heh2RZFDN1NXVi2RUmTb
|
||||
2oDfIZUL2P7ULtwONKdfVoCm81ma3t7YNQJMPCc7Xc/YJeETJ9eNiCDtwosCKbZB
|
||||
NCaKW2YZhuXwAtp/zF0LX4niN4c3kdOC1uCYCys39zZ/MWXZQYCXwZi+alFJxOzu
|
||||
yGyRhpmuoEg93sHrROikGbTbX9MUSL9PHaGSmME5eiLZHRiu+rn6qtPfBlcFrwNc
|
||||
Erp3uPjBYLPTeHhSY+icrfOX4iv3MD8eykXYGIAc62UOBD7IUZ8FqwU4njjmibwA
|
||||
NkTMTBGMykayQALGCNc8bTcqiFGgU3MZms/jV1BlvsB8CE4nNBqf10Jti8K/5cSF
|
||||
ejpwJS9woG0l4q0p6Bvnh0Prw8wQHEQ5EwIL/xlCM5X6k9krUytNriAYCWM7xUxu
|
||||
KZR4rXHEX8vMMbIeWfMRvfZZj0ELS7J5UAFfi4tOgm/Ah+IVFaIlolbOUAGoalpr
|
||||
+X6aF9BLNukclB1xm4xNtNqcjDAO41Hq1FSjIozqBmbXB+sweKf3SdE7U6EcDoKw
|
||||
OzQiPQGFbalV61xv7nY/Lq9q9W01cCGx7DnkxD94GNJh0VpthP2y47fb22CEVMUf
|
||||
MVpy5G3gc4jU8IPt+o3QxfuoLt3FM/520p/GGPmNY417jN5ahfIjGgKG4bsCAwEA
|
||||
AQKCAgA+uHIT3yKK7VslqPO7+tfwJSLqNSI6LQvgON30sUezRjY1A4vGD+OkxG+L
|
||||
O7wO1Wn4As2G9AQRm/QQOGYIwvnda2Kn4S5N8psvPdU4t1K6xwXyH0Vx9Xs/yCWn
|
||||
IiL+n/GuYicdH7rWoqZNXdz+XvTRig7zrPEB2ZA143EUlhqFOwFgdzc1+j0vWT6k
|
||||
2UGSKkV2xjOExQvLw2PUiaLjBM++80uNHbe8oG/YvC7rzsg10Iz4VhKxu8eDAV82
|
||||
LLegMcucpEgu5XrWYa60Idm4hR/HjhuQASx3JvXxhwQYiwT4QY4Rsi8T3S9gANok
|
||||
jvxKo2F+oS3cWGNRsGu0NOwH+yjsVyMYazcLOUesAAe85ttXgYr02+Z/uMnxqtOF
|
||||
gjIHY3X5QZbD4l4gbwx+PLbjsj4KC6r3yZrr51PdLUrBvoqBhqwuCksdaMntWGME
|
||||
u0V/ooJi4+uzCYzN06jFfAFXa2pWzVB5yKw1d6yYi9U/bPd4xn1phLUMHrC2bvdM
|
||||
H8P18gAS6rkWn+ageiWRHmkf4uoKgv3PrMjijkBaGpf6xjv6+0Q393jdVIC7wgJV
|
||||
8W0i1f1Awv68089mHBEarPTv3gz39251WFCPNQhEuSy79Li5zjwOprZXS0MnJXbm
|
||||
B00IPTIw51KuaoueWzY1pA2IFQ/0sH3fo2JhD0pp1gI0Dde7EQKCAQEA7RVgNelk
|
||||
3H3ZJsoOfOTFa03lnXuEfTAyxhEECRz64k54pSbEWV73PIeYByZvnsrKRydpYWUP
|
||||
Cp/mKhAJH4UCf2hzY0GyO7/D6+HEKZdCg6a0DNKckAoFkBfeOlLJLjLVAW2eEVxz
|
||||
tlFt+/WBE90GCvE5ovXuEhXGaPxCPp5giIN5phSzSD557bwwOyPwNKFZ7Ao77UNK
|
||||
kz6EzcvQgqb205SRRKGpS8/T/9LcLsUYVkBfYQ/BayjffO+cQF4vH5rB4x/8/T7t
|
||||
uUa79uY+LeGHgTSFIAui9LEK5ry//2hDJINsItYMks1Qo4Suu23pOuGerjiFTKWl
|
||||
mOIoFmPmbebAcwKCAQEAy6WaJczPcKQQ/hqglQtxU6VfP49ZjUKkoi+OCmtvKDts
|
||||
7pJfIkuluhnYGeqFfjnopw5bgZHNFm6Gccc5CwBtN6Wk1KnnOgDIg3kYCrfjtKy/
|
||||
BSSV3kLEBvhe9EJA56mFgP7RufMbHTXhXPGLkgE7JBZj2EKxp1qGYYVZesTMFwDM
|
||||
KEHwzIGcFkyZsd2jptyLYqcfDKzTHmFGcw1mdtLWAUdpv3xrS3GvrCbUMqIodjRd
|
||||
qkrg/d/kQpK7A3oLOWfa6eBQ2BXqaWB1x13bzJ2WlshxJAZ1p1ozKii5BQ9rvwWo
|
||||
muI5vd7o6A9Xsl8QzluSSSPi+NhjZ64gMBrXciRvmQKCAQB/dB5k3TP71SwITle7
|
||||
jMEVDquCHgT7yA2DrWIeBBZb0xPItS6ZXRRM1hhEv8UB+MMFvYpJcarEa3Gw6y38
|
||||
Y+UT2XMuyQKoXE9XX+e09DwtylDBE/hW9wxGio5NjHPbAjjAq81uR+Vs/hnCehkK
|
||||
NKgq+cOid9OkpVAk4Hg8cagzu3qKblZzYCLsS18ibA+WO6e73USaKLLOta1vdUKC
|
||||
+n92/0eZPc9lkjTGMvVrr0mGFNUxuOaiVTbQU4AMmpV6yBezol6/RjVGhWBHOz/y
|
||||
KmxOaY2nzJmuMf9KS+5rwAFYf86Ca9AWm4neXlYRLOVVYjWMM5Z1vhdoOSyT3ODj
|
||||
9ElBAoIBAGCRPaBxF2j9k8U7ESy8CVg10g3MxxVSJcl2rW9JdKNqUoRqykvz/Tlb
|
||||
afsYF4c8pJMbHs85OTxK2tv3MZiC8kdx99CUZL4/gtW9RWZHvuV9CPPCXoLPvC7l
|
||||
9fjztd1kqJb7vq3jltbqJtyw+ZMZnFbHez8gmSeXqKNz3XN3AKRjz2vDoREI4OA+
|
||||
IJ+UTzcf28TDJNkY1t/QFt0V3KG55psipwWTVTmoRjpnCzabaH5s5IGNElWwpoff
|
||||
FmlWpR3qnodKxGtDMS4Y/KC2ZDUKAU+s6uG/YmkiP6LdPqckod4qK8KORf1AR8dL
|
||||
BzXhGJISIDMonkeMLM8MZd0JzWIl3vkCggEAPBkExd2j4VY5s+wQJdiMto5DDoci
|
||||
kAEIvIkJY9I+Pt2lpinQKAcAAXbvueaJkJpq31f6Y66uok8QnD09bIQCABjjlIve
|
||||
o7qQ+H8/iqHQX1nbHDzInaDdad3jYtkWUHjHPaKg2/ktyNkFtlSHskvvCEVw5aju
|
||||
80Q3tRpQG9Pe4ZRjKEzNIpMXfQksFH0KwjwAVKwYJLqZxtNEYok4dpefSIsnH/rX
|
||||
pwK/pyBrFqxU6PURULUJuLqRlaIRXAU31RmJsVs2JbmI7Cbtj2TmqAOxsLsi5UeJ
|
||||
cZxcTAuYCNYMu88ktHul8YJdBF3rQKUOnsgW1cx7H6LGbuPZTpg8Sbyltw==
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
DB8IRfWqBx2nWw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
BAF3B5C5C6D0D14A
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue