[project] More Spring Cleaning (#2751)
* [project] Remove hmyclient * [drand] Remove dead drand * [drand] Remove dead code in proto, more dead drand things * [project] Remove dead demo tool * [project] Remove dead Lottery solidity * [project] Remove dead hmy client main * [node] Remove unused variable in test code * [project] Remove keygen * [project] Remove not useful testpull/2758/head
parent
ba1c0cd0a3
commit
a7b79616e2
@ -1,13 +0,0 @@ |
||||
package client |
||||
|
||||
import ( |
||||
"testing" |
||||
) |
||||
|
||||
func TestClient(t *testing.T) { |
||||
shardID := uint32(0) |
||||
client := NewClient(nil, shardID) |
||||
if client.ShardID != uint32(0) { |
||||
t.Errorf("client initiate incorrect") |
||||
} |
||||
} |
@ -1,23 +0,0 @@ |
||||
package message |
||||
|
||||
import ( |
||||
"testing" |
||||
) |
||||
|
||||
const ( |
||||
testIP = "127.0.0.1" |
||||
) |
||||
|
||||
func TestClient(t *testing.T) { |
||||
s := NewServer(nil, nil, nil) |
||||
if _, err := s.Start(); err != nil { |
||||
t.Fatalf("cannot start server: %s", err) |
||||
} |
||||
|
||||
client := NewClient(testIP) |
||||
_, err := client.Process(&Message{}) |
||||
if err == nil { |
||||
t.Errorf("Not expected.") |
||||
} |
||||
s.Stop() |
||||
} |
@ -1,120 +0,0 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"net" |
||||
"net/http" |
||||
"strconv" |
||||
|
||||
"github.com/gorilla/handlers" |
||||
"github.com/gorilla/mux" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
) |
||||
|
||||
// Constants for main demo.
|
||||
const ( |
||||
Port = "31313" |
||||
LocalIP = "127.0.0.1" |
||||
) |
||||
|
||||
var ( |
||||
server *http.Server |
||||
grpcClient = msg_pb.NewClient(LocalIP) |
||||
) |
||||
|
||||
// Enter ---
|
||||
func Enter(w http.ResponseWriter, r *http.Request) { |
||||
w.Header().Set("Content-Type", "application/json") |
||||
key := r.FormValue("key") |
||||
amount, err := strconv.ParseInt(r.FormValue("amount"), 10, 0) |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
json.NewEncoder(w).Encode("") |
||||
return |
||||
} |
||||
|
||||
msg := &msg_pb.Message{ |
||||
Type: msg_pb.MessageType_LOTTERY_REQUEST, |
||||
Request: &msg_pb.Message_LotteryRequest{ |
||||
LotteryRequest: &msg_pb.LotteryRequest{ |
||||
Type: msg_pb.LotteryRequest_ENTER, |
||||
PrivateKey: key, |
||||
Amount: amount, |
||||
}, |
||||
}, |
||||
} |
||||
res, err := grpcClient.Process(msg) |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
json.NewEncoder(w).Encode("") |
||||
return |
||||
} |
||||
json.NewEncoder(w).Encode(res) |
||||
} |
||||
|
||||
// Result --
|
||||
func Result(w http.ResponseWriter, r *http.Request) { |
||||
w.Header().Set("Content-Type", "application/json") |
||||
key := r.FormValue("key") |
||||
|
||||
json.NewEncoder(w).Encode("") |
||||
msg := &msg_pb.Message{ |
||||
Type: msg_pb.MessageType_LOTTERY_REQUEST, |
||||
Request: &msg_pb.Message_LotteryRequest{ |
||||
LotteryRequest: &msg_pb.LotteryRequest{ |
||||
Type: msg_pb.LotteryRequest_RESULT, |
||||
PrivateKey: key, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
res, err := grpcClient.Process(msg) |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
json.NewEncoder(w).Encode("") |
||||
return |
||||
} |
||||
json.NewEncoder(w).Encode(res) |
||||
} |
||||
|
||||
// PickWinner picks the winner by running pickWinner of smart contract.
|
||||
func PickWinner(w http.ResponseWriter, r *http.Request) { |
||||
w.Header().Set("Content-Type", "application/json") |
||||
|
||||
json.NewEncoder(w).Encode("") |
||||
msg := &msg_pb.Message{ |
||||
Type: msg_pb.MessageType_LOTTERY_REQUEST, |
||||
Request: &msg_pb.Message_LotteryRequest{ |
||||
LotteryRequest: &msg_pb.LotteryRequest{ |
||||
Type: msg_pb.LotteryRequest_PICK_WINNER, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
res, err := grpcClient.Process(msg) |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
json.NewEncoder(w).Encode("") |
||||
return |
||||
} |
||||
json.NewEncoder(w).Encode(res) |
||||
} |
||||
|
||||
func main() { |
||||
addr := net.JoinHostPort("", Port) |
||||
|
||||
router := mux.NewRouter() |
||||
// Set up router for server.
|
||||
router.Path("/enter").Queries("key", "{[0-9A-Fa-fx]*?}", "amount", "[0-9]*").HandlerFunc(Enter).Methods("GET") |
||||
router.Path("/enter").HandlerFunc(Enter) |
||||
|
||||
router.Path("/result").HandlerFunc(Result) |
||||
router.Path("/winner").HandlerFunc(PickWinner) |
||||
|
||||
handlers.AllowedOrigins([]string{"*"}) |
||||
|
||||
server := &http.Server{Addr: addr, Handler: handlers.CORS(handlers.AllowedOrigins([]string{"*"}))(router)} |
||||
fmt.Println("Serving") |
||||
server.ListenAndServe() |
||||
} |
@ -1,65 +0,0 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"math/big" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/rpc" |
||||
"github.com/harmony-one/harmony/hmyclient" |
||||
) |
||||
|
||||
// newRPCClient creates a rpc client with specified node URL.
|
||||
func newRPCClient(url string) *rpc.Client { |
||||
client, err := rpc.Dial(url) |
||||
if err != nil { |
||||
fmt.Errorf("Failed to connect to Ethereum node: %v", err) |
||||
} |
||||
return client |
||||
} |
||||
|
||||
func main() { |
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) |
||||
defer cancelFn() |
||||
rpcClient := newRPCClient("http://localhost:9500") |
||||
if rpcClient == nil { |
||||
fmt.Errorf("Failed to create rpc client") |
||||
} |
||||
client := hmyclient.NewClient(rpcClient) |
||||
if client == nil { |
||||
fmt.Errorf("Failed to create client") |
||||
} |
||||
|
||||
networkID, err := client.NetworkID(ctx) |
||||
if err != nil { |
||||
fmt.Errorf("Failed to get net_version: %v", err) |
||||
} |
||||
fmt.Printf("net_version: %v\n", networkID) |
||||
|
||||
blockNumber, err := client.BlockNumber(ctx) |
||||
if err != nil { |
||||
fmt.Errorf("Failed to get hmy_blockNumber: %v", err) |
||||
} |
||||
fmt.Printf("hmy_blockNumber: %v\n", blockNumber) |
||||
|
||||
block, err := client.BlockByNumber(ctx, new(big.Int).SetUint64(uint64(blockNumber))) |
||||
if err != nil { |
||||
fmt.Errorf("Failed to get hmy_getBlockByNumber %v: %v", blockNumber, err) |
||||
} |
||||
fmt.Printf("hmy_getBlockByNumber(%v):\n", blockNumber) |
||||
fmt.Printf("number: %v\n", block.Number().Text(16)) |
||||
fmt.Printf("hash: %v\n", block.Hash().String()) |
||||
fmt.Printf("parentHash: %v\n", block.ParentHash().String()) |
||||
fmt.Printf("timestamp: %v\n", block.Time().Text(16)) |
||||
fmt.Printf("size: %v\n", block.Size()) |
||||
fmt.Printf("miner: %v\n", block.Coinbase().String()) |
||||
fmt.Printf("receiptsRoot: %v\n", block.ReceiptHash().String()) |
||||
fmt.Printf("transactionsRoot: %v\n", block.TxHash().String()) |
||||
|
||||
block, err = client.BlockByNumber(ctx, nil) |
||||
if err != nil { |
||||
fmt.Errorf("Failed to get block: %v", err) |
||||
} |
||||
fmt.Printf("hmy_getBlockByNumber(latest): %v", block) |
||||
} |
@ -1,49 +0,0 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"crypto/rand" |
||||
"encoding/hex" |
||||
"fmt" |
||||
"io" |
||||
"os" |
||||
"strconv" |
||||
|
||||
crypto2 "github.com/ethereum/go-ethereum/crypto" |
||||
|
||||
"github.com/harmony-one/harmony/internal/common" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
) |
||||
|
||||
var ( |
||||
version string |
||||
builtBy string |
||||
builtAt string |
||||
commit string |
||||
) |
||||
|
||||
func main() { |
||||
if len(os.Args) < 2 { |
||||
fmt.Println("Please provide # of keys to be generated") |
||||
os.Exit(1) |
||||
} |
||||
|
||||
if n, err := strconv.Atoi(os.Args[1]); err == nil { |
||||
for i := 0; i < n; i++ { |
||||
randomBytes := [32]byte{} |
||||
_, err := io.ReadFull(rand.Reader, randomBytes[:]) |
||||
if err != nil { |
||||
fmt.Println("Failed to get randomness for the private key...") |
||||
return |
||||
} |
||||
priKey, err := crypto2.GenerateKey() |
||||
if err != nil { |
||||
utils.FatalErrMsg(err, "cannot generate wallet key") |
||||
} |
||||
crypto2.FromECDSA(priKey) |
||||
|
||||
fmt.Printf("{Address: \"%s\", Private: \"%s\", Public: \"%s\"},"+"\n", common.MustAddressToBech32(crypto2.PubkeyToAddress(priKey.PublicKey)), hex.EncodeToString(crypto2.FromECDSA(priKey)), common.MustAddressToBech32(crypto2.PubkeyToAddress(priKey.PublicKey))) |
||||
} |
||||
} else { |
||||
fmt.Println("Unable to parse # as the argument.") |
||||
} |
||||
} |
@ -1,309 +0,0 @@ |
||||
// Code generated - DO NOT EDIT.
|
||||
// This file is a generated binding and any manual changes will be lost.
|
||||
|
||||
package contracts |
||||
|
||||
import ( |
||||
"math/big" |
||||
"strings" |
||||
|
||||
ethereum "github.com/ethereum/go-ethereum" |
||||
"github.com/ethereum/go-ethereum/accounts/abi" |
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind" |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/event" |
||||
) |
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var ( |
||||
_ = big.NewInt |
||||
_ = strings.NewReader |
||||
_ = ethereum.NotFound |
||||
_ = abi.U256 |
||||
_ = bind.Bind |
||||
_ = common.Big1 |
||||
_ = types.BloomLookup |
||||
_ = event.NewSubscription |
||||
) |
||||
|
||||
// LotteryABI is the input ABI used to generate the binding from.
|
||||
const LotteryABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"manager\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"pickWinner\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getPlayers\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"enter\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"players\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]" |
||||
|
||||
// LotteryBin is the compiled bytecode used for deploying new contracts.
|
||||
const LotteryBin = `0x608060405234801561001057600080fd5b50600080546001600160a01b031916331790556104d8806100326000396000f3fe60806040526004361061004a5760003560e01c8063481c6a751461004f5780635d495aea146100805780638b5b9ccc1461008a578063e97dcb62146100ef578063f71d96cb146100f7575b600080fd5b34801561005b57600080fd5b50610064610121565b604080516001600160a01b039092168252519081900360200190f35b610088610130565b005b34801561009657600080fd5b5061009f61028c565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156100db5781810151838201526020016100c3565b505050509050019250505060405180910390f35b6100886102ef565b34801561010357600080fd5b506100646004803603602081101561011a57600080fd5b50356103c1565b6000546001600160a01b031681565b60005460408051808201909152601381527f556e617574686f72697a656420416363657373000000000000000000000000006020820152906001600160a01b031633146101fe57604051600160e51b62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b838110156101c35781810151838201526020016101ab565b50505050905090810190601f1680156101f05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060015460009061020d6103e9565b81151561021657fe5b06905060018181548110151561022857fe5b60009182526020822001546040516001600160a01b0390911691303180156108fc02929091818181858888f1935050505015801561026a573d6000803e3d6000fd5b50604080516000815260208101918290525161028891600191610423565b5050565b606060018054806020026020016040519081016040528092919081815260200182805480156102e457602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116102c6575b505050505090505b90565b60408051808201909152601181527f496e73756666696369656e742046756e64000000000000000000000000000000602082015267016345785d8a0000341161037d57604051600160e51b62461bcd028152600401808060200182810382528381815181526020019150805190602001908083836000838110156101c35781810151838201526020016101ab565b506001805480820182556000919091527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b03191633179055565b60018054829081106103cf57fe5b6000918252602090912001546001600160a01b0316905081565b604080514260208083019190915233606090811b8385015230901b6054830152825160488184030181526068909201909252805191012090565b828054828255906000526020600020908101928215610478579160200282015b8281111561047857825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190610443565b50610484929150610488565b5090565b6102ec91905b808211156104845780546001600160a01b031916815560010161048e56fea165627a7a7230582045d0ba2aa43cdf6d10a8a4c13e1bbe1b2860c498f3727cb51b84378e1d6e99120029` |
||||
|
||||
// DeployLottery deploys a new Ethereum contract, binding an instance of Lottery to it.
|
||||
func DeployLottery(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Lottery, error) { |
||||
parsed, err := abi.JSON(strings.NewReader(LotteryABI)) |
||||
if err != nil { |
||||
return common.Address{}, nil, nil, err |
||||
} |
||||
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(LotteryBin), backend) |
||||
if err != nil { |
||||
return common.Address{}, nil, nil, err |
||||
} |
||||
return address, tx, &Lottery{LotteryCaller: LotteryCaller{contract: contract}, LotteryTransactor: LotteryTransactor{contract: contract}, LotteryFilterer: LotteryFilterer{contract: contract}}, nil |
||||
} |
||||
|
||||
// Lottery is an auto generated Go binding around an Ethereum contract.
|
||||
type Lottery struct { |
||||
LotteryCaller // Read-only binding to the contract
|
||||
LotteryTransactor // Write-only binding to the contract
|
||||
LotteryFilterer // Log filterer for contract events
|
||||
} |
||||
|
||||
// LotteryCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
type LotteryCaller struct { |
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
} |
||||
|
||||
// LotteryTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||
type LotteryTransactor struct { |
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
} |
||||
|
||||
// LotteryFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||
type LotteryFilterer struct { |
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
} |
||||
|
||||
// LotterySession is an auto generated Go binding around an Ethereum contract,
|
||||
// with pre-set call and transact options.
|
||||
type LotterySession struct { |
||||
Contract *Lottery // Generic contract binding to set the session for
|
||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||
} |
||||
|
||||
// LotteryCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||
// with pre-set call options.
|
||||
type LotteryCallerSession struct { |
||||
Contract *LotteryCaller // Generic contract caller binding to set the session for
|
||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||
} |
||||
|
||||
// LotteryTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||
// with pre-set transact options.
|
||||
type LotteryTransactorSession struct { |
||||
Contract *LotteryTransactor // Generic contract transactor binding to set the session for
|
||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||
} |
||||
|
||||
// LotteryRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||
type LotteryRaw struct { |
||||
Contract *Lottery // Generic contract binding to access the raw methods on
|
||||
} |
||||
|
||||
// LotteryCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||
type LotteryCallerRaw struct { |
||||
Contract *LotteryCaller // Generic read-only contract binding to access the raw methods on
|
||||
} |
||||
|
||||
// LotteryTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||
type LotteryTransactorRaw struct { |
||||
Contract *LotteryTransactor // Generic write-only contract binding to access the raw methods on
|
||||
} |
||||
|
||||
// NewLottery creates a new instance of Lottery, bound to a specific deployed contract.
|
||||
func NewLottery(address common.Address, backend bind.ContractBackend) (*Lottery, error) { |
||||
contract, err := bindLottery(address, backend, backend, backend) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &Lottery{LotteryCaller: LotteryCaller{contract: contract}, LotteryTransactor: LotteryTransactor{contract: contract}, LotteryFilterer: LotteryFilterer{contract: contract}}, nil |
||||
} |
||||
|
||||
// NewLotteryCaller creates a new read-only instance of Lottery, bound to a specific deployed contract.
|
||||
func NewLotteryCaller(address common.Address, caller bind.ContractCaller) (*LotteryCaller, error) { |
||||
contract, err := bindLottery(address, caller, nil, nil) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &LotteryCaller{contract: contract}, nil |
||||
} |
||||
|
||||
// NewLotteryTransactor creates a new write-only instance of Lottery, bound to a specific deployed contract.
|
||||
func NewLotteryTransactor(address common.Address, transactor bind.ContractTransactor) (*LotteryTransactor, error) { |
||||
contract, err := bindLottery(address, nil, transactor, nil) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &LotteryTransactor{contract: contract}, nil |
||||
} |
||||
|
||||
// NewLotteryFilterer creates a new log filterer instance of Lottery, bound to a specific deployed contract.
|
||||
func NewLotteryFilterer(address common.Address, filterer bind.ContractFilterer) (*LotteryFilterer, error) { |
||||
contract, err := bindLottery(address, nil, nil, filterer) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &LotteryFilterer{contract: contract}, nil |
||||
} |
||||
|
||||
// bindLottery binds a generic wrapper to an already deployed contract.
|
||||
func bindLottery(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { |
||||
parsed, err := abi.JSON(strings.NewReader(LotteryABI)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil |
||||
} |
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
// sets the output to result. The result type might be a single field for simple
|
||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||
// returns.
|
||||
func (_Lottery *LotteryRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||
return _Lottery.Contract.LotteryCaller.contract.Call(opts, result, method, params...) |
||||
} |
||||
|
||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||
// its default method if one is available.
|
||||
func (_Lottery *LotteryRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||
return _Lottery.Contract.LotteryTransactor.contract.Transfer(opts) |
||||
} |
||||
|
||||
// Transact invokes the (paid) contract method with params as input values.
|
||||
func (_Lottery *LotteryRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||
return _Lottery.Contract.LotteryTransactor.contract.Transact(opts, method, params...) |
||||
} |
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
// sets the output to result. The result type might be a single field for simple
|
||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||
// returns.
|
||||
func (_Lottery *LotteryCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { |
||||
return _Lottery.Contract.contract.Call(opts, result, method, params...) |
||||
} |
||||
|
||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||
// its default method if one is available.
|
||||
func (_Lottery *LotteryTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||
return _Lottery.Contract.contract.Transfer(opts) |
||||
} |
||||
|
||||
// Transact invokes the (paid) contract method with params as input values.
|
||||
func (_Lottery *LotteryTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { |
||||
return _Lottery.Contract.contract.Transact(opts, method, params...) |
||||
} |
||||
|
||||
// GetPlayers is a free data retrieval call binding the contract method 0x8b5b9ccc.
|
||||
//
|
||||
// Solidity: function getPlayers() constant returns(address[])
|
||||
func (_Lottery *LotteryCaller) GetPlayers(opts *bind.CallOpts) ([]common.Address, error) { |
||||
var ( |
||||
ret0 = new([]common.Address) |
||||
) |
||||
out := ret0 |
||||
err := _Lottery.contract.Call(opts, out, "getPlayers") |
||||
return *ret0, err |
||||
} |
||||
|
||||
// GetPlayers is a free data retrieval call binding the contract method 0x8b5b9ccc.
|
||||
//
|
||||
// Solidity: function getPlayers() constant returns(address[])
|
||||
func (_Lottery *LotterySession) GetPlayers() ([]common.Address, error) { |
||||
return _Lottery.Contract.GetPlayers(&_Lottery.CallOpts) |
||||
} |
||||
|
||||
// GetPlayers is a free data retrieval call binding the contract method 0x8b5b9ccc.
|
||||
//
|
||||
// Solidity: function getPlayers() constant returns(address[])
|
||||
func (_Lottery *LotteryCallerSession) GetPlayers() ([]common.Address, error) { |
||||
return _Lottery.Contract.GetPlayers(&_Lottery.CallOpts) |
||||
} |
||||
|
||||
// Manager is a free data retrieval call binding the contract method 0x481c6a75.
|
||||
//
|
||||
// Solidity: function manager() constant returns(address)
|
||||
func (_Lottery *LotteryCaller) Manager(opts *bind.CallOpts) (common.Address, error) { |
||||
var ( |
||||
ret0 = new(common.Address) |
||||
) |
||||
out := ret0 |
||||
err := _Lottery.contract.Call(opts, out, "manager") |
||||
return *ret0, err |
||||
} |
||||
|
||||
// Manager is a free data retrieval call binding the contract method 0x481c6a75.
|
||||
//
|
||||
// Solidity: function manager() constant returns(address)
|
||||
func (_Lottery *LotterySession) Manager() (common.Address, error) { |
||||
return _Lottery.Contract.Manager(&_Lottery.CallOpts) |
||||
} |
||||
|
||||
// Manager is a free data retrieval call binding the contract method 0x481c6a75.
|
||||
//
|
||||
// Solidity: function manager() constant returns(address)
|
||||
func (_Lottery *LotteryCallerSession) Manager() (common.Address, error) { |
||||
return _Lottery.Contract.Manager(&_Lottery.CallOpts) |
||||
} |
||||
|
||||
// Players is a free data retrieval call binding the contract method 0xf71d96cb.
|
||||
//
|
||||
// Solidity: function players(uint256 ) constant returns(address)
|
||||
func (_Lottery *LotteryCaller) Players(opts *bind.CallOpts, arg0 *big.Int) (common.Address, error) { |
||||
var ( |
||||
ret0 = new(common.Address) |
||||
) |
||||
out := ret0 |
||||
err := _Lottery.contract.Call(opts, out, "players", arg0) |
||||
return *ret0, err |
||||
} |
||||
|
||||
// Players is a free data retrieval call binding the contract method 0xf71d96cb.
|
||||
//
|
||||
// Solidity: function players(uint256 ) constant returns(address)
|
||||
func (_Lottery *LotterySession) Players(arg0 *big.Int) (common.Address, error) { |
||||
return _Lottery.Contract.Players(&_Lottery.CallOpts, arg0) |
||||
} |
||||
|
||||
// Players is a free data retrieval call binding the contract method 0xf71d96cb.
|
||||
//
|
||||
// Solidity: function players(uint256 ) constant returns(address)
|
||||
func (_Lottery *LotteryCallerSession) Players(arg0 *big.Int) (common.Address, error) { |
||||
return _Lottery.Contract.Players(&_Lottery.CallOpts, arg0) |
||||
} |
||||
|
||||
// Enter is a paid mutator transaction binding the contract method 0xe97dcb62.
|
||||
//
|
||||
// Solidity: function enter() returns()
|
||||
func (_Lottery *LotteryTransactor) Enter(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||
return _Lottery.contract.Transact(opts, "enter") |
||||
} |
||||
|
||||
// Enter is a paid mutator transaction binding the contract method 0xe97dcb62.
|
||||
//
|
||||
// Solidity: function enter() returns()
|
||||
func (_Lottery *LotterySession) Enter() (*types.Transaction, error) { |
||||
return _Lottery.Contract.Enter(&_Lottery.TransactOpts) |
||||
} |
||||
|
||||
// Enter is a paid mutator transaction binding the contract method 0xe97dcb62.
|
||||
//
|
||||
// Solidity: function enter() returns()
|
||||
func (_Lottery *LotteryTransactorSession) Enter() (*types.Transaction, error) { |
||||
return _Lottery.Contract.Enter(&_Lottery.TransactOpts) |
||||
} |
||||
|
||||
// PickWinner is a paid mutator transaction binding the contract method 0x5d495aea.
|
||||
//
|
||||
// Solidity: function pickWinner() returns()
|
||||
func (_Lottery *LotteryTransactor) PickWinner(opts *bind.TransactOpts) (*types.Transaction, error) { |
||||
return _Lottery.contract.Transact(opts, "pickWinner") |
||||
} |
||||
|
||||
// PickWinner is a paid mutator transaction binding the contract method 0x5d495aea.
|
||||
//
|
||||
// Solidity: function pickWinner() returns()
|
||||
func (_Lottery *LotterySession) PickWinner() (*types.Transaction, error) { |
||||
return _Lottery.Contract.PickWinner(&_Lottery.TransactOpts) |
||||
} |
||||
|
||||
// PickWinner is a paid mutator transaction binding the contract method 0x5d495aea.
|
||||
//
|
||||
// Solidity: function pickWinner() returns()
|
||||
func (_Lottery *LotteryTransactorSession) PickWinner() (*types.Transaction, error) { |
||||
return _Lottery.Contract.PickWinner(&_Lottery.TransactOpts) |
||||
} |
@ -1,52 +0,0 @@ |
||||
pragma solidity >=0.4.22; |
||||
|
||||
contract Lottery { |
||||
string internal constant INSUFFICIENT_FUND_MESSAGE = "Insufficient Fund"; |
||||
string internal constant RESTRICTED_MESSAGE = "Unauthorized Access"; |
||||
|
||||
address public manager; // The adress of the owner of this contract |
||||
address payable[] public players; // The players of current session |
||||
|
||||
constructor() public { |
||||
manager = msg.sender; |
||||
} |
||||
|
||||
/** |
||||
* @dev The player enters into the current lottery session by |
||||
* paying at least 0.1 token. |
||||
*/ |
||||
function enter() public payable { |
||||
require(msg.value > .1 ether, INSUFFICIENT_FUND_MESSAGE); |
||||
|
||||
players.push(msg.sender); |
||||
} |
||||
|
||||
/** |
||||
* @dev Random number used for picking the winner. |
||||
*/ |
||||
function random() private view returns (uint) { |
||||
return uint(keccak256(abi.encodePacked(now, msg.sender, this))); |
||||
} |
||||
|
||||
/** |
||||
* @dev Randomly select one of the players in current session as winner |
||||
* and send all the token in smart contract balance to it. |
||||
*/ |
||||
function pickWinner() public payable restricted { |
||||
uint index = random() % players.length; |
||||
players[index].transfer(address(this).balance); |
||||
players = new address payable[](0); |
||||
} |
||||
|
||||
modifier restricted() { |
||||
require(msg.sender == manager, RESTRICTED_MESSAGE); |
||||
_; |
||||
} |
||||
|
||||
/** |
||||
* @dev Returns a list of all players in the current session. |
||||
*/ |
||||
function getPlayers() public view returns (address payable[] memory) { |
||||
return players; |
||||
} |
||||
} |
@ -1,3 +0,0 @@ |
||||
# abigen -sol Lottery.sol -out Lottery.go --pkg contracts |
||||
abigen -sol Puzzle.sol -out Puzzle.go --pkg contracts |
||||
# abigen -sol Faucet.sol -out Faucet.go --pkg contracts |
@ -1,215 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
"errors" |
||||
"sync" |
||||
|
||||
"github.com/harmony-one/harmony/crypto/hash" |
||||
|
||||
protobuf "github.com/golang/protobuf/proto" |
||||
"github.com/harmony-one/bls/ffi/go/bls" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/core/types" |
||||
bls_cosi "github.com/harmony-one/harmony/crypto/bls" |
||||
"github.com/harmony-one/harmony/crypto/vrf" |
||||
"github.com/harmony-one/harmony/crypto/vrf/p256" |
||||
"github.com/harmony-one/harmony/p2p" |
||||
) |
||||
|
||||
// DRand is the main struct which contains state for the distributed randomness protocol.
|
||||
type DRand struct { |
||||
vrfs *map[string][]byte // Key is the address hex
|
||||
bitmap *bls_cosi.Mask |
||||
pRand *[32]byte |
||||
rand *[32]byte |
||||
ConfirmedBlockChannel chan *types.Block // Channel to receive confirmed blocks
|
||||
PRndChannel chan []byte // Channel to send pRnd (preimage of randomness resulting from combined vrf randomnesses) to consensus. The first 32 bytes are randomness, the rest is for bitmap.
|
||||
RndChannel chan [64]byte // Channel for DRG protocol to send the final randomness to consensus. The first 32 bytes are the randomness and the last 32 bytes are the hash of the block where the corresponding pRnd was generated
|
||||
|
||||
// global consensus mutex
|
||||
mutex sync.Mutex |
||||
|
||||
// Leader's address
|
||||
leader p2p.Peer |
||||
|
||||
// Public keys of the committee including leader and validators
|
||||
PublicKeys []*bls.PublicKey |
||||
// The addresses of my committee
|
||||
CommitteePublicKeys map[string]bool |
||||
pubKeyLock sync.Mutex |
||||
|
||||
// private/public keys of current node
|
||||
priKey *bls.SecretKey |
||||
pubKey *bls.PublicKey |
||||
// VRF private and public key
|
||||
// TODO: directly use signature signing key (BLS) for vrf
|
||||
vrfPriKey *vrf.PrivateKey |
||||
vrfPubKey *vrf.PublicKey |
||||
|
||||
// Whether I am leader. False means I am validator
|
||||
IsLeader bool |
||||
|
||||
// Leader or validator address
|
||||
SelfAddress string |
||||
|
||||
// The p2p host used to send/receive p2p messages
|
||||
host p2p.Host |
||||
|
||||
// Shard Id which this node belongs to
|
||||
ShardID uint32 |
||||
|
||||
// Blockhash - 32 byte
|
||||
blockHash [32]byte |
||||
} |
||||
|
||||
// New creates a new dRand object
|
||||
func New(host p2p.Host, ShardID uint32, peers []p2p.Peer, leader p2p.Peer, confirmedBlockChannel chan *types.Block, blsPriKey *bls.SecretKey) *DRand { |
||||
dRand := DRand{} |
||||
dRand.host = host |
||||
|
||||
if confirmedBlockChannel != nil { |
||||
dRand.ConfirmedBlockChannel = confirmedBlockChannel |
||||
} |
||||
|
||||
dRand.PRndChannel = make(chan []byte) |
||||
dRand.RndChannel = make(chan [64]byte) |
||||
|
||||
selfPeer := host.GetSelfPeer() |
||||
if leader.Port == selfPeer.Port && leader.IP == selfPeer.IP { |
||||
dRand.IsLeader = true |
||||
} else { |
||||
dRand.IsLeader = false |
||||
} |
||||
|
||||
dRand.leader = leader |
||||
dRand.CommitteePublicKeys = map[string]bool{} |
||||
for _, peer := range peers { |
||||
dRand.CommitteePublicKeys[peer.ConsensusPubKey.SerializeToHexStr()] = true |
||||
} |
||||
|
||||
dRand.vrfs = &map[string][]byte{} |
||||
|
||||
// Initialize cosign bitmap
|
||||
allPublicKeys := make([]*bls.PublicKey, 0) |
||||
for _, validatorPeer := range peers { |
||||
allPublicKeys = append(allPublicKeys, validatorPeer.ConsensusPubKey) |
||||
} |
||||
allPublicKeys = append(allPublicKeys, leader.ConsensusPubKey) |
||||
|
||||
dRand.PublicKeys = allPublicKeys |
||||
|
||||
bitmap, _ := bls_cosi.NewMask(dRand.PublicKeys, dRand.leader.ConsensusPubKey) |
||||
dRand.bitmap = bitmap |
||||
|
||||
dRand.pRand = nil |
||||
dRand.rand = nil |
||||
|
||||
// For now use socket address as ID
|
||||
dRand.SelfAddress = selfPeer.ConsensusPubKey.SerializeToHexStr() |
||||
|
||||
// Set private key for myself so that I can sign messages.
|
||||
if blsPriKey != nil { |
||||
dRand.priKey = blsPriKey |
||||
dRand.pubKey = blsPriKey.GetPublicKey() |
||||
} |
||||
|
||||
// VRF keys
|
||||
priKey, pubKey := p256.GenerateKey() |
||||
dRand.vrfPriKey = &priKey |
||||
dRand.vrfPubKey = &pubKey |
||||
dRand.ShardID = ShardID |
||||
|
||||
return &dRand |
||||
} |
||||
|
||||
// Sign on the drand message signature field.
|
||||
func (dRand *DRand) signDRandMessage(message *msg_pb.Message) error { |
||||
message.Signature = nil |
||||
marshaledMessage, err := protobuf.Marshal(message) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
// 64 byte of signature on previous data
|
||||
hash := hash.Keccak256(marshaledMessage) |
||||
signature := dRand.priKey.SignHash(hash[:]) |
||||
|
||||
message.Signature = signature.Serialize() |
||||
return nil |
||||
} |
||||
|
||||
// Signs the drand message and returns the marshaled message.
|
||||
func (dRand *DRand) signAndMarshalDRandMessage(message *msg_pb.Message) ([]byte, error) { |
||||
err := dRand.signDRandMessage(message) |
||||
if err != nil { |
||||
return []byte{}, err |
||||
} |
||||
|
||||
marshaledMessage, err := protobuf.Marshal(message) |
||||
if err != nil { |
||||
return []byte{}, err |
||||
} |
||||
return marshaledMessage, nil |
||||
} |
||||
|
||||
func (dRand *DRand) vrf(blockHash [32]byte) (rand [32]byte, proof []byte) { |
||||
rand, proof = (*dRand.vrfPriKey).Evaluate(blockHash[:]) |
||||
return |
||||
} |
||||
|
||||
// Verify the signature of the message are valid from the signer's public key.
|
||||
func verifyMessageSig(signerPubKey *bls.PublicKey, message *msg_pb.Message) error { |
||||
signature := message.Signature |
||||
message.Signature = nil |
||||
messageBytes, err := protobuf.Marshal(message) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
msgSig := bls.Sign{} |
||||
err = msgSig.Deserialize(signature) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
msgHash := hash.Keccak256(messageBytes) |
||||
if !msgSig.VerifyHash(signerPubKey, msgHash[:]) { |
||||
return errors.New("failed to verify the signature") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// IsValidatorInCommittee returns whether the given validator BLS address is part of my committee
|
||||
func (dRand *DRand) IsValidatorInCommittee(validatorBLSPubKey string) bool { |
||||
_, ok := dRand.CommitteePublicKeys[validatorBLSPubKey] |
||||
return ok |
||||
} |
||||
|
||||
// ResetState resets the state of the randomness protocol
|
||||
func (dRand *DRand) ResetState() { |
||||
dRand.vrfs = &map[string][]byte{} |
||||
|
||||
bitmap, _ := bls_cosi.NewMask(dRand.PublicKeys, dRand.leader.ConsensusPubKey) |
||||
dRand.bitmap = bitmap |
||||
dRand.pRand = nil |
||||
dRand.rand = nil |
||||
} |
||||
|
||||
// SetLeaderPubKey deserialize the public key of drand leader
|
||||
func (dRand *DRand) SetLeaderPubKey(k []byte) error { |
||||
dRand.leader.ConsensusPubKey = &bls.PublicKey{} |
||||
return dRand.leader.ConsensusPubKey.Deserialize(k) |
||||
} |
||||
|
||||
// UpdatePublicKeys updates the PublicKeys variable, protected by a mutex
|
||||
func (dRand *DRand) UpdatePublicKeys(pubKeys []*bls.PublicKey) int { |
||||
dRand.pubKeyLock.Lock() |
||||
dRand.PublicKeys = append(pubKeys[:0:0], pubKeys...) |
||||
dRand.CommitteePublicKeys = map[string]bool{} |
||||
for _, pubKey := range dRand.PublicKeys { |
||||
dRand.CommitteePublicKeys[pubKey.SerializeToHexStr()] = true |
||||
} |
||||
// TODO: use pubkey to identify leader rather than p2p.Peer.
|
||||
dRand.leader = p2p.Peer{ConsensusPubKey: pubKeys[0]} |
||||
dRand.pubKeyLock.Unlock() |
||||
|
||||
return len(dRand.PublicKeys) |
||||
} |
@ -1,203 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
"bytes" |
||||
"time" |
||||
|
||||
protobuf "github.com/golang/protobuf/proto" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/core" |
||||
"github.com/harmony-one/harmony/core/types" |
||||
"github.com/harmony-one/harmony/crypto/bls" |
||||
"github.com/harmony-one/harmony/crypto/vdf" |
||||
"github.com/harmony-one/harmony/crypto/vrf/p256" |
||||
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
"github.com/harmony-one/harmony/p2p/host" |
||||
"github.com/harmony-one/harmony/shard" |
||||
) |
||||
|
||||
const ( |
||||
vdfDifficulty = 5000000 // This takes about 20s to finish the vdf
|
||||
) |
||||
|
||||
// WaitForEpochBlock waits for the first epoch block to run DRG on
|
||||
func (dRand *DRand) WaitForEpochBlock(blockChannel chan *types.Block, stopChan chan struct{}, stoppedChan chan struct{}) { |
||||
go func() { |
||||
defer close(stoppedChan) |
||||
for { |
||||
select { |
||||
default: |
||||
// keep waiting for epoch block
|
||||
newBlock := <-blockChannel |
||||
if shard.Schedule.IsLastBlock(newBlock.Number().Uint64()) { |
||||
dRand.init(newBlock) |
||||
} |
||||
// TODO: use real vrf
|
||||
pRnd := [32]byte{} //newBlock.Header().Vrf
|
||||
zeros := [32]byte{} |
||||
if core.IsEpochBlock(newBlock) && !bytes.Equal(pRnd[:], zeros[:]) { |
||||
// The epoch block should contain the randomness preimage pRnd
|
||||
// TODO ek – limit concurrency
|
||||
go func() { |
||||
vdf := vdf.New(vdfDifficulty, pRnd) |
||||
outputChannel := vdf.GetOutputChannel() |
||||
start := time.Now() |
||||
vdf.Execute() |
||||
duration := time.Now().Sub(start) |
||||
utils.Logger().Info().Dur("duration", duration).Msg("VDF computation finished") |
||||
output := <-outputChannel |
||||
|
||||
rndBytes := [64]byte{} // The first 32 bytes are the randomness and the last 32 bytes are the hash of the block where the corresponding pRnd was generated
|
||||
copy(rndBytes[:32], output[:]) |
||||
|
||||
blockHash := newBlock.Hash() |
||||
copy(rndBytes[32:], blockHash[:]) |
||||
|
||||
dRand.RndChannel <- rndBytes |
||||
}() |
||||
} |
||||
case <-stopChan: |
||||
return |
||||
} |
||||
} |
||||
}() |
||||
} |
||||
|
||||
func (dRand *DRand) init(epochBlock *types.Block) { |
||||
utils.Logger().Debug().Msg("INITING DRAND") |
||||
dRand.ResetState() |
||||
// Copy over block hash and block header data
|
||||
blockHash := epochBlock.Hash() |
||||
copy(dRand.blockHash[:], blockHash[:]) |
||||
|
||||
msgToSend := dRand.constructInitMessage() |
||||
|
||||
// Leader commit vrf itself
|
||||
rand, proof := dRand.vrf(dRand.blockHash) |
||||
|
||||
(*dRand.vrfs)[dRand.SelfAddress] = append(rand[:], proof...) |
||||
|
||||
utils.Logger().Info(). |
||||
Hex("msg", msgToSend). |
||||
Str("leader.PubKey", dRand.leader.ConsensusPubKey.SerializeToHexStr()). |
||||
Msg("[DRG] sent init") |
||||
dRand.host.SendMessageToGroups([]nodeconfig.GroupID{nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(dRand.ShardID))}, host.ConstructP2pMessage(byte(17), msgToSend)) |
||||
} |
||||
|
||||
// ProcessMessageLeader dispatches messages for the leader to corresponding processors.
|
||||
func (dRand *DRand) ProcessMessageLeader(payload []byte) { |
||||
message := &msg_pb.Message{} |
||||
err := protobuf.Unmarshal(payload, message) |
||||
|
||||
if err != nil { |
||||
utils.Logger().Error().Err(err).Interface("dRand", dRand).Msg("Failed to unmarshal message payload") |
||||
} |
||||
|
||||
if message.GetDrand().ShardId != dRand.ShardID { |
||||
utils.Logger().Warn(). |
||||
Uint32("myShardId", dRand.ShardID). |
||||
Uint32("receivedShardId", message.GetDrand().ShardId). |
||||
Msg("Received drand message from different shard") |
||||
return |
||||
} |
||||
|
||||
switch message.Type { |
||||
case msg_pb.MessageType_DRAND_COMMIT: |
||||
dRand.processCommitMessage(message) |
||||
default: |
||||
utils.Logger().Error(). |
||||
Uint32("msgType", uint32(message.Type)). |
||||
Interface("dRand", dRand). |
||||
Msg("Unexpected message type") |
||||
} |
||||
} |
||||
|
||||
// ProcessMessageValidator dispatches validator's consensus message.
|
||||
func (dRand *DRand) processCommitMessage(message *msg_pb.Message) { |
||||
utils.Logger().Info().Msg("[DRG] Leader received commit") |
||||
if message.Type != msg_pb.MessageType_DRAND_COMMIT { |
||||
utils.Logger().Error(). |
||||
Uint32("expected", uint32(msg_pb.MessageType_DRAND_COMMIT)). |
||||
Uint32("got", uint32(message.Type)). |
||||
Msg("Wrong message type received") |
||||
return |
||||
} |
||||
|
||||
dRand.mutex.Lock() |
||||
defer dRand.mutex.Unlock() |
||||
|
||||
drandMsg := message.GetDrand() |
||||
|
||||
senderPubKey, err := bls.BytesToBLSPublicKey(drandMsg.SenderPubkey) |
||||
if err != nil { |
||||
utils.Logger().Debug().Err(err).Msg("Failed to deserialize BLS public key") |
||||
return |
||||
} |
||||
validatorAddress := senderPubKey.SerializeToHexStr() |
||||
|
||||
if !dRand.IsValidatorInCommittee(validatorAddress) { |
||||
utils.Logger().Error().Str("validatorAddress", validatorAddress).Msg("Invalid validator") |
||||
return |
||||
} |
||||
|
||||
vrfs := dRand.vrfs |
||||
if len((*vrfs)) >= ((len(dRand.PublicKeys))/3 + 1) { |
||||
utils.Logger().Debug(). |
||||
Str("validatorAddress", validatorAddress).Msg("Received additional randomness commit message") |
||||
return |
||||
} |
||||
|
||||
// Verify message signature
|
||||
err = verifyMessageSig(senderPubKey, message) |
||||
if err != nil { |
||||
utils.Logger().Debug(). |
||||
Err(err).Str("PubKey", senderPubKey.SerializeToHexStr()).Msg("[DRAND] failed to verify the message signature") |
||||
return |
||||
} |
||||
|
||||
rand := drandMsg.Payload[:32] |
||||
proof := drandMsg.Payload[32 : len(drandMsg.Payload)-64] |
||||
pubKeyBytes := drandMsg.Payload[len(drandMsg.Payload)-64:] |
||||
_, vrfPubKey := p256.GenerateKey() |
||||
vrfPubKey.Deserialize(pubKeyBytes) |
||||
|
||||
expectedRand, err := vrfPubKey.ProofToHash(dRand.blockHash[:], proof) |
||||
|
||||
if err != nil || !bytes.Equal(expectedRand[:], rand) { |
||||
utils.Logger().Error(). |
||||
Err(err). |
||||
Str("validatorAddress", validatorAddress). |
||||
Hex("expectedRand", expectedRand[:]). |
||||
Hex("receivedRand", rand[:]). |
||||
Msg("[DRAND] Failed to verify the VRF") |
||||
return |
||||
} |
||||
|
||||
utils.Logger().Debug(). |
||||
Int("numReceivedSoFar", len((*vrfs))). |
||||
Str("validatorAddress", validatorAddress). |
||||
Int("PublicKeys", len(dRand.PublicKeys)). |
||||
Msg("Received new VRF commit") |
||||
|
||||
(*vrfs)[validatorAddress] = drandMsg.Payload |
||||
dRand.bitmap.SetKey(senderPubKey, true) // Set the bitmap indicating that this validator signed.
|
||||
|
||||
if len((*vrfs)) >= ((len(dRand.PublicKeys))/3 + 1) { |
||||
// Construct pRand and initiate consensus on it
|
||||
utils.Logger().Debug(). |
||||
Int("numReceivedSoFar", len((*vrfs))). |
||||
Str("validatorAddress", validatorAddress). |
||||
Int("PublicKeys", len(dRand.PublicKeys)). |
||||
Msg("[DRAND] {BINGO} Received enough randomness commit") |
||||
|
||||
pRnd := [32]byte{} |
||||
// Bitwise XOR on all the submitted vrfs
|
||||
for _, vrf := range *vrfs { |
||||
for i := 0; i < len(pRnd); i++ { |
||||
pRnd[i] = pRnd[i] ^ vrf[i] |
||||
} |
||||
} |
||||
dRand.PRndChannel <- append(pRnd[:], dRand.bitmap.Bitmap...) |
||||
} |
||||
} |
@ -1,29 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
"github.com/harmony-one/harmony/api/proto" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
) |
||||
|
||||
// Constructs the init message
|
||||
func (dRand *DRand) constructInitMessage() []byte { |
||||
message := &msg_pb.Message{ |
||||
ServiceType: msg_pb.ServiceType_DRAND, |
||||
Type: msg_pb.MessageType_DRAND_INIT, |
||||
Request: &msg_pb.Message_Drand{ |
||||
Drand: &msg_pb.DrandRequest{}, |
||||
}, |
||||
} |
||||
|
||||
drandMsg := message.GetDrand() |
||||
drandMsg.SenderPubkey = dRand.pubKey.Serialize() |
||||
drandMsg.BlockHash = dRand.blockHash[:] |
||||
drandMsg.ShardId = dRand.ShardID |
||||
// Don't need the payload in init message
|
||||
marshaledMessage, err := dRand.signAndMarshalDRandMessage(message) |
||||
if err != nil { |
||||
utils.Logger().Error().Err(err).Msg("Failed to sign and marshal the init message") |
||||
} |
||||
return proto.ConstructDRandMessage(marshaledMessage) |
||||
} |
@ -1,58 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/harmony-one/harmony/crypto/bls" |
||||
|
||||
protobuf "github.com/golang/protobuf/proto" |
||||
"github.com/harmony-one/harmony/api/proto" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
"github.com/harmony-one/harmony/p2p" |
||||
"github.com/harmony-one/harmony/p2p/p2pimpl" |
||||
) |
||||
|
||||
func TestConstructInitMessage(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "19999"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "55555"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls.RandPrivateKey()) |
||||
dRand.blockHash = [32]byte{} |
||||
msg := dRand.constructInitMessage() |
||||
|
||||
msgPayload, _ := proto.GetDRandMessagePayload(msg) |
||||
|
||||
message := &msg_pb.Message{} |
||||
err = protobuf.Unmarshal(msgPayload, message) |
||||
|
||||
if err != nil { |
||||
test.Error("Error in extracting Init message from payload", err) |
||||
} |
||||
} |
||||
|
||||
func TestProcessCommitMessage(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "19999"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "55555"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls.RandPrivateKey()) |
||||
dRand.blockHash = [32]byte{} |
||||
msg := dRand.constructCommitMessage([32]byte{}, []byte{}) |
||||
|
||||
msgPayload, _ := proto.GetDRandMessagePayload(msg) |
||||
|
||||
message := &msg_pb.Message{} |
||||
err = protobuf.Unmarshal(msgPayload, message) |
||||
|
||||
if err != nil { |
||||
test.Error("Error in extracting Commit message from payload", err) |
||||
} |
||||
} |
@ -1,135 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
"math/big" |
||||
"strings" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/harmony-one/bls/ffi/go/bls" |
||||
|
||||
blockfactory "github.com/harmony-one/harmony/block/factory" |
||||
bls2 "github.com/harmony-one/harmony/crypto/bls" |
||||
|
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/core/types" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
"github.com/harmony-one/harmony/p2p" |
||||
"github.com/harmony-one/harmony/p2p/p2pimpl" |
||||
) |
||||
|
||||
func TestNew(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "9902"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "9905"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls2.RandPrivateKey()) |
||||
|
||||
if !dRand.IsLeader { |
||||
test.Error("dRand should belong to a leader") |
||||
} |
||||
} |
||||
|
||||
func TestResetState(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "9902"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "9905"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls2.RandPrivateKey()) |
||||
dRand.ResetState() |
||||
} |
||||
|
||||
func TestSetLeaderPubKey(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "9902"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "9905"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls2.RandPrivateKey()) |
||||
|
||||
_, newPublicKey, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
newPublicKeyBytes, _ := newPublicKey.Bytes() |
||||
if dRand.SetLeaderPubKey(newPublicKeyBytes) == nil { |
||||
test.Error("Failed to Set Public Key of Leader") |
||||
} |
||||
} |
||||
|
||||
func TestUpdatePublicKeys(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "9902"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "9905"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls2.RandPrivateKey()) |
||||
|
||||
pubKey1 := bls2.RandPrivateKey().GetPublicKey() |
||||
pubKey2 := bls2.RandPrivateKey().GetPublicKey() |
||||
|
||||
publicKeys := []*bls.PublicKey{pubKey1, pubKey2} |
||||
|
||||
if dRand.UpdatePublicKeys(publicKeys) != 2 { |
||||
test.Error("Count of public keys doesn't match") |
||||
} |
||||
|
||||
for index, publicKey := range dRand.PublicKeys { |
||||
if strings.Compare(publicKey.SerializeToHexStr(), publicKeys[index].SerializeToHexStr()) != 0 { |
||||
test.Error("Public keys not updated succssfully") |
||||
} |
||||
} |
||||
} |
||||
|
||||
func TestVerifyMessageSig(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "9902"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "9905"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls2.RandPrivateKey()) |
||||
|
||||
message := &msg_pb.Message{ |
||||
ServiceType: msg_pb.ServiceType_DRAND, |
||||
Type: msg_pb.MessageType_DRAND_INIT, |
||||
Request: &msg_pb.Message_Drand{ |
||||
Drand: &msg_pb.DrandRequest{}, |
||||
}, |
||||
} |
||||
drandMsg := message.GetDrand() |
||||
drandMsg.SenderPubkey = dRand.pubKey.Serialize() |
||||
drandMsg.BlockHash = dRand.blockHash[:] |
||||
|
||||
dRand.signDRandMessage(message) |
||||
|
||||
if verifyMessageSig(dRand.pubKey, message) != nil { |
||||
test.Error("Failed to verify the signature") |
||||
} |
||||
} |
||||
|
||||
func TestVrf(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "9902"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "9905"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls2.RandPrivateKey()) |
||||
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) |
||||
txs := []*types.Transaction{tx1} |
||||
|
||||
block := types.NewBlock(blockfactory.NewTestHeader().With().Number(big.NewInt(314)).Header(), txs, types.Receipts{&types.Receipt{}}, nil, nil, nil) |
||||
blockHash := block.Hash() |
||||
|
||||
dRand.vrf(blockHash) |
||||
} |
@ -1,63 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
protobuf "github.com/golang/protobuf/proto" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
"github.com/harmony-one/harmony/p2p/host" |
||||
) |
||||
|
||||
// ProcessMessageValidator dispatches messages for the validator to corresponding processors.
|
||||
func (dRand *DRand) ProcessMessageValidator(payload []byte) { |
||||
message := &msg_pb.Message{} |
||||
err := protobuf.Unmarshal(payload, message) |
||||
|
||||
if err != nil { |
||||
utils.Logger().Error().Interface("dRand", dRand).Err(err).Msg("Failed to unmarshal message payload") |
||||
} |
||||
|
||||
switch message.Type { |
||||
case msg_pb.MessageType_DRAND_INIT: |
||||
dRand.processInitMessage(message) |
||||
case msg_pb.MessageType_DRAND_COMMIT: |
||||
// do nothing on the COMMIT message, as it is intended to send to leader
|
||||
default: |
||||
utils.Logger().Error(). |
||||
Interface("dRand", dRand). |
||||
Uint32("msgType", uint32(message.Type)). |
||||
Msg("Unexpected message type") |
||||
} |
||||
} |
||||
|
||||
// ProcessMessageValidator dispatches validator's consensus message.
|
||||
func (dRand *DRand) processInitMessage(message *msg_pb.Message) { |
||||
if message.Type != msg_pb.MessageType_DRAND_INIT { |
||||
utils.Logger().Error(). |
||||
Uint32("expected", uint32(msg_pb.MessageType_DRAND_INIT)). |
||||
Uint32("got", uint32(message.Type)). |
||||
Msg("Wrong message type received") |
||||
return |
||||
} |
||||
|
||||
drandMsg := message.GetDrand() |
||||
|
||||
// Verify message signature
|
||||
err := verifyMessageSig(dRand.leader.ConsensusPubKey, message) |
||||
if err != nil { |
||||
utils.Logger().Warn().Err(err).Msg("[DRG] Failed to verify the message signature") |
||||
return |
||||
} |
||||
utils.Logger().Debug().Msg("[DRG] verify the message signature Succeeded") |
||||
|
||||
// TODO: check the blockHash is the block hash of last block of last epoch.
|
||||
blockHash := drandMsg.BlockHash |
||||
copy(dRand.blockHash[:], blockHash[:]) |
||||
|
||||
rand, proof := dRand.vrf(dRand.blockHash) |
||||
|
||||
msgToSend := dRand.constructCommitMessage(rand, proof) |
||||
|
||||
// Send the commit message back to leader
|
||||
dRand.host.SendMessageToGroups([]nodeconfig.GroupID{nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(dRand.ShardID))}, host.ConstructP2pMessage(byte(17), msgToSend)) |
||||
} |
@ -1,32 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
"github.com/harmony-one/harmony/api/proto" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
) |
||||
|
||||
// Constructs the init message
|
||||
func (dRand *DRand) constructCommitMessage(vrf [32]byte, proof []byte) []byte { |
||||
message := &msg_pb.Message{ |
||||
ServiceType: msg_pb.ServiceType_DRAND, |
||||
Type: msg_pb.MessageType_DRAND_COMMIT, |
||||
Request: &msg_pb.Message_Drand{ |
||||
Drand: &msg_pb.DrandRequest{}, |
||||
}, |
||||
} |
||||
|
||||
drandMsg := message.GetDrand() |
||||
drandMsg.SenderPubkey = dRand.pubKey.Serialize() |
||||
drandMsg.BlockHash = dRand.blockHash[:] |
||||
drandMsg.ShardId = dRand.ShardID |
||||
drandMsg.Payload = append(vrf[:], proof...) |
||||
// Adding the public key into payload so leader can verify the vrf
|
||||
// TODO: change the curve to follow the same curve with consensus, so the public key doesn't need to be attached.
|
||||
drandMsg.Payload = append(drandMsg.Payload, (*dRand.vrfPubKey).Serialize()...) |
||||
marshaledMessage, err := dRand.signAndMarshalDRandMessage(message) |
||||
if err != nil { |
||||
utils.Logger().Error().Err(err).Msg("Failed to sign and marshal the commit message") |
||||
} |
||||
return proto.ConstructDRandMessage(marshaledMessage) |
||||
} |
@ -1,59 +0,0 @@ |
||||
package drand |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/harmony-one/harmony/crypto/bls" |
||||
|
||||
protobuf "github.com/golang/protobuf/proto" |
||||
"github.com/harmony-one/harmony/api/proto" |
||||
msg_pb "github.com/harmony-one/harmony/api/proto/message" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
"github.com/harmony-one/harmony/p2p" |
||||
"github.com/harmony-one/harmony/p2p/p2pimpl" |
||||
) |
||||
|
||||
func TestConstructCommitMessage(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "19999"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "55555"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls.RandPrivateKey()) |
||||
dRand.blockHash = [32]byte{} |
||||
msg := dRand.constructCommitMessage([32]byte{}, []byte{}) |
||||
msgPayload, _ := proto.GetDRandMessagePayload(msg) |
||||
|
||||
message := &msg_pb.Message{} |
||||
err = protobuf.Unmarshal(msgPayload, message) |
||||
|
||||
if err != nil { |
||||
test.Error("Error in extracting Commit message from payload", err) |
||||
} |
||||
} |
||||
|
||||
func TestProcessInitMessage(test *testing.T) { |
||||
leader := p2p.Peer{IP: "127.0.0.1", Port: "19999"} |
||||
validator := p2p.Peer{IP: "127.0.0.1", Port: "55555"} |
||||
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902") |
||||
host, err := p2pimpl.NewHost(&leader, priKey) |
||||
if err != nil { |
||||
test.Fatalf("newhost failure: %v", err) |
||||
} |
||||
dRand := New(host, 0, []p2p.Peer{leader, validator}, leader, nil, bls.RandPrivateKey()) |
||||
dRand.blockHash = [32]byte{} |
||||
msg := dRand.constructInitMessage() |
||||
|
||||
msgPayload, _ := proto.GetDRandMessagePayload(msg) |
||||
|
||||
message := &msg_pb.Message{} |
||||
err = protobuf.Unmarshal(msgPayload, message) |
||||
|
||||
if err != nil { |
||||
test.Error("Error in extracting Init message from payload", err) |
||||
} |
||||
|
||||
dRand.ProcessMessageValidator(msgPayload) |
||||
} |
@ -1,136 +0,0 @@ |
||||
package hmyclient |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"fmt" |
||||
"math/big" |
||||
|
||||
types2 "github.com/harmony-one/harmony/staking/types" |
||||
|
||||
ethereum "github.com/ethereum/go-ethereum" |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/common/hexutil" |
||||
"github.com/ethereum/go-ethereum/rpc" |
||||
|
||||
"github.com/harmony-one/harmony/block" |
||||
"github.com/harmony-one/harmony/core/types" |
||||
) |
||||
|
||||
// Client defines typed wrappers for the Ethereum RPC API.
|
||||
type Client struct { |
||||
c *rpc.Client |
||||
} |
||||
|
||||
// Dial connects a client to the given URL.
|
||||
func Dial(rawurl string) (*Client, error) { |
||||
return dialContext(context.Background(), rawurl) |
||||
} |
||||
|
||||
func dialContext(ctx context.Context, rawurl string) (*Client, error) { |
||||
c, err := rpc.DialContext(ctx, rawurl) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return NewClient(c), nil |
||||
} |
||||
|
||||
// NewClient creates a client that uses the given RPC client.
|
||||
func NewClient(c *rpc.Client) *Client { |
||||
return &Client{c} |
||||
} |
||||
|
||||
// Close closes the client
|
||||
func (c *Client) Close() { |
||||
c.c.Close() |
||||
} |
||||
|
||||
// BlockNumber returns the block height.
|
||||
func (c *Client) BlockNumber(ctx context.Context) (hexutil.Uint64, error) { |
||||
var raw json.RawMessage |
||||
err := c.c.CallContext(ctx, &raw, "hmy_blockNumber") |
||||
if err != nil { |
||||
return 0, err |
||||
} else if len(raw) == 0 { |
||||
return 0, ethereum.NotFound |
||||
} |
||||
var blockNumber hexutil.Uint64 |
||||
if err := json.Unmarshal(raw, &blockNumber); err != nil { |
||||
return 0, err |
||||
} |
||||
return blockNumber, nil |
||||
} |
||||
|
||||
// BlockByHash returns the given full block.
|
||||
//
|
||||
// Note that loading full blocks requires two requests. Use HeaderByHash
|
||||
// if you don't need all transactions or uncle headers.
|
||||
func (c *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { |
||||
return c.getBlock(ctx, "hmy_getBlockByHash", hash, true) |
||||
} |
||||
|
||||
// BlockByNumber returns a block from the current canonical chain. If number is nil, the
|
||||
// latest known block is returned.
|
||||
//
|
||||
// Note that loading full blocks requires two requests. Use HeaderByNumber
|
||||
// if you don't need all transactions or uncle headers.
|
||||
func (c *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { |
||||
return c.getBlock(ctx, "hmy_getBlockByNumber", toBlockNumArg(number), true) |
||||
} |
||||
|
||||
// NetworkID returns the network ID (also known as the chain ID) for this chain.
|
||||
func (c *Client) NetworkID(ctx context.Context) (*big.Int, error) { |
||||
version := new(big.Int) |
||||
var ver string |
||||
if err := c.c.CallContext(ctx, &ver, "net_version"); err != nil { |
||||
return nil, err |
||||
} |
||||
if _, ok := version.SetString(ver, 10); !ok { |
||||
return nil, fmt.Errorf("invalid net_version result %q", ver) |
||||
} |
||||
return version, nil |
||||
} |
||||
|
||||
func (c *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { |
||||
var raw json.RawMessage |
||||
err := c.c.CallContext(ctx, &raw, method, args...) |
||||
if err != nil { |
||||
return nil, err |
||||
} else if len(raw) == 0 { |
||||
return nil, ethereum.NotFound |
||||
} |
||||
// Decode header and transactions.
|
||||
var head *block.Header |
||||
var body rpcBlock |
||||
if err := json.Unmarshal(raw, &head); err != nil { |
||||
return nil, err |
||||
} |
||||
if err := json.Unmarshal(raw, &body); err != nil { |
||||
return nil, err |
||||
} |
||||
// Quick-verify transaction. This mostly helps with debugging the server.
|
||||
if head.TxHash() == types.EmptyRootHash && len(body.Transactions) > 0 { |
||||
return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions") |
||||
} |
||||
if head.TxHash() != types.EmptyRootHash && len(body.Transactions) == 0 { |
||||
return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions") |
||||
} |
||||
// Fill the sender cache of transactions in the block.
|
||||
txs := make([]*types.Transaction, len(body.Transactions)) |
||||
for i, tx := range body.Transactions { |
||||
if tx.From != nil { |
||||
setSenderFromServer(tx.tx, *tx.From, body.Hash) |
||||
} |
||||
txs[i] = tx.tx |
||||
} |
||||
stakingTxs := make([]*types2.StakingTransaction, len(body.StakingTransactions)) |
||||
// TODO: add staking txns
|
||||
return types.NewBlockWithHeader(head).WithBody(txs, stakingTxs, []*block.Header{}, nil), nil |
||||
} |
||||
|
||||
func toBlockNumArg(number *big.Int) string { |
||||
if number == nil { |
||||
return "latest" |
||||
} |
||||
return hexutil.EncodeBig(number) |
||||
} |
@ -1,42 +0,0 @@ |
||||
package hmyclient |
||||
|
||||
import ( |
||||
"errors" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/harmony-one/harmony/core/types" |
||||
) |
||||
|
||||
var errNotCached = errors.New("sender not cached") |
||||
|
||||
// senderFromServer is a types.Signer that remembers the sender address returned by the RPC
|
||||
// server. It is stored in the transaction's sender address cache to avoid an additional
|
||||
// request in TransactionSender.
|
||||
type senderFromServer struct { |
||||
addr common.Address |
||||
blockhash common.Hash |
||||
} |
||||
|
||||
func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) { |
||||
// Use types.Sender for side-effect to store our signer into the cache.
|
||||
types.Sender(&senderFromServer{addr, block}, tx) |
||||
} |
||||
func (s *senderFromServer) Equal(other types.Signer) bool { |
||||
os, ok := other.(*senderFromServer) |
||||
return ok && os.blockhash == s.blockhash |
||||
} |
||||
|
||||
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { |
||||
if s.blockhash == (common.Hash{}) { |
||||
return common.Address{}, errNotCached |
||||
} |
||||
return s.addr, nil |
||||
} |
||||
|
||||
func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash { |
||||
panic("can't sign with senderFromServer") |
||||
} |
||||
func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) { |
||||
panic("can't sign with senderFromServer") |
||||
} |
@ -1,30 +0,0 @@ |
||||
package hmyclient |
||||
|
||||
import ( |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/harmony-one/harmony/core/types" |
||||
types2 "github.com/harmony-one/harmony/staking/types" |
||||
) |
||||
|
||||
type rpcBlock struct { |
||||
Hash common.Hash `json:"hash"` |
||||
Transactions []rpcTransaction `json:"transactions"` |
||||
StakingTransactions []rpcStakingTransaction `json:"staking_transactions"` |
||||
UncleHashes []common.Hash `json:"uncles"` |
||||
} |
||||
|
||||
type rpcTransaction struct { |
||||
tx *types.Transaction |
||||
txExtraInfo |
||||
} |
||||
|
||||
type txExtraInfo struct { |
||||
BlockNumber *string `json:"blockNumber,omitempty"` |
||||
BlockHash *common.Hash `json:"blockHash,omitempty"` |
||||
From *common.Address `json:"from,omitempty"` |
||||
} |
||||
|
||||
type rpcStakingTransaction struct { |
||||
tx *types2.StakingTransaction |
||||
txExtraInfo |
||||
} |
@ -1,277 +0,0 @@ |
||||
# NAT Hole Punching Spike Report |
||||
|
||||
(That is, can we run Harmony on a home network?) |
||||
|
||||
## Purpose |
||||
|
||||
Find out if we can easily punch a TCP port forwarding hole through a home gateway, so that we can run Harmony node at home. |
||||
|
||||
## Goal |
||||
|
||||
We should identify a way to easily tell the home gateway to punch a TCP hole so that it is forwarded to a local IP address and port number of our choice, that is, we configure our Harmony client to run on that address/port. |
||||
|
||||
## Background |
||||
|
||||
VoIP and games have needed this for a long time, and there are a few protocols that can do this: |
||||
|
||||
* [NAT Port Mapping Protocol (NAT-PMP)](https://tools.ietf.org/html/rfc6886) |
||||
* Apple’s version |
||||
* Used by Back To My Mac |
||||
* IETF Informational (recognized but not standardized) |
||||
* [Port Control Protocol (PCP)](https://tools.ietf.org/html/rfc6887) |
||||
* Successor of NAT-PMP |
||||
* Works with large-scale NAT |
||||
* IETF Proposed Standard |
||||
* Internet Gateway Device (IGD) Control Protocol |
||||
* Microsoft’s version, originally part of Universal Plug and Play (UPnP) |
||||
* UPnP is now ISO/IEC 29341 |
||||
* Older, dates back to first Xbox (gaming was the first use case) |
||||
|
||||
Most home gateways support one or both of these. IGD/UPnP is more common because it’s older and the use case is more compelling – PC/console gaming. We first focus on IGD therefore. Our office is behind a Google Wi-Fi router, which supports both IGD and PCP. |
||||
|
||||
## Plan & “tl;dr” Result |
||||
|
||||
(See Transcript for details.) |
||||
|
||||
1. Find a CLI-based IGD/UPnP implementation we can use in our scripts.<br/>**⇒ MiniUPnP seemed promising** |
||||
2. Examine docs to see if it can setup and teardown the port mapping.<br/>**⇒ list (upnpc -l), setup (upnp -r), teardown (upnp -d)** |
||||
3. Set up a port forwarding to SSH daemon my Mac<br/>**⇒ upnpc -r 22 TCP** |
||||
4. Figure out the port mapping established.<br/>**⇒ found in the upnpc -r command output, also shown in upnpc -l output** |
||||
5. SSH to a server out of the home network. |
||||
6. SSH back to my Mac, using the public IP/port obtained from step 4.<br/>**⇒ WORKS!** |
||||
7. Tear down the port mapping.<br/>**⇒ upnpc -d 22 TCP** |
||||
|
||||
## Next Step |
||||
|
||||
1. Use this for the startup script to use in Docker. |
||||
2. See if we can also find a suitable CLI tool for PCP or NAT-PMP. |
||||
a. I believe Apple AirPort access point implements only NAT-PMP, and not UPnP. |
||||
3. Figure out how to deal with timeout issue. |
||||
a. Can we refresh existing mapping so the public port number does not change? |
||||
4. Figure out how to deal with changes in public IP address. |
||||
a. When this happens, public port may also change, or mapping may be gone. |
||||
5. Figure out how to deal with router reboots. |
||||
a. Depends on implementation, but chances are, the mapping may be gone. |
||||
|
||||
Also, **everyone:** |
||||
|
||||
6. Try this at home. |
||||
a. Let me know how it goes. |
||||
|
||||
## Appendix A. Transcript |
||||
|
||||
### Docs Examination |
||||
|
||||
Command-line help; bold ones seem to fit our purposes. |
||||
``` |
||||
quelthalas 09:07:25 ~ $ 11 upnpc -h |
||||
upnpc : miniupnpc library test client, version 2.1. |
||||
(c) 2005-2018 Thomas Bernard. |
||||
Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ |
||||
for more information. |
||||
Usage : upnpc [options] -a ip port external_port protocol [duration] |
||||
Add port redirection |
||||
upnpc [options] -d external_port protocol <remote host> |
||||
Delete port redirection |
||||
upnpc [options] -s |
||||
Get Connection status |
||||
upnpc [options] -l |
||||
List redirections |
||||
upnpc [options] -L |
||||
List redirections (using GetListOfPortMappings (for IGD:2 only) |
||||
upnpc [options] -n ip port external_port protocol [duration] |
||||
Add (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only) |
||||
upnpc [options] -N external_port_start external_port_end protocol [manage] |
||||
Delete range of port redirections (for IGD:2 only) |
||||
upnpc [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...] |
||||
Add all redirections to the current host |
||||
upnpc [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time |
||||
Add Pinhole (for IGD:2 only) |
||||
upnpc [options] -U uniqueID new_lease_time |
||||
Update Pinhole (for IGD:2 only) |
||||
upnpc [options] -C uniqueID |
||||
Check if Pinhole is Working (for IGD:2 only) |
||||
upnpc [options] -K uniqueID |
||||
Get Number of packets going through the rule (for IGD:2 only) |
||||
upnpc [options] -D uniqueID |
||||
Delete Pinhole (for IGD:2 only) |
||||
upnpc [options] -S |
||||
Get Firewall status (for IGD:2 only) |
||||
upnpc [options] -G remote_ip remote_port internal_ip internal_port protocol |
||||
Get Outbound Pinhole Timeout (for IGD:2 only) |
||||
upnpc [options] -P |
||||
Get Presentation url |
||||
|
||||
protocol is UDP or TCP |
||||
Options: |
||||
-e description : set description for port mapping. |
||||
-6 : use ip v6 instead of ip v4. |
||||
-u url : bypass discovery process by providing the XML root description url. |
||||
-m address/interface : provide ip address (ip v4) or interface name (ip v4 or v6) to use for sending SSDP multicast packets. |
||||
-z localport : SSDP packets local (source) port (1024-65535). |
||||
-p path : use this path for MiniSSDPd socket. |
||||
-t ttl : set multicast TTL. Default value is 2. |
||||
``` |
||||
|
||||
## Forwarding Setup |
||||
|
||||
### Precondition |
||||
|
||||
``` |
||||
quelthalas 09:07:27 ~ $ 12 upnpc -l |
||||
upnpc : miniupnpc library test client, version 2.1. |
||||
(c) 2005-2018 Thomas Bernard. |
||||
Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ |
||||
for more information. |
||||
List of UPNP devices found on the network : |
||||
desc: http://192.168.86.1:5000/rootDesc.xml |
||||
st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 |
||||
|
||||
Found valid IGD : http://192.168.86.1:5000/ctl/IPConn |
||||
Local LAN ip address : 192.168.86.250 |
||||
Connection Type : IP_Routed |
||||
Status : Connected, uptime=4460733s, LastConnectionError : ERROR_NONE |
||||
Time started : Wed Jan 16 18:02:03 2019 |
||||
MaxBitRateDown : 1000000000 bps (1000.0 Mbps) MaxBitRateUp 1000000000 bps (1000.0 Mbps) |
||||
ExternalIPAddress = 73.71.156.214 |
||||
i protocol exPort->inAddr:inPort description remoteHost leaseTime |
||||
0 UDP 58956->192.168.86.230:58956 'WhatsApp (1552097793) ()' '' 551337 |
||||
1 UDP 57027->192.168.86.230:57027 'WhatsApp (1552098628) ()' '' 552173 |
||||
2 UDP 58671->192.168.86.230:58671 'WhatsApp (1552149239) ()' '' 602783 |
||||
3 UDP 55355->192.168.86.230:55355 'WhatsApp (1552150382) ()' '' 603926 |
||||
GetGenericPortMappingEntry() returned 713 (SpecifiedArrayIndexInvalid) |
||||
``` |
||||
|
||||
### Trigger |
||||
|
||||
``` |
||||
quelthalas 09:07:36 ~ $ 13 upnpc -r 22 TCP |
||||
upnpc : miniupnpc library test client, version 2.1. |
||||
(c) 2005-2018 Thomas Bernard. |
||||
Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ |
||||
for more information. |
||||
List of UPNP devices found on the network : |
||||
desc: http://192.168.86.1:5000/rootDesc.xml |
||||
st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 |
||||
|
||||
Found valid IGD : http://192.168.86.1:5000/ctl/IPConn |
||||
Local LAN ip address : 192.168.86.250 |
||||
ExternalIPAddress = 73.71.156.214 |
||||
InternalIP:Port = 192.168.86.250:22 |
||||
external 73.71.156.214:22 TCP is redirected to internal 192.168.86.250:22 (duration=604800) |
||||
``` |
||||
|
||||
It assigned the external port number (exPort) 22. The external IP address is 73.71.156.214. It also has a one-week (604800 seconds) lifetime. |
||||
|
||||
### Postcondition |
||||
|
||||
``` |
||||
quelthalas 09:07:41 ~ $ 14 upnpc -l |
||||
upnpc : miniupnpc library test client, version 2.1. |
||||
(c) 2005-2018 Thomas Bernard. |
||||
Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ |
||||
for more information. |
||||
List of UPNP devices found on the network : |
||||
desc: http://192.168.86.1:5000/rootDesc.xml |
||||
st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 |
||||
|
||||
Found valid IGD : http://192.168.86.1:5000/ctl/IPConn |
||||
Local LAN ip address : 192.168.86.250 |
||||
Connection Type : IP_Routed |
||||
Status : Connected, uptime=4460745s, LastConnectionError : ERROR_NONE |
||||
Time started : Wed Jan 16 18:02:03 2019 |
||||
MaxBitRateDown : 1000000000 bps (1000.0 Mbps) MaxBitRateUp 1000000000 bps (1000.0 Mbps) |
||||
ExternalIPAddress = 73.71.156.214 |
||||
i protocol exPort->inAddr:inPort description remoteHost leaseTime |
||||
0 UDP 58956->192.168.86.230:58956 'WhatsApp (1552097793) ()' '' 551325 |
||||
1 UDP 57027->192.168.86.230:57027 'WhatsApp (1552098628) ()' '' 552161 |
||||
2 UDP 58671->192.168.86.230:58671 'WhatsApp (1552149239) ()' '' 602771 |
||||
3 UDP 55355->192.168.86.230:55355 'WhatsApp (1552150382) ()' '' 603914 |
||||
4 TCP 22->192.168.86.250:22 'libminiupnpc' '' 604793 |
||||
GetGenericPortMappingEntry() returned 713 (SpecifiedArrayIndexInvalid) |
||||
``` |
||||
|
||||
Index 4 is the new entry we just set up. |
||||
|
||||
### SSH Roundtrip Test |
||||
|
||||
#### SSH to an out-of-home server |
||||
|
||||
We use our Jenkins server on AWS. |
||||
|
||||
``` |
||||
quelthalas 09:07:48 ~ $ 15 ssh jenkins |
||||
Last login: Sat Mar 9 16:45:50 2019 from c-73-71-156-214.hsd1.ca.comcast.net |
||||
|
||||
__| __|_ ) |
||||
_| ( / Amazon Linux 2 AMI |
||||
___|\___|___| |
||||
|
||||
https://aws.amazon.com/amazon-linux-2/ |
||||
8 package(s) needed for security, out of 11 available |
||||
Run "sudo yum update" to apply all updates. |
||||
[ec2-user@ip-172-31-14-35 ~]$ |
||||
``` |
||||
|
||||
#### SSH back to my Mac |
||||
|
||||
Use the external address and port number we gathered. |
||||
|
||||
``` |
||||
[ec2-user@ip-172-31-14-35 ~]$ ssh -p 22 ek@73.71.156.214 |
||||
Password: |
||||
Last login: Sat Mar 9 08:46:14 2019 from 54.183.5.66 |
||||
Agent running at /var/folders/bj/fgmkk8fj715_jcbdzrxjlrh80000gn/T//ssh-73WRpgJuix8J/agent.9152 (PID 9153) |
||||
quelthalas 09:08:13 ~ $ 1 logout |
||||
Connection to 73.71.156.214 closed. |
||||
[ec2-user@ip-172-31-14-35 ~]$ logout |
||||
Shared connection to jenkins.harmony.one closed. |
||||
quelthalas 09:08:30 ~ $ 16 |
||||
``` |
||||
|
||||
### Forwarding Teardown |
||||
|
||||
#### Trigger |
||||
|
||||
``` |
||||
quelthalas 09:29:12 ~ $ 18 upnpc -d 22 TCP |
||||
upnpc : miniupnpc library test client, version 2.1. |
||||
(c) 2005-2018 Thomas Bernard. |
||||
Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ |
||||
for more information. |
||||
List of UPNP devices found on the network : |
||||
desc: http://192.168.86.1:5000/rootDesc.xml |
||||
st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 |
||||
|
||||
Found valid IGD : http://192.168.86.1:5000/ctl/IPConn |
||||
Local LAN ip address : 192.168.86.250 |
||||
UPNP_DeletePortMapping() returned : 0 |
||||
``` |
||||
|
||||
#### Postcondition |
||||
|
||||
``` |
||||
quelthalas 09:29:19 ~ $ 19 upnpc -l |
||||
upnpc : miniupnpc library test client, version 2.1. |
||||
(c) 2005-2018 Thomas Bernard. |
||||
Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ |
||||
for more information. |
||||
List of UPNP devices found on the network : |
||||
desc: http://192.168.86.1:5000/rootDesc.xml |
||||
st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 |
||||
|
||||
Found valid IGD : http://192.168.86.1:5000/ctl/IPConn |
||||
Local LAN ip address : 192.168.86.250 |
||||
Connection Type : IP_Routed |
||||
Status : Connected, uptime=4462039s, LastConnectionError : ERROR_NONE |
||||
Time started : Wed Jan 16 18:02:03 2019 |
||||
MaxBitRateDown : 1000000000 bps (1000.0 Mbps) MaxBitRateUp 1000000000 bps (1000.0 Mbps) |
||||
ExternalIPAddress = 73.71.156.214 |
||||
i protocol exPort->inAddr:inPort description remoteHost leaseTime |
||||
0 UDP 58956->192.168.86.230:58956 'WhatsApp (1552097793) ()' '' 550031 |
||||
1 UDP 57027->192.168.86.230:57027 'WhatsApp (1552098628) ()' '' 550867 |
||||
2 UDP 58671->192.168.86.230:58671 'WhatsApp (1552149239) ()' '' 601477 |
||||
GetGenericPortMappingEntry() returned 713 (SpecifiedArrayIndexInvalid) |
||||
``` |
||||
|
||||
Note that the TCP port 22 entry is gone. Teardown worked. |
@ -1,70 +0,0 @@ |
||||
# Peer Discovery |
||||
|
||||
## Introduction |
||||
For any decentralized blockchain network, peer discovery is the essential step to create a real p2p mesh network. |
||||
New node uses peer discovery to join the blockchain network. |
||||
In Harmony network, the new node will also need to find the right shard to join. |
||||
The proposed peer discovery process works as illustrated below. |
||||
|
||||
## P2P Overlay Networks |
||||
|
||||
Each Harmony node joins two P2P overlay networks: |
||||
|
||||
* The **global** overlay, used for *inter*-shard communication; and |
||||
* The **shard** overlay, used for *intra*-shard communication. |
||||
|
||||
For each overlay network, the node: |
||||
|
||||
* Initiates up to 16 outbound peer connections; and |
||||
* Accepts up to 128 inbound peer connections. |
||||
|
||||
## Bootnodes |
||||
New nodes contact any bootnode to get a list of 32 random nodes at the global level. |
||||
A list of the IP addresses of the bootnodes are hard-coded in the node software. |
||||
Alternatively, the new node may use a Harmony owned DNSseed (ex. bootnode.harmony.one) to find a list of long running bootnodes if the hard-coded bootnodes are not accepting connections due to network congestion. |
||||
|
||||
## Register |
||||
To prevent Sybil and DoS attacks, new nodes need to solve a PoW puzzle in order to join the p2p network. |
||||
We support different type of nodes joining the network. |
||||
For a validator node, the difficulty of the puzzle can be adjusted to within 1 minute on a typical server machine (4 cores, 2.7 GHz). |
||||
For a light client such like a mobile device or IoT device that won't join the consensus, the puzzle should be adjusted to within 5 seconds on a typical mobile processor, ex, 2 ARM cores of 1GHz. |
||||
|
||||
Bootnodes use the Kademlia based DHT to return a list of 32 peer nodes to the new node. |
||||
Each bootnode may keep up to 3,000 IP addresses of the peer nodes in the network. |
||||
The Kademlia based DHT calculates the XOR distances of the public keys of the nodes to keep nodes into different buckets. |
||||
|
||||
After joining the network, for a full node that will participate in the consensus, it needs to broadcast the network via the peers returned by bootnodes for Proof-of-Stake verification. |
||||
The first message is to submit a transaction of deposit of a certain amount of Harmony tokens as the stake to join the network as validators. |
||||
Beacon chain will be responsible for validating the stakes and the transaction. |
||||
Details of the PoS staking and verification process should refer to our Beacon Chain design spec. |
||||
|
||||
For a light node that will not join in the consensus, it has no requirement of the PoS verification. |
||||
|
||||
Beacon chain will use randomness to generate a permutation of all nodes that are waiting to join consensus. |
||||
The randomness and node permutation is used determine which shard the new node should be joining. |
||||
Details of the sharding algorithm please refer to the White Paper. |
||||
|
||||
Every new node’s public key and deposit will be recorded in the beacon chain blocks. |
||||
|
||||
## Shard Discovery |
||||
New node compute the shard info based on randomness generated by beacon chain. |
||||
It then broadcasts the shard info together with its public key to the network using the shard id as a topic. |
||||
The peers within the same shard as the new node will respond to the topic based on the XOR distance in the DHT. |
||||
We are investigating the rendezvous feature of libp2p to implement the shard discovery feature. |
||||
|
||||
## LibP2P |
||||
The new node are connected to the two P2P overlay networks. |
||||
Harmony utilizes libp2p as the underlying networking layer for peer discovery and p2p network transportation. |
||||
It is still a crucial task to understand the protocol messages and how the libp2p handles the messages. |
||||
We may need to fork the libp2p to fit our requirement during the development. |
||||
|
||||
## Two Stages of Peer Discovery |
||||
Harmony uses two stages of peer discovery mechanism to form the overlay of p2p networks. |
||||
The first stage is to connect to beacon chain to stake and get the shard information. |
||||
The second stage is to create the real p2p network within the shard afterwards. |
||||
New nodes will always keep the connection to some beacon chain nodes for further communication. |
||||
The current implementation works like the following. |
||||
* beacon chain nodes bootstrap by contacting bootnodes using discovery rendezvous string "0". Then the beacon chain is formed. |
||||
* new nodes contact bootnodes using rendezvous string "0" to connect to beacon chain nodes. |
||||
* new nodes use pubsub to stake in beacon chain, and get shard information from beacon chain after the randomness and resharding algorithm. |
||||
* new nodes use the new shardID as the rendezvous string to connect to bootnodes again to form new p2p network at the shard level. |
@ -1,268 +0,0 @@ |
||||
# Harmony blockchain test plan |
||||
|
||||
We list the major testing area and the test cases. |
||||
For each test case, it should have a description of the test case, expected behavior and passing criteria. |
||||
|
||||
## test case template |
||||
|
||||
### template |
||||
* test case # : |
||||
* description : |
||||
* test procedure : |
||||
* passing criteria |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
## functional test |
||||
Functional test is used to validate the function of each component in the blockchain. |
||||
It should cover the basic function to pass, to fail, and error conditions. |
||||
|
||||
### peer discovery |
||||
|
||||
* test case # : PD1 |
||||
* description : find peers using rendezvous string |
||||
* test procedure : a new node connected to bootnode and call FindPeers function to return a list of peers using different rendezvous strings |
||||
* passing criteria : new node can get a list of peers using the same rendezvous string |
||||
* dependency |
||||
* note |
||||
* automated? N |
||||
--- |
||||
* test case # : PD2 |
||||
* description : boot node recovery |
||||
* test procedure : start a bootnode, start a few new node to connect to bootnode, restart the bootnode, start a few new node to discover peers |
||||
* passing criteria : bootnode should be able to store previously connected peers |
||||
* dependency |
||||
* note |
||||
* automated? N |
||||
--- |
||||
* test case # : PD3 |
||||
* description : multiple boot nodes |
||||
* test procedure : start 3-5 bootnode, start a few new node to connect to all the bootnodes for peer discovery |
||||
* passing criteria : new node can get a list of peers using the same rendezvous string |
||||
* dependency : redundant peers maybe returned and we should handle the reduanancy (or not) |
||||
* note |
||||
* automated? N |
||||
--- |
||||
* test case # : PD4 |
||||
* description : number of peers supported in bootnode |
||||
* test procedure : start a bootnode, connect up to 5000 new node using the same rendezvous string |
||||
* passing criteria : each new node should get a list of peers, not sure how many though |
||||
* dependency |
||||
* note |
||||
* automated? N |
||||
--- |
||||
|
||||
### state syncing |
||||
|
||||
* test case # : SS1 |
||||
* description : node state sync basic |
||||
* test procedure : node joins network and is able to sync to latest block |
||||
* passing criteria : blockheight of node is equal to that of the shards blockchain and node has joined consensus. |
||||
* dependency |
||||
* note |
||||
* automated? N |
||||
--- |
||||
* test case # : SS2 |
||||
* description : network connectivity issues |
||||
* test procedure : node experiences network connectivity issues or is down and then regains connectivity |
||||
* passing criteria : the node is able to sync back to current state of blockchain from the point where it dropped off |
||||
* dependency |
||||
* note |
||||
* automated? N |
||||
--- |
||||
* test case # : SS1 |
||||
* description : beacon chain node ss |
||||
* test procedure : one new beacon node join in the beacon chain after beacon chain reach a few consensuses |
||||
* passing criteria : the new node can join in the consensus after state syncing |
||||
* dependency |
||||
* note |
||||
* automated? N |
||||
--- |
||||
|
||||
### consensus |
||||
|
||||
* test case # : CS1 |
||||
* description : beacon chain reach consensus |
||||
* test procedure : start beacon chain with 50, 100, 150, 200, 250, 300 nodes, check leader log on number of consensuses |
||||
* passing criteria |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### drand |
||||
|
||||
* test case # : DR1 |
||||
* description : drand generate random number |
||||
* test procedure : start beacon chain with 50, 150, 300 nodes, check leader log on the success of generating random number |
||||
* passing criteria : random number genreated |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : DR2 |
||||
* description : |
||||
* test procedure : |
||||
* passing criteria |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### smartcontract |
||||
|
||||
* test case # : SC1 |
||||
* description : deploy ERC20 smart contract in 1 shard |
||||
* test procedure : smart contract is deployed in a automated fashion e.g. erc20 (utility) token contract |
||||
* passing criteria: ability to interact with smart contract e.g. transfer erc20 tokens from one address to another |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : SC2 |
||||
* description : deploy ERC721 (non-fungible tokens) smart contract in 1 shard |
||||
* test procedure : smart contract is deployed in a automated fashion e.g. smart contract like Cryptokitties |
||||
* passing criteria: ability to succesfully transfer the non-fungible token from one address to another via transaction |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### staking |
||||
|
||||
* test case # : SK1 |
||||
* description : |
||||
* test procedure : |
||||
* passing criteria |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : SK2 |
||||
* description : |
||||
* test procedure : |
||||
* passing criteria |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### resharding |
||||
|
||||
* test case # : RS1 |
||||
* description : reshard with 1 shard only |
||||
* test procedure : start beacon chain with 50 nodes, wait for resharding after 5 blocks |
||||
* passing criteria : a new leader should be selected |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : RS2 |
||||
* description : reshard with 2 shards |
||||
* test procedure : 0 new nodes join the network for the new epoch |
||||
* passing criteria : a new leader should be selected |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : RS3 |
||||
* description : reshard with 2 shards with 250 nodes each |
||||
* test procedure : 50 to 250 new nodes join the network for the new epoch |
||||
* passing criteria : a new leader should be selected |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### transaction |
||||
|
||||
* test case # : TX1 |
||||
* description : wallet send transaction to blockchain |
||||
* test procedure : start beacon chain with 50 nodes, send one transaction from wallet |
||||
* passing criteria : transaction is recorded in blockchain |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### view change |
||||
|
||||
* test case # : VC1 |
||||
* description : change leader after resharding |
||||
* test procedure : after resharding, new leader was selected and changed |
||||
* passing criteria : consensus keeps going |
||||
* dependency : resharding |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : VC2 |
||||
* description : change malicious leader |
||||
* test procedure : started beacon chain with 50 nodes, leader started consensus and offline, after sometime, a new leader is selected |
||||
* passing criteria : new leader keeps the consensus going |
||||
* dependency : resharding |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
## stress test |
||||
|
||||
### protocol level stress |
||||
|
||||
* test case # : STP1 |
||||
* description : |
||||
* test procedure : increase number of txns in block from 1000 to 10,000, stepwise |
||||
* evaluation: change in block latency per shard/change in transactions per sec, per shard. |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : STP2 |
||||
* description : increasing number of nodes in a shard |
||||
* test procedure : increase number of nodes in a shard from 100 to 1000 |
||||
* evaluation: change in latency per shard/change in transactions per sec, per shard. |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
* test case # : STP3 |
||||
* description : epoch change with increasing number of nodes in shard |
||||
* test procedure : initiate epoch change with increasing number of nodes in a shard from 100 to 1000 |
||||
* evaluation: latency in leader election |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
## epoch change stress |
||||
|
||||
* test case # : EC1 |
||||
* description : hight waiting nodes |
||||
* test procedure : for 5 shards having 200 nodes each initiate epoch change with 10,000 nodes waiting |
||||
* evaluation: latency in leader election/resharding |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### networking level stress |
||||
|
||||
* test case # : NT1 |
||||
* description : |
||||
* test procedure : start consensus with a peer-p2p network of 50 peers, increase it to 1000 peers, send 1 ping message every 100 ms. |
||||
* evaluation: change in latency per shard/change in transactions per sec, per shard. |
||||
* dependency |
||||
* note |
||||
* automated? |
||||
--- |
||||
|
||||
### long running stress |
||||
### storage |
||||
|
||||
## security test |
||||
### malicious node |
||||
### DoS attack |
||||
|
||||
## stability test |
||||
### |
Loading…
Reference in new issue