cmd/puppeth: fix dashboard iframes, extend with new services

This commit is contained in:
Péter Szilágyi 2017-10-23 12:24:25 +03:00
parent 51a86f61be
commit 6eb38e02a8
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
5 changed files with 441 additions and 51 deletions

@ -28,6 +28,140 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// cppEthereumGenesisSpec represents the genesis specification format used by the
// C++ Ethereum implementation.
type cppEthereumGenesisSpec struct {
SealEngine string `json:"sealEngine"`
Params struct {
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
NetworkID hexutil.Uint64 `json:"networkID"`
ChainID hexutil.Uint64 `json:"chainID"`
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
GasLimitBoundDivisor *hexutil.Big `json:"gasLimitBoundDivisor"`
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
DurationLimit *hexutil.Big `json:"durationLimit"`
BlockReward *hexutil.Big `json:"blockReward"`
} `json:"params"`
Genesis struct {
Nonce hexutil.Bytes `json:"nonce"`
Difficulty *hexutil.Big `json:"difficulty"`
MixHash common.Hash `json:"mixHash"`
Author common.Address `json:"author"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ParentHash common.Hash `json:"parentHash"`
ExtraData hexutil.Bytes `json:"extraData"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
} `json:"genesis"`
Accounts map[common.Address]*cppEthereumGenesisSpecAccount `json:"accounts"`
}
// cppEthereumGenesisSpecAccount is the prefunded genesis account and/or precompiled
// contract definition.
type cppEthereumGenesisSpecAccount struct {
Balance *hexutil.Big `json:"balance"`
Nonce uint64 `json:"nonce,omitempty"`
Precompiled *cppEthereumGenesisSpecBuiltin `json:"precompiled,omitempty"`
}
// cppEthereumGenesisSpecBuiltin is the precompiled contract definition.
type cppEthereumGenesisSpecBuiltin struct {
Name string `json:"name,omitempty"`
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
Linear *cppEthereumGenesisSpecLinearPricing `json:"linear,omitempty"`
}
type cppEthereumGenesisSpecLinearPricing struct {
Base uint64 `json:"base"`
Word uint64 `json:"word"`
}
// newCppEthereumGenesisSpec converts a go-ethereum genesis block into a Parity specific
// chain specification format.
func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEthereumGenesisSpec, error) {
// Only ethash is currently supported between go-ethereum and cpp-ethereum
if genesis.Config.Ethash == nil {
return nil, errors.New("unsupported consensus engine")
}
// Reconstruct the chain spec in Parity's format
spec := &cppEthereumGenesisSpec{
SealEngine: "Ethash",
}
spec.Params.AccountStartNonce = 0
spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64())
spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64())
spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64())
spec.Params.ByzantiumForkBlock = (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())
spec.Params.ConstantinopleForkBlock = (hexutil.Uint64)(math.MaxUint64)
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainId.Uint64())
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainId.Uint64())
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit.Uint64())
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxUint64)
spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
spec.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
spec.Params.GasLimitBoundDivisor = (*hexutil.Big)(params.GasLimitBoundDivisor)
spec.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8))
binary.LittleEndian.PutUint64(spec.Genesis.Nonce[:], genesis.Nonce)
spec.Genesis.MixHash = genesis.Mixhash
spec.Genesis.Difficulty = (*hexutil.Big)(genesis.Difficulty)
spec.Genesis.Author = genesis.Coinbase
spec.Genesis.Timestamp = (hexutil.Uint64)(genesis.Timestamp)
spec.Genesis.ParentHash = genesis.ParentHash
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
spec.Accounts = make(map[common.Address]*cppEthereumGenesisSpecAccount)
for address, account := range genesis.Alloc {
spec.Accounts[address] = &cppEthereumGenesisSpecAccount{
Balance: (*hexutil.Big)(account.Balance),
Nonce: account.Nonce,
}
}
spec.Accounts[common.BytesToAddress([]byte{1})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "ecrecover", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 3000},
}
spec.Accounts[common.BytesToAddress([]byte{2})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "sha256", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 60, Word: 12},
}
spec.Accounts[common.BytesToAddress([]byte{3})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "ripemd160", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 600, Word: 120},
}
spec.Accounts[common.BytesToAddress([]byte{4})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "identity", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 15, Word: 3},
}
if genesis.Config.ByzantiumBlock != nil {
spec.Accounts[common.BytesToAddress([]byte{5})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "modexp", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
}
spec.Accounts[common.BytesToAddress([]byte{6})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "alt_bn128_G1_add", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 500},
}
spec.Accounts[common.BytesToAddress([]byte{7})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "alt_bn128_G1_mul", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 40000},
}
spec.Accounts[common.BytesToAddress([]byte{8})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "alt_bn128_pairing_product", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
}
}
return spec, nil
}
// parityChainSpec is the chain specification format used by Parity. // parityChainSpec is the chain specification format used by Parity.
type parityChainSpec struct { type parityChainSpec struct {
Name string `json:"name"` Name string `json:"name"`
@ -206,3 +340,40 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
} }
return spec, nil return spec, nil
} }
// pyEthereumGenesisSpec represents the genesis specification format used by the
// Python Ethereum implementation.
type pyEthereumGenesisSpec struct {
Nonce hexutil.Bytes `json:"nonce"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ExtraData hexutil.Bytes `json:"extraData"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
Difficulty *hexutil.Big `json:"difficulty"`
Mixhash common.Hash `json:"mixhash"`
Coinbase common.Address `json:"coinbase"`
Alloc core.GenesisAlloc `json:"alloc"`
ParentHash common.Hash `json:"parentHash"`
}
// newPyEthereumGenesisSpec converts a go-ethereum genesis block into a Parity specific
// chain specification format.
func newPyEthereumGenesisSpec(network string, genesis *core.Genesis) (*pyEthereumGenesisSpec, error) {
// Only ethash is currently supported between go-ethereum and pyethereum
if genesis.Config.Ethash == nil {
return nil, errors.New("unsupported consensus engine")
}
spec := &pyEthereumGenesisSpec{
Timestamp: (hexutil.Uint64)(genesis.Timestamp),
ExtraData: genesis.ExtraData,
GasLimit: (hexutil.Uint64)(genesis.GasLimit),
Difficulty: (*hexutil.Big)(genesis.Difficulty),
Mixhash: genesis.Mixhash,
Coinbase: genesis.Coinbase,
Alloc: genesis.Alloc,
ParentHash: genesis.ParentHash,
}
spec.Nonce = (hexutil.Bytes)(make([]byte, 8))
binary.LittleEndian.PutUint64(spec.Nonce[:], genesis.Nonce)
return spec, nil
}

@ -18,6 +18,7 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"html/template" "html/template"
"math/rand" "math/rand"
@ -77,25 +78,26 @@ var dashboardContent = `
<div id="sidebar-menu" class="main_menu_side hidden-print main_menu"> <div id="sidebar-menu" class="main_menu_side hidden-print main_menu">
<div class="menu_section"> <div class="menu_section">
<ul class="nav side-menu"> <ul class="nav side-menu">
{{if .EthstatsPage}}<li><a onclick="load('//{{.EthstatsPage}}')"><i class="fa fa-tachometer"></i> Network Stats</a></li>{{end}} {{if .EthstatsPage}}<li id="stats_menu"><a onclick="load('#stats')"><i class="fa fa-tachometer"></i> Network Stats</a></li>{{end}}
{{if .ExplorerPage}}<li><a onclick="load('//{{.ExplorerPage}}')"><i class="fa fa-database"></i> Block Explorer</a></li>{{end}} {{if .ExplorerPage}}<li id="explorer_menu"><a onclick="load('#explorer')"><i class="fa fa-database"></i> Block Explorer</a></li>{{end}}
{{if .WalletPage}}<li><a onclick="load('//{{.WalletPage}}')"><i class="fa fa-address-book-o"></i> Browser Wallet</a></li>{{end}} {{if .WalletPage}}<li id="wallet_menu"><a onclick="load('#wallet')"><i class="fa fa-address-book-o"></i> Browser Wallet</a></li>{{end}}
{{if .FaucetPage}}<li><a onclick="load('//{{.FaucetPage}}')"><i class="fa fa-bath"></i> Crypto Faucet</a></li>{{end}} {{if .FaucetPage}}<li id="faucet_menu"><a onclick="load('#faucet')"><i class="fa fa-bath"></i> Crypto Faucet</a></li>{{end}}
<li id="connect"><a><i class="fa fa-plug"></i> Connect Yourself</a> <li id="connect_menu"><a><i class="fa fa-plug"></i> Connect Yourself</a>
<ul id="connect_list" class="nav child_menu"> <ul id="connect_list" class="nav child_menu">
<li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-geth')">Go Ethereum: Geth</a></li> <li><a onclick="$('#connect_menu').removeClass('active'); $('#connect_list').toggle(); load('#geth')">Go Ethereum: Geth</a></li>
<li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-mist')">Go Ethereum: Wallet & Mist</a></li> <li><a onclick="$('#connect_menu').removeClass('active'); $('#connect_list').toggle(); load('#mist')">Go Ethereum: Wallet & Mist</a></li>
<li><a onclick="$('#connect').removeClass('active'); $('#connect_list').toggle(); load('#connect-go-ethereum-mobile')">Go Ethereum: Android & iOS</a></li> <li><a onclick="$('#connect_menu').removeClass('active'); $('#connect_list').toggle(); load('#mobile')">Go Ethereum: Android & iOS</a></li>{{if .Ethash}}
<li><a onclick="$('#connect_menu').removeClass('active'); $('#connect_list').toggle(); load('#other')">Other Ethereum Clients</a></li>{{end}}
</ul> </ul>
</li> </li>
<li><a onclick="load('#about')"><i class="fa fa-heartbeat"></i> About Puppeth</a></li> <li id="about_menu"><a onclick="load('#about')"><i class="fa fa-heartbeat"></i> About Puppeth</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="right_col" role="main" style="padding: 0"> <div class="right_col" role="main" style="padding: 0 !important">
<div id="connect-go-ethereum-geth" hidden style="padding: 16px;"> <div id="geth" hidden style="padding: 16px;">
<div class="page-title"> <div class="page-title">
<div class="title_left"> <div class="title_left">
<h3>Connect Yourself &ndash; Go Ethereum: Geth</h3> <h3>Connect Yourself &ndash; Go Ethereum: Geth</h3>
@ -155,7 +157,7 @@ var dashboardContent = `
<p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Low end machines with arbitrary storage, weak CPUs and 512MB+ RAM should cope well.</p> <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Low end machines with arbitrary storage, weak CPUs and 512MB+ RAM should cope well.</p>
<br/> <br/>
<p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: <p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} --light init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
<pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre> <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre>
</p> </p>
<br/> <br/>
@ -174,8 +176,8 @@ var dashboardContent = `
<p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Embedded machines with arbitrary storage, low power CPUs and 128MB+ RAM may work.</p> <p>Initial processing required to synchronize is light, as it only verifies the validity of the headers; similarly required disk capacity is small, tallying around 500 bytes per header. Embedded machines with arbitrary storage, low power CPUs and 128MB+ RAM may work.</p>
<br/> <br/>
<p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: <p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} --light init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
<pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=32 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre> <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=16 --ethash.cachesinmem=1 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre>
</p> </p>
<br/> <br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@ -184,7 +186,7 @@ var dashboardContent = `
</div> </div>
</div> </div>
</div> </div>
<div id="connect-go-ethereum-mist" hidden style="padding: 16px;"> <div id="mist" hidden style="padding: 16px;">
<div class="page-title"> <div class="page-title">
<div class="title_left"> <div class="title_left">
<h3>Connect Yourself &ndash; Go Ethereum: Wallet &amp; Mist</h3> <h3>Connect Yourself &ndash; Go Ethereum: Wallet &amp; Mist</h3>
@ -236,7 +238,7 @@ var dashboardContent = `
</div> </div>
</div> </div>
</div> </div>
<div id="connect-go-ethereum-mobile" hidden style="padding: 16px;"> <div id="mobile" hidden style="padding: 16px;">
<div class="page-title"> <div class="page-title">
<div class="title_left"> <div class="title_left">
<h3>Connect Yourself &ndash; Go Ethereum: Android &amp; iOS</h3> <h3>Connect Yourself &ndash; Go Ethereum: Android &amp; iOS</h3>
@ -310,7 +312,101 @@ try! node?.start();
</div> </div>
</div> </div>
</div> </div>
</div> </div>{{if .Ethash}}
<div id="other" hidden style="padding: 16px;">
<div class="page-title">
<div class="title_left">
<h3>Connect Yourself &ndash; Other Ethereum Clients</h3>
</div>
</div>
<div class="clearfix"></div>
<div class="row">
<div class="col-md-6">
<div class="x_panel">
<div class="x_title">
<h2>
<svg height="14px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 115 115"><path fill="#5C8DBC" d="M9.7 83.3V35.5s0-3.4 3.3-5.2c3.3-1.8 39.6-23.5 39.6-23.5s4.6-3.1 9.4 0c0 0 43.1 23.9 42.4 25.3L85.3 43.3s-3.6-8.4-13.1-13c-11.3-5.5-29.7-6.2-42.9 13.3 0 0-8.6 13.5.3 31.6l-19 10.7s-.9-.6-.9-2.6z"/><path fill="#5C8DBC" d="M71 51.3c-2.8-4.7-7.9-7.9-13.8-7.9-8.8 0-16 7.2-16 16 0 2.8.7 5.4 2 7.7L71 51.3z"/><path fill="#194674" d="M43.1 67c2.8 4.7 7.9 7.9 13.8 7.9 8.8 0 16-7.2 16-16 0-2.8-.7-5.4-2-7.7L43.1 67z"/><path fill="#1B598E" d="M104.4 32.1s1.3 52.6-.3 53.6L58 58.6l46.4-26.5z"/><path fill="#FFF" d="M90 57h-3.9v-4.1h-4.2V57h-4v4.1h4V65h4.2v-3.9H90zm13.6 0h-3.9v-4.1h-4.2V57h-4v4.1h4V65h4.2v-3.9h3.9z"/><path fill="#194674" d="M29.5 75.1s9.2 17 28.5 16.1 27.3-16.6 27.3-16.6L104 85.4s4.1.8-41.6 25.7c0 0-4.9 3.3-10.2 0 0 0-41.3-23.1-41.6-25.3l18.9-10.7z"/></svg>
C++ Ethereum <small>Official C++ client from the Ethereum Foundation</small>
</h2>
<div class="clearfix"></div>
</div>
<div class="x_content">
<p>C++ Ethereum is the third most popular of the Ethereum clients, focusing on code portability to a broad range of operating systems and hardware. The client is currently a full node with transaction processing based synchronization.</p>
<br/>
<p>To run a cpp-ethereum node, download <a href="/{{.CppGenesis}}"><code>{{.CppGenesis}}</code></a> and start the node with:
<pre>eth --config {{.CppGenesis}} --datadir $HOME/.{{.Network}} --peerset "{{.CppBootnodes}}"</pre>
</p>
<br/>
<p>You can find cpp-ethereum at <a href="https://github.com/ethereum/cpp-ethereum/" target="about:blank">https://github.com/ethereum/cpp-ethereum/</a>.</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="x_panel">
<div class="x_title">
<h2>
<svg height="14px" version="1.1" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M46.42,13.07S24.51,18.54,35,30.6c3.09,3.55-.81,6.75-0.81,6.75s7.84-4,4.24-9.11C35,23.51,32.46,21.17,46.42,13.07ZM32.1,16.88C45.05,6.65,38.4,0,38.4,0c2.68,10.57-9.46,13.76-13.84,20.34-3,4.48,1.46,9.3,7.53,14.77C29.73,29.77,21.71,25.09,32.1,16.88Z" transform="translate(-8.4)" fill="#e57125"/><path d="M23.6,49.49c-9.84,2.75,6,8.43,18.51,3.06a23.06,23.06,0,0,1-3.52-1.72,36.62,36.62,0,0,1-13.25.56C21.16,50.92,23.6,49.49,23.6,49.49Zm17-5.36a51.7,51.7,0,0,1-17.1.82c-4.19-.43-1.45-2.46-1.45-2.46-10.84,3.6,6,7.68,21.18,3.25A7.59,7.59,0,0,1,40.62,44.13ZM51.55,54.68s1.81,1.49-2,2.64c-7.23,2.19-30.1,2.85-36.45.09-2.28-1,2-2.37,3.35-2.66a8.69,8.69,0,0,1,2.21-.25c-2.54-1.79-16.41,3.51-7,5C37.15,63.67,58.17,57.67,51.55,54.68ZM42.77,39.12a20.42,20.42,0,0,1,2.93-1.57s-4.83.86-9.65,1.27A87.37,87.37,0,0,1,20.66,39c-7.51-1,4.12-3.77,4.12-3.77A22,22,0,0,0,14.7,37.61C8.14,40.79,31,42.23,42.77,39.12Zm2.88,7.77a1,1,0,0,1-.24.31C61.44,43,55.54,32.35,47.88,35a2.19,2.19,0,0,0-1,.79,9,9,0,0,1,1.37-.37C52.1,34.66,57.65,40.65,45.64,46.89Zm0.43,14.75a94.76,94.76,0,0,1-29.17.45s1.47,1.22,9,1.7c11.53,0.74,29.22-.41,29.64-5.86C55.6,57.94,54.79,60,46.08,61.65Z" transform="translate(-8.4)" fill="#5482a2"/></svg>
Ethereum Harmony<small>Third party Java client from EtherCamp</small>
</h2>
<div class="clearfix"></div>
</div>
<div class="x_content">
<p>Ethereum Harmony is a web user-interface based graphical Ethereum client built on top of the EthereumJ Java implementation of the Ethereum protocol. The client currently is a full node with state download based synchronization.</p>
<br/>
<p>To run an Ethereum Harmony node, download <a href="/{{.HarmonyGenesis}}"><code>{{.HarmonyGenesis}}</code></a> and start the node with:
<pre>./gradlew runCustom -DgenesisFile={{.HarmonyGenesis}} -Dpeer.networkId={{.NetworkID}} -Ddatabase.dir=$HOME/.harmony/{{.Network}} {{.HarmonyBootnodes}} </pre>
</p>
<br/>
<p>You can find Ethereum Harmony at <a href="https://github.com/ether-camp/ethereum-harmony/" target="about:blank">https://github.com/ether-camp/ethereum-harmony/</a>.</p>
</div>
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="row">
<div class="col-md-6">
<div class="x_panel">
<div class="x_title">
<h2>
<svg height="14px" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 104.56749 104.56675" version="1.1" viewbox="0 0 144 144" y="0px" x="0px"><metadata id="metadata10"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs id="defs8" /><path style="fill:#676767;" id="path2" d="m 49.0125,12.3195 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m -37.077,28.14 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m 74.153,0.145 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m -65.156,4.258 c 1.43,-0.635 2.076,-2.311 1.441,-3.744 l -1.379,-3.118 h 5.423 v 24.444 h -10.941 a 38.265,38.265 0 0 1 -1.239,-14.607 z m 22.685,0.601 v -7.205 h 12.914 c 0.667,0 4.71,0.771 4.71,3.794 0,2.51 -3.101,3.41 -5.651,3.41 z m -17.631,38.793 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m 46.051,0.145 a 3.108,3.108 0 0 1 6.216,0 3.108,3.108 0 0 1 -6.216,0 m 0.961,-7.048 c -1.531,-0.328 -3.037,0.646 -3.365,2.18 l -1.56,7.28 a 38.265,38.265 0 0 1 -31.911,-0.153 l -1.559,-7.28 c -0.328,-1.532 -1.834,-2.508 -3.364,-2.179 l -6.427,1.38 a 38.265,38.265 0 0 1 -3.323,-3.917 h 31.272 c 0.354,0 0.59,-0.064 0.59,-0.386 v -11.062 c 0,-0.322 -0.236,-0.386 -0.59,-0.386 h -9.146 v -7.012 h 9.892 c 0.903,0 4.828,0.258 6.083,5.275 0.393,1.543 1.256,6.562 1.846,8.169 0.588,1.802 2.982,5.402 5.533,5.402 h 16.146 a 38.265,38.265 0 0 1 -3.544,4.102 z m 17.365,-29.207 a 38.265,38.265 0 0 1 0.081,6.643 h -3.926 c -0.393,0 -0.551,0.258 -0.551,0.643 v 1.803 c 0,4.244 -2.393,5.167 -4.49,5.402 -1.997,0.225 -4.211,-0.836 -4.484,-2.058 -1.178,-6.626 -3.141,-8.041 -6.241,-10.486 3.847,-2.443 7.85,-6.047 7.85,-10.871 0,-5.209 -3.571,-8.49 -6.005,-10.099 -3.415,-2.251 -7.196,-2.702 -8.216,-2.702 h -40.603 a 38.265,38.265 0 0 1 21.408,-12.082 l 4.786,5.021 c 1.082,1.133 2.874,1.175 4.006,0.092 l 5.355,-5.122 a 38.265,38.265 0 0 1 26.196,18.657 l -3.666,8.28 c -0.633,1.433 0.013,3.109 1.442,3.744 z m 9.143,0.134 -0.125,-1.28 3.776,-3.522 c 0.768,-0.716 0.481,-2.157 -0.501,-2.523 l -4.827,-1.805 -0.378,-1.246 3.011,-4.182 c 0.614,-0.85 0.05,-2.207 -0.984,-2.377 l -5.09,-0.828 -0.612,-1.143 2.139,-4.695 c 0.438,-0.956 -0.376,-2.179 -1.428,-2.139 l -5.166,0.18 -0.816,-0.99 1.187,-5.032 c 0.24,-1.022 -0.797,-2.06 -1.819,-1.82 l -5.031,1.186 -0.992,-0.816 0.181,-5.166 c 0.04,-1.046 -1.184,-1.863 -2.138,-1.429 l -4.694,2.14 -1.143,-0.613 -0.83,-5.091 c -0.168,-1.032 -1.526,-1.596 -2.376,-0.984 l -4.185,3.011 -1.244,-0.377 -1.805,-4.828 c -0.366,-0.984 -1.808,-1.267 -2.522,-0.503 l -3.522,3.779 -1.28,-0.125 -2.72,-4.395 c -0.55,-0.89 -2.023,-0.89 -2.571,0 l -2.72,4.395 -1.281,0.125 -3.523,-3.779 c -0.714,-0.764 -2.156,-0.481 -2.522,0.503 l -1.805,4.828 -1.245,0.377 -4.184,-3.011 c -0.85,-0.614 -2.209,-0.048 -2.377,0.984 l -0.83,5.091 -1.143,0.613 -4.694,-2.14 c -0.954,-0.436 -2.178,0.383 -2.138,1.429 l 0.18,5.166 -0.992,0.816 -5.031,-1.186 c -1.022,-0.238 -2.06,0.798 -1.82,1.82 l 1.185,5.032 -0.814,0.99 -5.166,-0.18 c -1.042,-0.03 -1.863,1.183 -1.429,2.139 l 2.14,4.695 -0.613,1.143 -5.09,0.828 c -1.034,0.168 -1.594,1.527 -0.984,2.377 l 3.011,4.182 -0.378,1.246 -4.828,1.805 c -0.98,0.366 -1.267,1.807 -0.501,2.523 l 3.777,3.522 -0.125,1.28 -4.394,2.72 c -0.89,0.55 -0.89,2.023 0,2.571 l 4.394,2.72 0.125,1.28 -3.777,3.523 c -0.766,0.714 -0.479,2.154 0.501,2.522 l 4.828,1.805 0.378,1.246 -3.011,4.183 c -0.612,0.852 -0.049,2.21 0.985,2.376 l 5.089,0.828 0.613,1.145 -2.14,4.693 c -0.436,0.954 0.387,2.181 1.429,2.139 l 5.164,-0.181 0.816,0.992 -1.185,5.033 c -0.24,1.02 0.798,2.056 1.82,1.816 l 5.031,-1.185 0.992,0.814 -0.18,5.167 c -0.04,1.046 1.184,1.864 2.138,1.428 l 4.694,-2.139 1.143,0.613 0.83,5.088 c 0.168,1.036 1.527,1.596 2.377,0.986 l 4.182,-3.013 1.246,0.379 1.805,4.826 c 0.366,0.98 1.808,1.269 2.522,0.501 l 3.523,-3.777 1.281,0.128 2.72,4.394 c 0.548,0.886 2.021,0.888 2.571,0 l 2.72,-4.394 1.28,-0.128 3.522,3.777 c 0.714,0.768 2.156,0.479 2.522,-0.501 l 1.805,-4.826 1.246,-0.379 4.183,3.013 c 0.85,0.61 2.208,0.048 2.376,-0.986 l 0.83,-5.088 1.143,-0.613 4.694,2.139 c 0.954,0.436 2.176,-0.38 2.138,-1.428 l -0.18,-5.167 0.991,-0.814 5.031,1.185 c 1.022,0.24 2.059,-0.796 1.819,-1.816 l -1.185,-5.033 0.814,-0.992 5.166,0.181 c 1.042,0.042 1.866,-1.185 1.428,-2.139 l -2.139,-4.693 0.612,-1.145 5.09,-0.828 c 1.036,-0.166 1.598,-1.524 0.984,-2.376 l -3.011,-4.183 0.378,-1.246 4.827,-1.805 c 0.982,-0.368 1.269,-1.808 0.501,-2.522 l -3.776,-3.523 0.125,-1.28 4.394,-2.72 c 0.89,-0.548 0.891,-2.021 10e-4,-2.571 z" /></svg>
Parity<small>Third party Rust client from Parity Technologies</small>
</h2>
<div class="clearfix"></div>
</div>
<div class="x_content">
<p>Parity is a fast, light and secure Ethereum client, supporting both headless mode of operation as well as a web user interface for direct manual interaction. The client is currently a full node with transaction processing based synchronization and state pruning enabled.</p>
<br/>
<p>To run a Parity node, download <a href="/{{.ParityGenesis}}"><code>{{.ParityGenesis}}</code></a> and start the node with:
<pre>parity --chain={{.ParityGenesis}}</pre>
</p>
<br/>
<p>You can find Parity at <a href="https://parity.io/" target="about:blank">https://parity.io/</a>.</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="x_panel">
<div class="x_title">
<h2>
<svg height="14px" version="1.1" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><linearGradient id="a" x1="13.79" y1="38.21" x2="75.87" y2="-15.2" gradientTransform="matrix(0.56, 0, 0, -0.57, -8.96, 23.53)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#5c9fd3"/><stop offset="1" stop-color="#316a99"/></linearGradient><linearGradient id="b" x1="99.87" y1="-47.53" x2="77.7" y2="-16.16" gradientTransform="matrix(0.56, 0, 0, -0.57, -8.96, 23.53)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffd43d"/><stop offset="1" stop-color="#fee875"/></linearGradient></defs><g><path d="M31.62,0a43.6,43.6,0,0,0-7.3.62c-6.46,1.14-7.63,3.53-7.63,7.94v5.82H32v1.94H11a9.53,9.53,0,0,0-9.54,7.74,28.54,28.54,0,0,0,0,15.52c1.09,4.52,3.68,7.74,8.11,7.74h5.25v-7a9.7,9.7,0,0,1,9.54-9.48H39.58a7.69,7.69,0,0,0,7.63-7.76V8.56c0-4.14-3.49-7.25-7.63-7.94A47.62,47.62,0,0,0,31.62,0ZM23.37,4.68A2.91,2.91,0,1,1,20.5,7.6,2.9,2.9,0,0,1,23.37,4.68Z" transform="translate(-0.35)" fill="url(#a)"/><path d="M49.12,16.32V23.1a9.79,9.79,0,0,1-9.54,9.68H24.33a7.79,7.79,0,0,0-7.63,7.76V55.08c0,4.14,3.6,6.57,7.63,7.76a25.55,25.55,0,0,0,15.25,0c3.84-1.11,7.63-3.35,7.63-7.76V49.26H32V47.32H54.85c4.44,0,6.09-3.1,7.63-7.74s1.53-9.38,0-15.52c-1.1-4.42-3.19-7.74-7.63-7.74H49.12ZM40.54,53.14A2.91,2.91,0,1,1,37.67,56,2.88,2.88,0,0,1,40.54,53.14Z" transform="translate(-0.35)" fill="url(#b)"/></g></svg>
PyEthApp<small>Official Python client from the Ethereum Foundation</small>
</h2>
<div class="clearfix"></div>
</div>
<div class="x_content">
<p>Pyethapp is the Ethereum Foundation's research client, aiming to provide an easily hackable and extendable codebase. The client is currently a full node with transaction processing based synchronization and state pruning enabled.</p>
<br/>
<p>To run a pyethapp node, download <a href="/{{.PythonGenesis}}"><code>{{.PythonGenesis}}</code></a> and start the node with:
<pre>mkdir -p $HOME/.config/pyethapp/{{.Network}}</pre>
<pre>pyethapp -c eth.genesis="$(cat {{.PythonGenesis}})" -c eth.network_id={{.NetworkID}} -c data_dir=$HOME/.config/pyethapp/{{.Network}} -c discovery.bootstrap_nodes="[{{.PythonBootnodes}}]" -c eth.block.HOMESTEAD_FORK_BLKNUM={{.Homestead}} -c eth.block.ANTI_DOS_FORK_BLKNUM={{.Tangerine}} -c eth.block.SPURIOUS_DRAGON_FORK_BLKNUM={{.Spurious}} -c eth.block.METROPOLIS_FORK_BLKNUM={{.Byzantium}} -c eth.block.DAO_FORK_BLKNUM=18446744073709551615 run --console</pre>
</p>
<br/>
<p>You can find pyethapp at <a href="https://github.com/ethereum/pyethapp/" target="about:blank">https://github.com/ethereum/pyethapp/</a>.</p>
</div>
</div>
</div>
</div>
</div>{{end}}
<div id="about" hidden> <div id="about" hidden>
<div class="row vertical-center"> <div class="row vertical-center">
<div style="margin: 0 auto;"> <div style="margin: 0 auto;">
@ -345,13 +441,33 @@ try! node?.start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/js/custom.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gentelella/1.3.0/js/custom.min.js"></script>
<script> <script>
var load = function(url) { var load = function(hash) {
$("#connect-go-ethereum-geth").fadeOut(300) window.location.hash = hash;
$("#connect-go-ethereum-mist").fadeOut(300)
$("#connect-go-ethereum-mobile").fadeOut(300) // Fade out all possible pages (yes, ugly, no, don't care)
$("#geth").fadeOut(300)
$("#mist").fadeOut(300)
$("#mobile").fadeOut(300)
$("#other").fadeOut(300)
$("#about").fadeOut(300) $("#about").fadeOut(300)
$("#frame-wrapper").fadeOut(300); $("#frame-wrapper").fadeOut(300);
// Depending on the hash, resolve it into a local or remote URL
var url = hash;
switch (hash) {
case "#stats":
url = "//{{.EthstatsPage}}";
break;
case "#explorer":
url = "//{{.ExplorerPage}}";
break;
case "#wallet":
url = "//{{.WalletPage}}";
break;
case "#faucet":
url = "//{{.FaucetPage}}";
break;
}
setTimeout(function() { setTimeout(function() {
if (url.substring(0, 1) == "#") { if (url.substring(0, 1) == "#") {
$('.body').css({overflowY: 'auto'}); $('.body').css({overflowY: 'auto'});
@ -365,13 +481,10 @@ try! node?.start();
} }
var resize = function() { var resize = function() {
var sidebar = $($(".navbar")[0]).width(); var sidebar = $($(".navbar")[0]).width();
var content = 1920;
var limit = document.body.clientWidth - sidebar; var limit = document.body.clientWidth - sidebar;
var scale = limit / content; var scale = limit / 1920;
console.log(document.body.clientHeight); $("#frame-wrapper").width(limit);
$("#frame-wrapper").width(content / scale);
$("#frame-wrapper").height(document.body.clientHeight / scale); $("#frame-wrapper").height(document.body.clientHeight / scale);
$("#frame-wrapper").css({ $("#frame-wrapper").css({
transform: 'scale(' + (scale) + ')', transform: 'scale(' + (scale) + ')',
@ -380,9 +493,17 @@ try! node?.start();
}; };
$(window).resize(resize); $(window).resize(resize);
var item = $(".side-menu").children()[0]; if (window.location.hash == "") {
$(item).children()[0].click(); var item = $(".side-menu").children()[0];
$(item).addClass("active"); $(item).children()[0].click();
$(item).addClass("active");
} else {
load(window.location.hash);
var menu = $(window.location.hash + "_menu");
if (menu !== undefined) {
$(menu).addClass("active");
}
}
</script> </script>
</body> </body>
</html> </html>
@ -406,6 +527,10 @@ RUN \
echo '});' >> server.js echo '});' >> server.js
ADD {{.Network}}.json /dashboard/{{.Network}}.json ADD {{.Network}}.json /dashboard/{{.Network}}.json
ADD {{.Network}}-cpp.json /dashboard/{{.Network}}-cpp.json
ADD {{.Network}}-harmony.json /dashboard/{{.Network}}-harmony.json
ADD {{.Network}}-parity.json /dashboard/{{.Network}}-parity.json
ADD {{.Network}}-python.json /dashboard/{{.Network}}-python.json
ADD index.html /dashboard/index.html ADD index.html /dashboard/index.html
ADD puppeth.png /dashboard/puppeth.png ADD puppeth.png /dashboard/puppeth.png
@ -423,8 +548,12 @@ services:
build: . build: .
image: {{.Network}}/dashboard{{if not .VHost}} image: {{.Network}}/dashboard{{if not .VHost}}
ports: ports:
- "{{.Port}}:80"{{else}} - "{{.Port}}:80"{{end}}
environment: environment:
- ETHSTATS_PAGE={{.EthstatsPage}}
- EXPLORER_PAGE={{.ExplorerPage}}
- WALLET_PAGE={{.WalletPage}}
- FAUCET_PAGE={{.FaucetPage}}{{if .VHost}}
- VIRTUAL_HOST={{.VHost}}{{end}} - VIRTUAL_HOST={{.VHost}}{{end}}
logging: logging:
driver: "json-file" driver: "json-file"
@ -437,7 +566,7 @@ services:
// deployDashboard deploys a new dashboard container to a remote machine via SSH, // deployDashboard deploys a new dashboard container to a remote machine via SSH,
// docker and docker-compose. If an instance with the specified network name // docker and docker-compose. If an instance with the specified network name
// already exists there, it will be overwritten! // already exists there, it will be overwritten!
func deployDashboard(client *sshClient, network string, port int, vhost string, services map[string]string, conf *config, ethstats bool, nocache bool) ([]byte, error) { func deployDashboard(client *sshClient, network string, conf *config, config *dashboardInfos, nocache bool) ([]byte, error) {
// Generate the content to upload to the server // Generate the content to upload to the server
workdir := fmt.Sprintf("%d", rand.Int63()) workdir := fmt.Sprintf("%d", rand.Int63())
files := make(map[string][]byte) files := make(map[string][]byte)
@ -450,37 +579,95 @@ func deployDashboard(client *sshClient, network string, port int, vhost string,
composefile := new(bytes.Buffer) composefile := new(bytes.Buffer)
template.Must(template.New("").Parse(dashboardComposefile)).Execute(composefile, map[string]interface{}{ template.Must(template.New("").Parse(dashboardComposefile)).Execute(composefile, map[string]interface{}{
"Network": network, "Network": network,
"Port": port, "Port": config.port,
"VHost": vhost, "VHost": config.host,
"EthstatsPage": config.ethstats,
"ExplorerPage": config.explorer,
"WalletPage": config.wallet,
"FaucetPage": config.faucet,
}) })
files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
statsLogin := fmt.Sprintf("yournode:%s", conf.ethstats) statsLogin := fmt.Sprintf("yournode:%s", conf.ethstats)
if !ethstats { if !config.trusted {
statsLogin = "" statsLogin = ""
} }
indexfile := new(bytes.Buffer) indexfile := new(bytes.Buffer)
bootCpp := make([]string, len(conf.bootFull))
for i, boot := range conf.bootFull {
bootCpp[i] = "required:" + strings.TrimPrefix(boot, "enode://")
}
bootHarmony := make([]string, len(conf.bootFull))
for i, boot := range conf.bootFull {
bootHarmony[i] = fmt.Sprintf("-Dpeer.active.%d.url=%s", i, boot)
}
bootPython := make([]string, len(conf.bootFull))
for i, boot := range conf.bootFull {
bootPython[i] = "'" + boot + "'"
}
template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{ template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{
"Network": network, "Network": network,
"NetworkID": conf.genesis.Config.ChainId, "NetworkID": conf.genesis.Config.ChainId,
"NetworkTitle": strings.Title(network), "NetworkTitle": strings.Title(network),
"EthstatsPage": services["ethstats"], "EthstatsPage": config.ethstats,
"ExplorerPage": services["explorer"], "ExplorerPage": config.explorer,
"WalletPage": services["wallet"], "WalletPage": config.wallet,
"FaucetPage": services["faucet"], "FaucetPage": config.faucet,
"GethGenesis": network + ".json", "GethGenesis": network + ".json",
"BootnodesFull": conf.bootFull, "BootnodesFull": conf.bootFull,
"BootnodesLight": conf.bootLight, "BootnodesLight": conf.bootLight,
"BootnodesFullFlat": strings.Join(conf.bootFull, ","), "BootnodesFullFlat": strings.Join(conf.bootFull, ","),
"BootnodesLightFlat": strings.Join(conf.bootLight, ","), "BootnodesLightFlat": strings.Join(conf.bootLight, ","),
"Ethstats": statsLogin, "Ethstats": statsLogin,
"Ethash": conf.genesis.Config.Ethash != nil,
"CppGenesis": network + "-cpp.json",
"CppBootnodes": strings.Join(bootCpp, " "),
"HarmonyGenesis": network + "-harmony.json",
"HarmonyBootnodes": strings.Join(bootHarmony, " "),
"ParityGenesis": network + "-parity.json",
"PythonGenesis": network + "-python.json",
"PythonBootnodes": strings.Join(bootPython, ","),
"Homestead": conf.genesis.Config.HomesteadBlock,
"Tangerine": conf.genesis.Config.EIP150Block,
"Spurious": conf.genesis.Config.EIP155Block,
"Byzantium": conf.genesis.Config.ByzantiumBlock,
}) })
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes() files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
// Marshal the genesis spec files for go-ethereum and all the other clients
genesis, _ := conf.genesis.MarshalJSON() genesis, _ := conf.genesis.MarshalJSON()
files[filepath.Join(workdir, network+".json")] = genesis files[filepath.Join(workdir, network+".json")] = genesis
if conf.genesis.Config.Ethash != nil {
cppSpec, err := newCppEthereumGenesisSpec(network, conf.genesis)
if err != nil {
return nil, err
}
cppSpecJSON, _ := json.Marshal(cppSpec)
files[filepath.Join(workdir, network+"-cpp.json")] = cppSpecJSON
harmonySpecJSON, _ := conf.genesis.MarshalJSON()
files[filepath.Join(workdir, network+"-harmony.json")] = harmonySpecJSON
paritySpec, err := newParityChainSpec(network, conf.genesis, conf.bootFull)
if err != nil {
return nil, err
}
paritySpecJSON, _ := json.Marshal(paritySpec)
files[filepath.Join(workdir, network+"-parity.json")] = paritySpecJSON
pyethSpec, err := newPyEthereumGenesisSpec(network, conf.genesis)
if err != nil {
return nil, err
}
pyethSpecJSON, _ := json.Marshal(pyethSpec)
files[filepath.Join(workdir, network+"-python.json")] = pyethSpecJSON
} else {
for _, client := range []string{"cpp", "harmony", "parity", "python"} {
files[filepath.Join(workdir, network+"-"+client+".json")] = []byte{}
}
}
files[filepath.Join(workdir, "puppeth.png")] = dashboardMascot files[filepath.Join(workdir, "puppeth.png")] = dashboardMascot
// Upload the deployment files to the remote server (and clean up afterwards) // Upload the deployment files to the remote server (and clean up afterwards)
@ -499,8 +686,14 @@ func deployDashboard(client *sshClient, network string, port int, vhost string,
// dashboardInfos is returned from an dashboard status check to allow reporting // dashboardInfos is returned from an dashboard status check to allow reporting
// various configuration parameters. // various configuration parameters.
type dashboardInfos struct { type dashboardInfos struct {
host string host string
port int port int
trusted bool
ethstats string
explorer string
wallet string
faucet string
} }
// Report converts the typed struct into a plain string->string map, cotnaining // Report converts the typed struct into a plain string->string map, cotnaining
@ -509,6 +702,10 @@ func (info *dashboardInfos) Report() map[string]string {
return map[string]string{ return map[string]string{
"Website address": info.host, "Website address": info.host,
"Website listener port": strconv.Itoa(info.port), "Website listener port": strconv.Itoa(info.port),
"Ethstats service": info.ethstats,
"Explorer service": info.explorer,
"Wallet service": info.wallet,
"Faucet service": info.faucet,
} }
} }
@ -544,7 +741,11 @@ func checkDashboard(client *sshClient, network string) (*dashboardInfos, error)
} }
// Container available, assemble and return the useful infos // Container available, assemble and return the useful infos
return &dashboardInfos{ return &dashboardInfos{
host: host, host: host,
port: port, port: port,
ethstats: infos.envvars["ETHSTATS_PAGE"],
explorer: infos.envvars["EXPLORER_PAGE"],
wallet: infos.envvars["WALLET_PAGE"],
faucet: infos.envvars["FAUCET_PAGE"],
}, nil }, nil
} }

@ -58,7 +58,6 @@ func (w *wizard) deployDashboard() {
available[service] = append(available[service], server) available[service] = append(available[service], server)
} }
} }
listing := make(map[string]string)
for _, service := range []string{"ethstats", "explorer", "wallet", "faucet"} { for _, service := range []string{"ethstats", "explorer", "wallet", "faucet"} {
// Gather all the locally hosted pages of this type // Gather all the locally hosted pages of this type
var pages []string var pages []string
@ -74,6 +73,14 @@ func (w *wizard) deployDashboard() {
if infos, err := checkEthstats(client, w.network); err == nil { if infos, err := checkEthstats(client, w.network); err == nil {
port = infos.port port = infos.port
} }
case "explorer":
if infos, err := checkExplorer(client, w.network); err == nil {
port = infos.webPort
}
case "wallet":
if infos, err := checkWallet(client, w.network); err == nil {
port = infos.webPort
}
case "faucet": case "faucet":
if infos, err := checkFaucet(client, w.network); err == nil { if infos, err := checkFaucet(client, w.network); err == nil {
port = infos.port port = infos.port
@ -101,30 +108,41 @@ func (w *wizard) deployDashboard() {
log.Error("Invalid listing choice, aborting") log.Error("Invalid listing choice, aborting")
return return
} }
var page string
switch { switch {
case choice <= len(pages): case choice <= len(pages):
listing[service] = pages[choice-1] page = pages[choice-1]
case choice == len(pages)+1: case choice == len(pages)+1:
fmt.Println() fmt.Println()
fmt.Printf("Which address is the external %s service at?\n", service) fmt.Printf("Which address is the external %s service at?\n", service)
listing[service] = w.readString() page = w.readString()
default: default:
// No service hosting for this // No service hosting for this
} }
// Save the users choice
switch service {
case "ethstats":
infos.ethstats = page
case "explorer":
infos.explorer = page
case "wallet":
infos.wallet = page
case "faucet":
infos.faucet = page
}
} }
// If we have ethstats running, ask whether to make the secret public or not // If we have ethstats running, ask whether to make the secret public or not
var ethstats bool
if w.conf.ethstats != "" { if w.conf.ethstats != "" {
fmt.Println() fmt.Println()
fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)") fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)")
ethstats = w.readDefaultString("y") == "y" infos.trusted = w.readDefaultString("y") == "y"
} }
// Try to deploy the dashboard container on the host // Try to deploy the dashboard container on the host
fmt.Println() fmt.Println()
fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n")
nocache := w.readDefaultString("n") != "n" nocache := w.readDefaultString("n") != "n"
if out, err := deployDashboard(client, w.network, infos.port, infos.host, listing, &w.conf, ethstats, nocache); err != nil { if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil {
log.Error("Failed to deploy dashboard container", "err", err) log.Error("Failed to deploy dashboard container", "err", err)
if len(out) > 0 { if len(out) > 0 {
fmt.Printf("%s\n", out) fmt.Printf("%s\n", out)

@ -37,7 +37,7 @@ func (w *wizard) makeGenesis() {
genesis := &core.Genesis{ genesis := &core.Genesis{
Timestamp: uint64(time.Now().Unix()), Timestamp: uint64(time.Now().Unix()),
GasLimit: 4700000, GasLimit: 4700000,
Difficulty: big.NewInt(1048576), Difficulty: big.NewInt(524288),
Alloc: make(core.GenesisAlloc), Alloc: make(core.GenesisAlloc),
Config: &params.ChainConfig{ Config: &params.ChainConfig{
HomesteadBlock: big.NewInt(1), HomesteadBlock: big.NewInt(1),

@ -65,7 +65,7 @@ func (w *wizard) deployNode(boot bool) {
fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.datadir) fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.datadir)
infos.datadir = w.readDefaultString(infos.datadir) infos.datadir = w.readDefaultString(infos.datadir)
} }
if w.conf.genesis.Config.Ethash != nil { if w.conf.genesis.Config.Ethash != nil && !boot {
fmt.Println() fmt.Println()
if infos.ethashdir == "" { if infos.ethashdir == "" {
fmt.Printf("Where should the ethash mining DAGs be stored on the remote machine?\n") fmt.Printf("Where should the ethash mining DAGs be stored on the remote machine?\n")