2017-09-06 00:59:12 -07:00
|
|
|
// Copyright 2017 AMIS Technologies
|
|
|
|
// This file is part of the go-ethereum library.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package k8s
|
|
|
|
|
|
|
|
import (
|
2017-09-11 20:48:24 -07:00
|
|
|
"context"
|
2017-09-12 20:05:13 -07:00
|
|
|
"crypto/ecdsa"
|
2017-09-11 20:48:24 -07:00
|
|
|
"errors"
|
|
|
|
"math/big"
|
2017-09-21 02:52:47 -07:00
|
|
|
"strings"
|
2017-09-11 20:48:24 -07:00
|
|
|
"sync"
|
2017-09-06 00:59:12 -07:00
|
|
|
"time"
|
|
|
|
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2017-09-11 23:46:27 -07:00
|
|
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
2017-09-06 00:59:12 -07:00
|
|
|
|
2017-09-12 20:05:13 -07:00
|
|
|
"github.com/getamis/go-ethereum/crypto"
|
2017-09-06 00:59:12 -07:00
|
|
|
"github.com/getamis/istanbul-tools/charts"
|
|
|
|
"github.com/getamis/istanbul-tools/client"
|
|
|
|
istcommon "github.com/getamis/istanbul-tools/common"
|
2017-09-11 20:48:24 -07:00
|
|
|
"github.com/getamis/istanbul-tools/container"
|
2017-09-06 00:59:12 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
func NewEthereum(options ...Option) *ethereum {
|
|
|
|
eth := ðereum{
|
|
|
|
name: istcommon.RandomHex(),
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, opt := range options {
|
|
|
|
opt(eth)
|
|
|
|
}
|
|
|
|
|
2017-09-12 20:05:13 -07:00
|
|
|
var err error
|
|
|
|
eth.key, err = crypto.HexToECDSA(eth.nodekey)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to create private key from nodekey", "nodekey", eth.nodekey)
|
|
|
|
return nil
|
|
|
|
}
|
2017-09-06 00:59:12 -07:00
|
|
|
eth.chart = charts.NewValidatorChart(eth.name, eth.args)
|
|
|
|
|
|
|
|
return eth
|
|
|
|
}
|
|
|
|
|
|
|
|
type ethereum struct {
|
|
|
|
chart *charts.ValidatorChart
|
|
|
|
name string
|
|
|
|
args []string
|
|
|
|
|
2017-09-12 20:05:13 -07:00
|
|
|
nodekey string
|
|
|
|
key *ecdsa.PrivateKey
|
2017-09-28 01:31:22 -07:00
|
|
|
accounts []*ecdsa.PrivateKey
|
2017-09-06 00:59:12 -07:00
|
|
|
k8sClient *kubernetes.Clientset
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) Init(genesisFile string) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) Start() error {
|
|
|
|
if err := eth.chart.Install(false); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
eth.k8sClient = k8sClient(eth.chart.Name() + "-0")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) Stop() error {
|
|
|
|
return eth.chart.Uninstall()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) Wait(t time.Duration) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) Running() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) ContainerID() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) DockerEnv() []string {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) DockerBinds() []string {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-24 19:19:21 -07:00
|
|
|
func (eth *ethereum) NewClient() client.Client {
|
2017-09-21 02:52:47 -07:00
|
|
|
for i := 0; i < healthCheckRetryCount; i++ {
|
|
|
|
client, err := client.Dial("ws://" + eth.Host() + ":8546")
|
|
|
|
if err != nil {
|
|
|
|
log.Warn("Failed to create client", "err", err)
|
|
|
|
<-time.After(healthCheckRetryDelay)
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
return client
|
|
|
|
}
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|
2017-09-21 02:52:47 -07:00
|
|
|
|
|
|
|
return nil
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) NodeAddress() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) Address() common.Address {
|
2017-09-12 20:05:13 -07:00
|
|
|
return crypto.PubkeyToAddress(eth.key.PublicKey)
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) ConsensusMonitor(errCh chan<- error, quit chan struct{}) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) WaitForProposed(expectedAddress common.Address, timeout time.Duration) error {
|
2017-09-11 20:48:24 -07:00
|
|
|
cli := eth.NewClient()
|
|
|
|
|
|
|
|
subCh := make(chan *ethtypes.Header)
|
|
|
|
|
|
|
|
sub, err := cli.SubscribeNewHead(context.Background(), subCh)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer sub.Unsubscribe()
|
|
|
|
|
|
|
|
timer := time.NewTimer(timeout)
|
2017-09-11 23:12:26 -07:00
|
|
|
defer timer.Stop()
|
2017-09-11 20:48:24 -07:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case err := <-sub.Err():
|
|
|
|
return err
|
|
|
|
case <-timer.C: // FIXME: this event may be missed
|
|
|
|
return errors.New("no result")
|
|
|
|
case head := <-subCh:
|
2017-09-11 23:46:27 -07:00
|
|
|
if container.GetProposer(head) == expectedAddress {
|
2017-09-11 20:48:24 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) WaitForPeersConnected(expectedPeercount int) error {
|
2017-09-11 20:48:24 -07:00
|
|
|
client := eth.NewClient()
|
|
|
|
if client == nil {
|
|
|
|
return errors.New("failed to retrieve client")
|
|
|
|
}
|
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
ticker := time.NewTicker(time.Second * 1)
|
2017-09-11 23:12:26 -07:00
|
|
|
defer ticker.Stop()
|
2017-09-11 20:48:24 -07:00
|
|
|
for _ = range ticker.C {
|
|
|
|
infos, err := client.AdminPeers(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(infos) < expectedPeercount {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-06 00:59:12 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) WaitForBlocks(num int, waitingTime ...time.Duration) error {
|
2017-09-11 20:48:24 -07:00
|
|
|
var first *big.Int
|
|
|
|
|
|
|
|
client := eth.NewClient()
|
|
|
|
if client == nil {
|
|
|
|
return errors.New("failed to retrieve client")
|
|
|
|
}
|
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
var t time.Duration
|
|
|
|
if len(waitingTime) > 0 {
|
|
|
|
t = waitingTime[0]
|
|
|
|
} else {
|
|
|
|
t = 1 * time.Hour
|
|
|
|
}
|
|
|
|
|
|
|
|
timeout := time.After(t)
|
|
|
|
ticker := time.NewTicker(time.Millisecond * 500)
|
2017-09-11 23:12:26 -07:00
|
|
|
defer ticker.Stop()
|
2017-09-11 20:48:24 -07:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-timeout:
|
2017-09-11 23:46:27 -07:00
|
|
|
return container.ErrNoBlock
|
2017-09-11 20:48:24 -07:00
|
|
|
case <-ticker.C:
|
|
|
|
n, err := client.BlockNumber(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if first == nil {
|
|
|
|
first = new(big.Int).Set(n)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Check if new blocks are getting generated
|
|
|
|
if new(big.Int).Sub(n, first).Int64() >= int64(num) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) WaitForBlockHeight(num int) error {
|
2017-09-11 20:48:24 -07:00
|
|
|
client := eth.NewClient()
|
|
|
|
if client == nil {
|
|
|
|
return errors.New("failed to retrieve client")
|
|
|
|
}
|
|
|
|
defer client.Close()
|
|
|
|
|
|
|
|
ticker := time.NewTicker(time.Millisecond * 500)
|
2017-09-11 23:12:26 -07:00
|
|
|
defer ticker.Stop()
|
2017-09-11 20:48:24 -07:00
|
|
|
for _ = range ticker.C {
|
|
|
|
n, err := client.BlockNumber(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if n.Int64() >= int64(num) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-06 00:59:12 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) WaitForNoBlocks(num int, duration time.Duration) error {
|
2017-09-11 20:48:24 -07:00
|
|
|
var first *big.Int
|
|
|
|
|
|
|
|
client := eth.NewClient()
|
|
|
|
if client == nil {
|
|
|
|
return errors.New("failed to retrieve client")
|
|
|
|
}
|
|
|
|
|
|
|
|
timeout := time.After(duration)
|
2017-09-11 23:12:26 -07:00
|
|
|
ticker := time.NewTicker(time.Millisecond * 500)
|
|
|
|
defer ticker.Stop()
|
2017-09-11 20:48:24 -07:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-timeout:
|
|
|
|
return nil
|
2017-09-11 23:12:26 -07:00
|
|
|
case <-ticker.C:
|
2017-09-11 20:48:24 -07:00
|
|
|
n, err := client.BlockNumber(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if first == nil {
|
|
|
|
first = new(big.Int).Set(n)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Check if new blocks are getting generated
|
|
|
|
if new(big.Int).Sub(n, first).Int64() > int64(num) {
|
|
|
|
return errors.New("generated more blocks than expected")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|
|
|
|
|
2017-09-11 20:48:24 -07:00
|
|
|
func (eth *ethereum) WaitForBalances(addrs []common.Address, duration ...time.Duration) error {
|
|
|
|
client := eth.NewClient()
|
|
|
|
if client == nil {
|
|
|
|
return errors.New("failed to retrieve client")
|
|
|
|
}
|
|
|
|
|
|
|
|
var t time.Duration
|
|
|
|
if len(duration) > 0 {
|
|
|
|
t = duration[0]
|
|
|
|
} else {
|
|
|
|
t = 1 * time.Hour
|
|
|
|
}
|
|
|
|
|
|
|
|
waitBalance := func(addr common.Address) error {
|
|
|
|
timeout := time.After(t)
|
2017-09-11 23:12:26 -07:00
|
|
|
ticker := time.NewTicker(time.Millisecond * 500)
|
|
|
|
defer ticker.Stop()
|
2017-09-11 20:48:24 -07:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-timeout:
|
|
|
|
return container.ErrTimeout
|
2017-09-11 23:12:26 -07:00
|
|
|
case <-ticker.C:
|
2017-09-11 20:48:24 -07:00
|
|
|
n, err := client.BalanceAt(context.Background(), addr, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if new blocks are getting generated
|
|
|
|
if n.Uint64() <= 0 {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
errc := make(chan error, len(addrs))
|
|
|
|
wg.Add(len(addrs))
|
|
|
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
addr := addr
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
errc <- waitBalance(addr)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
// Wait for the first error, then terminate the others.
|
|
|
|
var err error
|
|
|
|
for i := 0; i < len(addrs); i++ {
|
|
|
|
if err = <-errc; err != nil {
|
2017-09-14 03:11:31 -07:00
|
|
|
break
|
2017-09-11 20:48:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2017-09-06 00:59:12 -07:00
|
|
|
func (eth *ethereum) AddPeer(address string) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) StartMining() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (eth *ethereum) StopMining() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-28 01:31:22 -07:00
|
|
|
func (eth *ethereum) Accounts() (addrs []common.Address) {
|
|
|
|
for _, acc := range eth.accounts {
|
|
|
|
addrs = append(addrs, crypto.PubkeyToAddress(acc.PublicKey))
|
|
|
|
}
|
|
|
|
return addrs
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func (eth *ethereum) Host() string {
|
2017-09-21 02:52:47 -07:00
|
|
|
index := strings.LastIndex(eth.chart.Name(), "-")
|
|
|
|
if index < 0 {
|
|
|
|
log.Error("Invalid validator pod name")
|
|
|
|
return ""
|
|
|
|
}
|
2017-09-21 02:53:35 -07:00
|
|
|
name := "validator-svc-" + eth.chart.Name()[index+1:]
|
2017-09-21 02:52:47 -07:00
|
|
|
svc, err := eth.k8sClient.CoreV1().Services(defaultNamespace).Get(name, metav1.GetOptions{})
|
2017-09-06 00:59:12 -07:00
|
|
|
if err != nil {
|
2017-09-21 02:52:47 -07:00
|
|
|
log.Error("Failed to find service", "svc", name, "err", err)
|
2017-09-06 00:59:12 -07:00
|
|
|
return ""
|
|
|
|
}
|
2017-09-21 02:52:47 -07:00
|
|
|
return svc.Status.LoadBalancer.Ingress[0].IP
|
2017-09-06 00:59:12 -07:00
|
|
|
}
|