forked from cerc-io/plugeth
Merge branch 'develop' into rpcfabian
This commit is contained in:
commit
118ad22ee5
@ -3,7 +3,7 @@
|
|||||||
<title>JevCoin</title>
|
<title>JevCoin</title>
|
||||||
<head>
|
<head>
|
||||||
<script type="text/javascript" src="../ext/bignumber.min.js"></script>
|
<script type="text/javascript" src="../ext/bignumber.min.js"></script>
|
||||||
<script type="text/javascript" src="../ext/ethereum.js/dist/ethereum.js"></script>
|
<script type="text/javascript" src="../ext/ethereum.js/dist/ethereum-light.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 2536888f817a1a15b05dab4727d30f73d6763f07
|
Subproject commit 31e046dbecea51d3b99b21f3e7e60ddfb6c39304
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"gopkg.in/fatih/set.v0"
|
"gopkg.in/fatih/set.v0"
|
||||||
@ -83,7 +84,7 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
statedb.Update(nil)
|
statedb.Update()
|
||||||
|
|
||||||
cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas))
|
cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas))
|
||||||
receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative)
|
receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative)
|
||||||
@ -210,14 +211,16 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
|
// Verify uncles
|
||||||
if err = sm.AccumulateRewards(state, block, parent); err != nil {
|
if err = sm.VerifyUncles(state, block, parent); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
|
||||||
|
AccumulateRewards(state, block)
|
||||||
|
|
||||||
// Commit state objects/accounts to a temporary trie (does not save)
|
// Commit state objects/accounts to a temporary trie (does not save)
|
||||||
// used to calculate the state root.
|
// used to calculate the state root.
|
||||||
state.Update(common.Big0)
|
state.Update()
|
||||||
if header.Root != state.Root() {
|
if header.Root != state.Root() {
|
||||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
|
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
|
||||||
return
|
return
|
||||||
@ -252,7 +255,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big
|
|||||||
// an uncle or anything that isn't on the current block chain.
|
// an uncle or anything that isn't on the current block chain.
|
||||||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||||
func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
|
func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
|
||||||
if len(block.Extra) > 1024 {
|
if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
||||||
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
|
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,13 +264,11 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
|
|||||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use use minGasLimit and gasLimitBoundDivisor from
|
// block.gasLimit - parent.gasLimit <= parent.gasLimit / GasLimitBoundDivisor
|
||||||
// https://github.com/ethereum/common/blob/master/params.json
|
|
||||||
// block.gasLimit - parent.gasLimit <= parent.gasLimit / 1024
|
|
||||||
a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
|
a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
|
||||||
a.Abs(a)
|
a.Abs(a)
|
||||||
b := new(big.Int).Div(parent.GasLimit, big.NewInt(1024))
|
b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor)
|
||||||
if !(a.Cmp(b) < 0) {
|
if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
|
||||||
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
|
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,9 +292,27 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, parent *types.Block) error {
|
func AccumulateRewards(statedb *state.StateDB, block *types.Block) {
|
||||||
reward := new(big.Int).Set(BlockReward)
|
reward := new(big.Int).Set(BlockReward)
|
||||||
|
|
||||||
|
for _, uncle := range block.Uncles() {
|
||||||
|
num := new(big.Int).Add(big.NewInt(8), uncle.Number)
|
||||||
|
num.Sub(num, block.Number())
|
||||||
|
|
||||||
|
r := new(big.Int)
|
||||||
|
r.Mul(BlockReward, num)
|
||||||
|
r.Div(r, big.NewInt(8))
|
||||||
|
|
||||||
|
statedb.AddBalance(uncle.Coinbase, r)
|
||||||
|
|
||||||
|
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the account associated with the coinbase
|
||||||
|
statedb.AddBalance(block.Header().Coinbase, reward)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
|
||||||
ancestors := set.New()
|
ancestors := set.New()
|
||||||
uncles := set.New()
|
uncles := set.New()
|
||||||
ancestorHeaders := make(map[common.Hash]*types.Header)
|
ancestorHeaders := make(map[common.Hash]*types.Header)
|
||||||
@ -327,21 +346,8 @@ func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, paren
|
|||||||
return ValidationError(fmt.Sprintf("%v", err))
|
return ValidationError(fmt.Sprintf("%v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
num := new(big.Int).Add(big.NewInt(8), uncle.Number)
|
|
||||||
num.Sub(num, block.Number())
|
|
||||||
|
|
||||||
r := new(big.Int)
|
|
||||||
r.Mul(BlockReward, num)
|
|
||||||
r.Div(r, big.NewInt(8))
|
|
||||||
|
|
||||||
statedb.AddBalance(uncle.Coinbase, r)
|
|
||||||
|
|
||||||
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the account associated with the coinbase
|
|
||||||
statedb.AddBalance(block.Header().Coinbase, reward)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,7 +364,6 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
|
|||||||
)
|
)
|
||||||
|
|
||||||
sm.TransitionState(state, parent, block, true)
|
sm.TransitionState(state, parent, block, true)
|
||||||
sm.AccumulateRewards(state, block, parent)
|
|
||||||
|
|
||||||
return state.Logs(), nil
|
return state.Logs(), nil
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// So we can generate blocks easily
|
// So we can generate blocks easily
|
||||||
@ -81,7 +81,7 @@ func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Datab
|
|||||||
cbase := state.GetOrNewStateObject(addr)
|
cbase := state.GetOrNewStateObject(addr)
|
||||||
cbase.SetGasPool(CalcGasLimit(parent, block))
|
cbase.SetGasPool(CalcGasLimit(parent, block))
|
||||||
cbase.AddBalance(BlockReward)
|
cbase.AddBalance(BlockReward)
|
||||||
state.Update(common.Big0)
|
state.Update()
|
||||||
block.SetRoot(state.Root())
|
block.SetRoot(state.Root())
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,18 +33,15 @@ type StateQuery interface {
|
|||||||
func CalcDifficulty(block, parent *types.Header) *big.Int {
|
func CalcDifficulty(block, parent *types.Header) *big.Int {
|
||||||
diff := new(big.Int)
|
diff := new(big.Int)
|
||||||
|
|
||||||
diffBoundDiv := big.NewInt(2048)
|
adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor)
|
||||||
min := big.NewInt(131072)
|
if big.NewInt(int64(block.Time)-int64(parent.Time)).Cmp(params.DurationLimit) < 0 {
|
||||||
|
|
||||||
adjust := new(big.Int).Div(parent.Difficulty, diffBoundDiv)
|
|
||||||
if (block.Time - parent.Time) < 8 {
|
|
||||||
diff.Add(parent.Difficulty, adjust)
|
diff.Add(parent.Difficulty, adjust)
|
||||||
} else {
|
} else {
|
||||||
diff.Sub(parent.Difficulty, adjust)
|
diff.Sub(parent.Difficulty, adjust)
|
||||||
}
|
}
|
||||||
|
|
||||||
if diff.Cmp(min) < 0 {
|
if diff.Cmp(params.MinimumDifficulty) < 0 {
|
||||||
return min
|
return params.MinimumDifficulty
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff
|
return diff
|
||||||
@ -76,7 +74,7 @@ func CalcGasLimit(parent, block *types.Block) *big.Int {
|
|||||||
result := new(big.Int).Add(previous, curInt)
|
result := new(big.Int).Add(previous, curInt)
|
||||||
result.Div(result, big.NewInt(1024))
|
result.Div(result, big.NewInt(1024))
|
||||||
|
|
||||||
return common.BigMax(GenesisGasLimit, result)
|
return common.BigMax(params.GenesisGasLimit, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChainManager struct {
|
type ChainManager struct {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Execution struct {
|
type Execution struct {
|
||||||
@ -43,7 +44,7 @@ func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.
|
|||||||
|
|
||||||
env := self.env
|
env := self.env
|
||||||
evm := self.evm
|
evm := self.evm
|
||||||
if env.Depth() == vm.MaxCallDepth {
|
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||||
caller.ReturnGas(self.Gas, self.price)
|
caller.ReturnGas(self.Gas, self.price)
|
||||||
|
|
||||||
return nil, vm.DepthError{}
|
return nil, vm.DepthError{}
|
||||||
|
@ -3,12 +3,12 @@ package core
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -18,13 +18,11 @@ import (
|
|||||||
var ZeroHash256 = make([]byte, 32)
|
var ZeroHash256 = make([]byte, 32)
|
||||||
var ZeroHash160 = make([]byte, 20)
|
var ZeroHash160 = make([]byte, 20)
|
||||||
var ZeroHash512 = make([]byte, 64)
|
var ZeroHash512 = make([]byte, 64)
|
||||||
var GenesisDiff = big.NewInt(131072)
|
|
||||||
var GenesisGasLimit = big.NewInt(3141592)
|
|
||||||
|
|
||||||
func GenesisBlock(db common.Database) *types.Block {
|
func GenesisBlock(db common.Database) *types.Block {
|
||||||
genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, GenesisDiff, 42, "")
|
genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, params.GenesisDifficulty, 42, "")
|
||||||
genesis.Header().Number = common.Big0
|
genesis.Header().Number = common.Big0
|
||||||
genesis.Header().GasLimit = GenesisGasLimit
|
genesis.Header().GasLimit = params.GenesisGasLimit
|
||||||
genesis.Header().GasUsed = common.Big0
|
genesis.Header().GasUsed = common.Big0
|
||||||
genesis.Header().Time = 0
|
genesis.Header().Time = 0
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ func TestNull(t *testing.T) {
|
|||||||
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
||||||
value := make([]byte, 16)
|
value := make([]byte, 16)
|
||||||
state.SetState(address, common.Hash{}, value)
|
state.SetState(address, common.Hash{}, value)
|
||||||
state.Update(nil)
|
state.Update()
|
||||||
state.Sync()
|
state.Sync()
|
||||||
value = state.GetState(address, common.Hash{})
|
value = state.GetState(address, common.Hash{})
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,7 @@ func (self *StateDB) Refunds() map[string]*big.Int {
|
|||||||
return self.refund
|
return self.refund
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Update(gasUsed *big.Int) {
|
func (self *StateDB) Update() {
|
||||||
self.refund = make(map[string]*big.Int)
|
self.refund = make(map[string]*big.Int)
|
||||||
|
|
||||||
for _, stateObject := range self.stateObjects {
|
for _, stateObject := range self.stateObjects {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
const tryJit = false
|
const tryJit = false
|
||||||
@ -178,7 +179,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Transaction gas
|
// Transaction gas
|
||||||
if err = self.UseGas(vm.GasTx); err != nil {
|
if err = self.UseGas(params.TxGas); err != nil {
|
||||||
return nil, nil, InvalidTxError(err)
|
return nil, nil, InvalidTxError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,9 +187,9 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
|
|||||||
dgas := new(big.Int)
|
dgas := new(big.Int)
|
||||||
for _, byt := range self.data {
|
for _, byt := range self.data {
|
||||||
if byt != 0 {
|
if byt != 0 {
|
||||||
dgas.Add(dgas, vm.GasTxDataNonzeroByte)
|
dgas.Add(dgas, params.TxDataNonZeroGas)
|
||||||
} else {
|
} else {
|
||||||
dgas.Add(dgas, vm.GasTxDataZeroByte)
|
dgas.Add(dgas, params.TxDataZeroGas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
|
|||||||
ret, err, ref = vmenv.Create(sender, self.msg.Data(), self.gas, self.gasPrice, self.value)
|
ret, err, ref = vmenv.Create(sender, self.msg.Data(), self.gas, self.gasPrice, self.value)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
dataGas := big.NewInt(int64(len(ret)))
|
dataGas := big.NewInt(int64(len(ret)))
|
||||||
dataGas.Mul(dataGas, vm.GasCreateByte)
|
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||||
if err := self.UseGas(dataGas); err == nil {
|
if err := self.UseGas(dataGas); err == nil {
|
||||||
ref.SetCode(ret)
|
ref.SetCode(ret)
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Address interface {
|
type Address interface {
|
||||||
@ -27,28 +28,28 @@ func PrecompiledContracts() map[string]*PrecompiledAccount {
|
|||||||
return map[string]*PrecompiledAccount{
|
return map[string]*PrecompiledAccount{
|
||||||
// ECRECOVER
|
// ECRECOVER
|
||||||
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||||
return GasEcrecover
|
return params.EcrecoverGas
|
||||||
}, ecrecoverFunc},
|
}, ecrecoverFunc},
|
||||||
|
|
||||||
// SHA256
|
// SHA256
|
||||||
string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||||
n := big.NewInt(int64(l+31) / 32)
|
n := big.NewInt(int64(l+31) / 32)
|
||||||
n.Mul(n, GasSha256Word)
|
n.Mul(n, params.Sha256WordGas)
|
||||||
return n.Add(n, GasSha256Base)
|
return n.Add(n, params.Sha256Gas)
|
||||||
}, sha256Func},
|
}, sha256Func},
|
||||||
|
|
||||||
// RIPEMD160
|
// RIPEMD160
|
||||||
string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||||
n := big.NewInt(int64(l+31) / 32)
|
n := big.NewInt(int64(l+31) / 32)
|
||||||
n.Mul(n, GasRipemdWord)
|
n.Mul(n, params.Ripemd160WordGas)
|
||||||
return n.Add(n, GasRipemdBase)
|
return n.Add(n, params.Ripemd160Gas)
|
||||||
}, ripemd160Func},
|
}, ripemd160Func},
|
||||||
|
|
||||||
string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
|
||||||
n := big.NewInt(int64(l+31) / 32)
|
n := big.NewInt(int64(l+31) / 32)
|
||||||
n.Mul(n, GasIdentityWord)
|
n.Mul(n, params.IdentityWordGas)
|
||||||
|
|
||||||
return n.Add(n, GasIdentityBase)
|
return n.Add(n, params.IdentityGas)
|
||||||
}, memCpy},
|
}, memCpy},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,6 @@ const (
|
|||||||
JitVmTy
|
JitVmTy
|
||||||
MaxVmTy
|
MaxVmTy
|
||||||
|
|
||||||
MaxCallDepth = 1025
|
|
||||||
|
|
||||||
LogTyPretty byte = 0x1
|
LogTyPretty byte = 0x1
|
||||||
LogTyDiff byte = 0x2
|
LogTyDiff byte = 0x2
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ func IsStack(err error) bool {
|
|||||||
type DepthError struct{}
|
type DepthError struct{}
|
||||||
|
|
||||||
func (self DepthError) Error() string {
|
func (self DepthError) Error() string {
|
||||||
return fmt.Sprintf("Max call depth exceeded (%d)", MaxCallDepth)
|
return fmt.Sprintf("Max call depth exceeded (%d)", params.CallCreateDepth)
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsDepthErr(err error) bool {
|
func IsDepthErr(err error) bool {
|
||||||
|
@ -2,6 +2,7 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,45 +14,10 @@ var (
|
|||||||
GasSlowStep = big.NewInt(10)
|
GasSlowStep = big.NewInt(10)
|
||||||
GasExtStep = big.NewInt(20)
|
GasExtStep = big.NewInt(20)
|
||||||
|
|
||||||
GasStorageGet = big.NewInt(50)
|
GasReturn = big.NewInt(0)
|
||||||
GasStorageAdd = big.NewInt(20000)
|
GasStop = big.NewInt(0)
|
||||||
GasStorageMod = big.NewInt(5000)
|
|
||||||
GasLogBase = big.NewInt(375)
|
|
||||||
GasLogTopic = big.NewInt(375)
|
|
||||||
GasLogByte = big.NewInt(8)
|
|
||||||
GasCreate = big.NewInt(32000)
|
|
||||||
GasCreateByte = big.NewInt(200)
|
|
||||||
GasCall = big.NewInt(40)
|
|
||||||
GasCallValueTransfer = big.NewInt(9000)
|
|
||||||
GasStipend = big.NewInt(2300)
|
|
||||||
GasCallNewAccount = big.NewInt(25000)
|
|
||||||
GasReturn = big.NewInt(0)
|
|
||||||
GasStop = big.NewInt(0)
|
|
||||||
GasJumpDest = big.NewInt(1)
|
|
||||||
|
|
||||||
RefundStorage = big.NewInt(15000)
|
GasContractByte = big.NewInt(200)
|
||||||
RefundSuicide = big.NewInt(24000)
|
|
||||||
|
|
||||||
GasMemWord = big.NewInt(3)
|
|
||||||
GasQuadCoeffDenom = big.NewInt(512)
|
|
||||||
GasContractByte = big.NewInt(200)
|
|
||||||
GasTransaction = big.NewInt(21000)
|
|
||||||
GasTxDataNonzeroByte = big.NewInt(68)
|
|
||||||
GasTxDataZeroByte = big.NewInt(4)
|
|
||||||
GasTx = big.NewInt(21000)
|
|
||||||
GasExp = big.NewInt(10)
|
|
||||||
GasExpByte = big.NewInt(10)
|
|
||||||
|
|
||||||
GasSha3Base = big.NewInt(30)
|
|
||||||
GasSha3Word = big.NewInt(6)
|
|
||||||
GasSha256Base = big.NewInt(60)
|
|
||||||
GasSha256Word = big.NewInt(12)
|
|
||||||
GasRipemdBase = big.NewInt(600)
|
|
||||||
GasRipemdWord = big.NewInt(12)
|
|
||||||
GasEcrecover = big.NewInt(3000)
|
|
||||||
GasIdentityBase = big.NewInt(15)
|
|
||||||
GasIdentityWord = big.NewInt(3)
|
|
||||||
GasCopyWord = big.NewInt(3)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
|
func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
|
||||||
@ -71,8 +37,8 @@ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.stackPush && len(stack.data)-r.stackPop+1 > 1024 {
|
if r.stackPush && len(stack.data)-r.stackPop+1 > int(params.StackLimit.Int64()) {
|
||||||
return fmt.Errorf("stack limit reached (%d)", maxStack)
|
return fmt.Errorf("stack limit reached (%d)", params.StackLimit.Int64())
|
||||||
}
|
}
|
||||||
|
|
||||||
gas.Add(gas, r.gas)
|
gas.Add(gas, r.gas)
|
||||||
@ -142,16 +108,16 @@ var _baseCheck = map[OpCode]req{
|
|||||||
MSIZE: {0, GasQuickStep, true},
|
MSIZE: {0, GasQuickStep, true},
|
||||||
GAS: {0, GasQuickStep, true},
|
GAS: {0, GasQuickStep, true},
|
||||||
BLOCKHASH: {1, GasExtStep, true},
|
BLOCKHASH: {1, GasExtStep, true},
|
||||||
BALANCE: {0, GasExtStep, true},
|
BALANCE: {1, GasExtStep, true},
|
||||||
EXTCODESIZE: {1, GasExtStep, true},
|
EXTCODESIZE: {1, GasExtStep, true},
|
||||||
EXTCODECOPY: {4, GasExtStep, false},
|
EXTCODECOPY: {4, GasExtStep, false},
|
||||||
SLOAD: {1, GasStorageGet, true},
|
SLOAD: {1, params.SloadGas, true},
|
||||||
SSTORE: {2, Zero, false},
|
SSTORE: {2, Zero, false},
|
||||||
SHA3: {1, GasSha3Base, true},
|
SHA3: {2, params.Sha3Gas, true},
|
||||||
CREATE: {3, GasCreate, true},
|
CREATE: {3, params.CreateGas, true},
|
||||||
CALL: {7, GasCall, true},
|
CALL: {7, params.CallGas, true},
|
||||||
CALLCODE: {7, GasCall, true},
|
CALLCODE: {7, params.CallGas, true},
|
||||||
JUMPDEST: {0, GasJumpDest, false},
|
JUMPDEST: {0, params.JumpdestGas, false},
|
||||||
SUICIDE: {1, Zero, false},
|
SUICIDE: {1, Zero, false},
|
||||||
RETURN: {2, Zero, false},
|
RETURN: {2, Zero, false},
|
||||||
PUSH1: {0, GasFastestStep, true},
|
PUSH1: {0, GasFastestStep, true},
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxStack = 1024
|
|
||||||
|
|
||||||
func newStack() *stack {
|
func newStack() *stack {
|
||||||
return &stack{}
|
return &stack{}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Vm struct {
|
type Vm struct {
|
||||||
@ -640,7 +641,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
|
|||||||
} else {
|
} else {
|
||||||
// gas < len(ret) * CreateDataGas == NO_CODE
|
// gas < len(ret) * CreateDataGas == NO_CODE
|
||||||
dataGas := big.NewInt(int64(len(ret)))
|
dataGas := big.NewInt(int64(len(ret)))
|
||||||
dataGas.Mul(dataGas, GasCreateByte)
|
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||||
if context.UseGas(dataGas) {
|
if context.UseGas(dataGas) {
|
||||||
ref.SetCode(ret)
|
ref.SetCode(ret)
|
||||||
}
|
}
|
||||||
@ -667,7 +668,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
|
|||||||
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
args := mem.Get(inOffset.Int64(), inSize.Int64())
|
||||||
|
|
||||||
if len(value.Bytes()) > 0 {
|
if len(value.Bytes()) > 0 {
|
||||||
gas.Add(gas, GasStipend)
|
gas.Add(gas, params.CallStipend)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -759,13 +760,13 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
|
|||||||
|
|
||||||
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||||
|
|
||||||
gas.Add(gas, GasLogBase)
|
gas.Add(gas, params.LogGas)
|
||||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), GasLogTopic))
|
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas))
|
||||||
gas.Add(gas, new(big.Int).Mul(mSize, GasLogByte))
|
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
|
||||||
|
|
||||||
newMemSize = calcMemSize(mStart, mSize)
|
newMemSize = calcMemSize(mStart, mSize)
|
||||||
case EXP:
|
case EXP:
|
||||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), GasExpByte))
|
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
|
||||||
case SSTORE:
|
case SSTORE:
|
||||||
err := stack.require(2)
|
err := stack.require(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -777,19 +778,19 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
|
|||||||
val := statedb.GetState(context.Address(), common.BigToHash(x))
|
val := statedb.GetState(context.Address(), common.BigToHash(x))
|
||||||
if len(val) == 0 && len(y.Bytes()) > 0 {
|
if len(val) == 0 && len(y.Bytes()) > 0 {
|
||||||
// 0 => non 0
|
// 0 => non 0
|
||||||
g = GasStorageAdd
|
g = params.SstoreSetGas
|
||||||
} else if len(val) > 0 && len(y.Bytes()) == 0 {
|
} else if len(val) > 0 && len(y.Bytes()) == 0 {
|
||||||
statedb.Refund(self.env.Origin(), RefundStorage)
|
statedb.Refund(self.env.Origin(), params.SstoreRefundGas)
|
||||||
|
|
||||||
g = GasStorageMod
|
g = params.SstoreClearGas
|
||||||
} else {
|
} else {
|
||||||
// non 0 => non 0 (or 0 => 0)
|
// non 0 => non 0 (or 0 => 0)
|
||||||
g = GasStorageMod
|
g = params.SstoreClearGas
|
||||||
}
|
}
|
||||||
gas.Set(g)
|
gas.Set(g)
|
||||||
case SUICIDE:
|
case SUICIDE:
|
||||||
if !statedb.IsDeleted(context.Address()) {
|
if !statedb.IsDeleted(context.Address()) {
|
||||||
statedb.Refund(self.env.Origin(), RefundSuicide)
|
statedb.Refund(self.env.Origin(), params.SuicideRefundGas)
|
||||||
}
|
}
|
||||||
case MLOAD:
|
case MLOAD:
|
||||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||||
@ -803,22 +804,22 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
|
|||||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||||
|
|
||||||
words := toWordSize(stack.data[stack.len()-2])
|
words := toWordSize(stack.data[stack.len()-2])
|
||||||
gas.Add(gas, words.Mul(words, GasSha3Word))
|
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||||
case CALLDATACOPY:
|
case CALLDATACOPY:
|
||||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||||
|
|
||||||
words := toWordSize(stack.data[stack.len()-3])
|
words := toWordSize(stack.data[stack.len()-3])
|
||||||
gas.Add(gas, words.Mul(words, GasCopyWord))
|
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||||
case CODECOPY:
|
case CODECOPY:
|
||||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||||
|
|
||||||
words := toWordSize(stack.data[stack.len()-3])
|
words := toWordSize(stack.data[stack.len()-3])
|
||||||
gas.Add(gas, words.Mul(words, GasCopyWord))
|
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||||
case EXTCODECOPY:
|
case EXTCODECOPY:
|
||||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
||||||
|
|
||||||
words := toWordSize(stack.data[stack.len()-4])
|
words := toWordSize(stack.data[stack.len()-4])
|
||||||
gas.Add(gas, words.Mul(words, GasCopyWord))
|
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||||
|
|
||||||
case CREATE:
|
case CREATE:
|
||||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
||||||
@ -827,12 +828,12 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
|
|||||||
|
|
||||||
if op == CALL {
|
if op == CALL {
|
||||||
if self.env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
if self.env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
|
||||||
gas.Add(gas, GasCallNewAccount)
|
gas.Add(gas, params.CallNewAccountGas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(stack.data[stack.len()-3].Bytes()) > 0 {
|
if len(stack.data[stack.len()-3].Bytes()) > 0 {
|
||||||
gas.Add(gas, GasCallValueTransfer)
|
gas.Add(gas, params.CallValueTransferGas)
|
||||||
}
|
}
|
||||||
|
|
||||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||||
@ -848,13 +849,13 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
|
|||||||
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
||||||
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
|
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
|
||||||
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
|
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
|
||||||
linCoef := new(big.Int).Mul(oldSize, GasMemWord)
|
linCoef := new(big.Int).Mul(oldSize, params.MemoryGas)
|
||||||
quadCoef := new(big.Int).Div(pow, GasQuadCoeffDenom)
|
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
|
||||||
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
||||||
|
|
||||||
pow.Exp(newMemSizeWords, common.Big2, Zero)
|
pow.Exp(newMemSizeWords, common.Big2, Zero)
|
||||||
linCoef = new(big.Int).Mul(newMemSizeWords, GasMemWord)
|
linCoef = new(big.Int).Mul(newMemSizeWords, params.MemoryGas)
|
||||||
quadCoef = new(big.Int).Div(pow, GasQuadCoeffDenom)
|
quadCoef = new(big.Int).Div(pow, params.QuadCoeffDiv)
|
||||||
newTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
newTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
||||||
|
|
||||||
fee := new(big.Int).Sub(newTotalFee, oldTotalFee)
|
fee := new(big.Int).Sub(newTotalFee, oldTotalFee)
|
||||||
|
@ -18,8 +18,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"math/big"
|
"math/big"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -330,7 +330,7 @@ func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initData
|
|||||||
ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
|
ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
|
||||||
if suberr == nil {
|
if suberr == nil {
|
||||||
dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
|
dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
|
||||||
dataGas.Mul(dataGas, GasCreateByte)
|
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||||
gas.Sub(gas, dataGas)
|
gas.Sub(gas, dataGas)
|
||||||
*result = hash2llvm(ref.Address())
|
*result = hash2llvm(ref.Address())
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,8 @@ var (
|
|||||||
defaultBootNodes = []*discover.Node{
|
defaultBootNodes = []*discover.Node{
|
||||||
// ETH/DEV cmd/bootnode
|
// ETH/DEV cmd/bootnode
|
||||||
discover.MustParseNode("enode://09fbeec0d047e9a37e63f60f8618aa9df0e49271f3fadb2c070dc09e2099b95827b63a8b837c6fd01d0802d457dd83e3bd48bd3e6509f8209ed90dabbc30e3d3@52.16.188.185:30303"),
|
discover.MustParseNode("enode://09fbeec0d047e9a37e63f60f8618aa9df0e49271f3fadb2c070dc09e2099b95827b63a8b837c6fd01d0802d457dd83e3bd48bd3e6509f8209ed90dabbc30e3d3@52.16.188.185:30303"),
|
||||||
// ETH/DEV cpp-ethereum (poc-8.ethdev.com)
|
// ETH/DEV cpp-ethereum (poc-9.ethdev.com)
|
||||||
discover.MustParseNode("enode://4a44599974518ea5b0f14c31c4463692ac0329cb84851f3435e6d1b18ee4eae4aa495f846a0fa1219bd58035671881d44423876e57db2abd57254d0197da0ebe@5.1.83.226:30303"),
|
discover.MustParseNode("enode://487611428e6c99a11a9795a6abe7b529e81315ca6aad66e2a2fc76e3adf263faba0d35466c2f8f68d561dbefa8878d4df5f1f2ddb1fbeab7f42ffb8cd328bd4a@5.1.83.226:30303"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
{
|
|
||||||
"genesisGasLimit": { "v": 1000000, "d": "Gas limit of the Genesis block." },
|
|
||||||
"minGasLimit": { "v": 125000, "d": "Minimum the gas limit may ever be." },
|
|
||||||
"gasLimitBoundDivisor": { "v": 1024, "d": "The bound divisor of the gas limit, used in update calculations." },
|
|
||||||
"genesisDifficulty": { "v": 131072, "d": "Difficulty of the Genesis block." },
|
|
||||||
"minimumDifficulty": { "v": 131072, "d": "The minimum that the difficulty may ever be." },
|
|
||||||
"difficultyBoundDivisor": { "v": 2048, "d": "The bound divisor of the difficulty, used in the update calculations." },
|
|
||||||
"durationLimit": { "v": 8, "d": "The decision boundary on the blocktime duration used to determine whether difficulty should go up or not." },
|
|
||||||
"maximumExtraDataSize": { "v": 1024, "d": "Maximum size extra data may be after Genesis." },
|
|
||||||
"epochDuration": { "v": 30000, "d": "Duration between proof-of-work epochs." },
|
|
||||||
"stackLimit": { "v": 1024, "d": "Maximum size of VM stack allowed." },
|
|
||||||
|
|
||||||
"tierStepGas": { "v": [ 0, 2, 3, 5, 8, 10, 20 ], "d": "Once per operation, for a selection of them." },
|
|
||||||
"expGas": { "v": 10, "d": "Once per EXP instuction." },
|
|
||||||
"expByteGas": { "v": 10, "d": "Times ceil(log256(exponent)) for the EXP instruction." },
|
|
||||||
|
|
||||||
"sha3Gas": { "v": 30, "d": "Once per SHA3 operation." },
|
|
||||||
"sha3WordGas": { "v": 6, "d": "Once per word of the SHA3 operation's data." },
|
|
||||||
|
|
||||||
"sloadGas": { "v": 50, "d": "Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added." },
|
|
||||||
"sstoreSetGas": { "v": 20000, "d": "Once per SLOAD operation." },
|
|
||||||
"sstoreResetGas": { "v": 5000, "d": "Once per SSTORE operation if the zeroness changes from zero." },
|
|
||||||
"sstoreClearGas": { "v": 5000, "d": "Once per SSTORE operation if the zeroness doesn't change." },
|
|
||||||
"sstoreRefundGas": { "v": 15000, "d": "Once per SSTORE operation if the zeroness changes to zero." },
|
|
||||||
"jumpdestGas": { "v": 1, "d": "Refunded gas, once per SSTORE operation if the zeroness changes to zero." },
|
|
||||||
|
|
||||||
"logGas": { "v": 375, "d": "Per LOG* operation." },
|
|
||||||
"logDataGas": { "v": 8, "d": "Per byte in a LOG* operation's data." },
|
|
||||||
"logTopicGas": { "v": 375, "d": "Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas." },
|
|
||||||
|
|
||||||
"createGas": { "v": 32000, "d": "Once per CREATE operation & contract-creation transaction." },
|
|
||||||
|
|
||||||
"callGas": { "v": 40, "d": "Once per CALL operation & message call transaction." },
|
|
||||||
"callStipend": { "v": 2300, "d": "Free gas given at beginning of call." },
|
|
||||||
"callValueTransferGas": { "v": 9000, "d": "Paid for CALL when the value transfor is non-zero." },
|
|
||||||
"callNewAccountGas": { "v": 25000, "d": "Paid for CALL when the destination address didn't exist prior." },
|
|
||||||
|
|
||||||
"suicideRefundGas": { "v": 24000, "d": "Refunded following a suicide operation." },
|
|
||||||
"memoryGas": { "v": 3, "d": "Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL." },
|
|
||||||
"quadCoeffDiv": { "v": 512, "d": "Divisor for the quadratic particle of the memory cost equation." },
|
|
||||||
|
|
||||||
"createDataGas": { "v": 200, "d": "" },
|
|
||||||
"txGas": { "v": 21000, "d": "Per transaction. NOTE: Not payable on data of calls between transactions." },
|
|
||||||
"txDataZeroGas": { "v": 4, "d": "Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions." },
|
|
||||||
"txDataNonZeroGas": { "v": 68, "d": "Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions." },
|
|
||||||
|
|
||||||
"copyGas": { "v": 3, "d": "" },
|
|
||||||
|
|
||||||
"ecrecoverGas": { "v": 3000, "d": "" },
|
|
||||||
"sha256Gas": { "v": 60, "d": "" },
|
|
||||||
"sha256WordGas": { "v": 12, "d": "" },
|
|
||||||
"ripemd160Gas": { "v": 600, "d": "" },
|
|
||||||
"ripemd160WordGas": { "v": 120, "d": "" },
|
|
||||||
"identityGas": { "v": 15, "d": "" },
|
|
||||||
"identityWordGas": { "v": 3, "d": ""}
|
|
||||||
}
|
|
@ -35,13 +35,16 @@ func main() {
|
|||||||
m := make(map[string]setting)
|
m := make(map[string]setting)
|
||||||
json.Unmarshal(content, &m)
|
json.Unmarshal(content, &m)
|
||||||
|
|
||||||
filepath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "core", os.Args[2])
|
filepath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "params", os.Args[2])
|
||||||
output, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE, os.ModePerm /*0777*/)
|
output, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE, os.ModePerm /*0777*/)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("error opening file for writing %v\n", err)
|
fatal("error opening file for writing %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
output.WriteString(`package core
|
output.WriteString(`// DO NOT EDIT!!!
|
||||||
|
// AUTOGENERATED FROM generators/defaults.go
|
||||||
|
|
||||||
|
package params
|
||||||
|
|
||||||
import "math/big"
|
import "math/big"
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -23,18 +23,28 @@ function pp(object, indent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
str += " ]";
|
str += " ]";
|
||||||
|
} else if (object instanceof Error) {
|
||||||
|
str += "\033[31m" + "Error";
|
||||||
|
} else if (isBigNumber(object)) {
|
||||||
|
str += "\033[32m'" + object.toString(10) + "'";
|
||||||
} else if(typeof(object) === "object") {
|
} else if(typeof(object) === "object") {
|
||||||
str += "{\n";
|
str += "{\n";
|
||||||
indent += " ";
|
indent += " ";
|
||||||
var last = Object.keys(object).pop()
|
var last = Object.getOwnPropertyNames(object).pop()
|
||||||
for(var k in object) {
|
Object.getOwnPropertyNames(object).forEach(function (k) {
|
||||||
str += indent + k + ": " + pp(object[k], indent);
|
str += indent + k + ": ";
|
||||||
|
try {
|
||||||
|
str += pp(object[k], indent);
|
||||||
|
} catch (e) {
|
||||||
|
str += pp(e, indent);
|
||||||
|
}
|
||||||
|
|
||||||
if(k !== last) {
|
if(k !== last) {
|
||||||
str += ",";
|
str += ",";
|
||||||
}
|
}
|
||||||
str += "\n";
|
|
||||||
}
|
str += "\n";
|
||||||
|
});
|
||||||
str += indent.substr(2, indent.length) + "}";
|
str += indent.substr(2, indent.length) + "}";
|
||||||
} else if(typeof(object) === "string") {
|
} else if(typeof(object) === "string") {
|
||||||
str += "\033[32m'" + object + "'";
|
str += "\033[32m'" + object + "'";
|
||||||
@ -43,7 +53,7 @@ function pp(object, indent) {
|
|||||||
} else if(typeof(object) === "number") {
|
} else if(typeof(object) === "number") {
|
||||||
str += "\033[31m" + object;
|
str += "\033[31m" + object;
|
||||||
} else if(typeof(object) === "function") {
|
} else if(typeof(object) === "function") {
|
||||||
str += "\033[35m[Function]";
|
str += "\033[35m[Function]";
|
||||||
} else {
|
} else {
|
||||||
str += object;
|
str += object;
|
||||||
}
|
}
|
||||||
@ -53,6 +63,11 @@ function pp(object, indent) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isBigNumber = function (object) {
|
||||||
|
return typeof BigNumber !== 'undefined' && object instanceof BigNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
function prettyPrint(/* */) {
|
function prettyPrint(/* */) {
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
var ret = "";
|
var ret = "";
|
||||||
|
@ -60,7 +60,7 @@ out:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(self.quitCurrentOp)
|
//close(self.quitCurrentOp)
|
||||||
done:
|
done:
|
||||||
// Empty channel
|
// Empty channel
|
||||||
for {
|
for {
|
||||||
|
@ -171,6 +171,8 @@ func (self *worker) wait() {
|
|||||||
}
|
}
|
||||||
self.mux.Post(core.NewMinedBlockEvent{block})
|
self.mux.Post(core.NewMinedBlockEvent{block})
|
||||||
|
|
||||||
|
minerlogger.Infof("🔨 Mined block #%v", block.Number())
|
||||||
|
|
||||||
jsonlogger.LogJson(&logger.EthMinerNewBlock{
|
jsonlogger.LogJson(&logger.EthMinerNewBlock{
|
||||||
BlockHash: block.Hash().Hex(),
|
BlockHash: block.Hash().Hex(),
|
||||||
BlockNumber: block.Number(),
|
BlockNumber: block.Number(),
|
||||||
@ -270,9 +272,9 @@ gasLimit:
|
|||||||
|
|
||||||
self.current.block.SetUncles(uncles)
|
self.current.block.SetUncles(uncles)
|
||||||
|
|
||||||
self.current.state.AddBalance(self.coinbase, core.BlockReward)
|
core.AccumulateRewards(self.current.state, self.current.block)
|
||||||
|
|
||||||
self.current.state.Update(common.Big0)
|
self.current.state.Update()
|
||||||
self.push()
|
self.push()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,9 +299,6 @@ func (self *worker) commitUncle(uncle *types.Header) error {
|
|||||||
return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", uncle.Hash()))
|
return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", uncle.Hash()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current.state.AddBalance(uncle.Coinbase, uncleReward)
|
|
||||||
self.current.state.AddBalance(self.coinbase, inclusionReward)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -30,7 +32,8 @@ type Node struct {
|
|||||||
DiscPort int // UDP listening port for discovery protocol
|
DiscPort int // UDP listening port for discovery protocol
|
||||||
TCPPort int // TCP listening port for RLPx
|
TCPPort int // TCP listening port for RLPx
|
||||||
|
|
||||||
active time.Time
|
// this must be set/read using atomic load and store.
|
||||||
|
activeStamp int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNode(id NodeID, addr *net.UDPAddr) *Node {
|
func newNode(id NodeID, addr *net.UDPAddr) *Node {
|
||||||
@ -39,7 +42,6 @@ func newNode(id NodeID, addr *net.UDPAddr) *Node {
|
|||||||
IP: addr.IP,
|
IP: addr.IP,
|
||||||
DiscPort: addr.Port,
|
DiscPort: addr.Port,
|
||||||
TCPPort: addr.Port,
|
TCPPort: addr.Port,
|
||||||
active: time.Now(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +50,20 @@ func (n *Node) isValid() bool {
|
|||||||
return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.TCPPort != 0 && n.DiscPort != 0
|
return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.TCPPort != 0 && n.DiscPort != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Node) bumpActive() {
|
||||||
|
stamp := time.Now().Unix()
|
||||||
|
atomic.StoreInt64(&n.activeStamp, stamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) active() time.Time {
|
||||||
|
stamp := atomic.LoadInt64(&n.activeStamp)
|
||||||
|
return time.Unix(stamp, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) addr() *net.UDPAddr {
|
||||||
|
return &net.UDPAddr{IP: n.IP, Port: n.DiscPort}
|
||||||
|
}
|
||||||
|
|
||||||
// The string representation of a Node is a URL.
|
// The string representation of a Node is a URL.
|
||||||
// Please see ParseNode for a description of the format.
|
// Please see ParseNode for a description of the format.
|
||||||
func (n *Node) String() string {
|
func (n *Node) String() string {
|
||||||
@ -304,3 +320,26 @@ func randomID(a NodeID, n int) (b NodeID) {
|
|||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nodeDB stores all nodes we know about.
|
||||||
|
type nodeDB struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
byID map[NodeID]*Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *nodeDB) get(id NodeID) *Node {
|
||||||
|
db.mu.RLock()
|
||||||
|
defer db.mu.RUnlock()
|
||||||
|
return db.byID[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *nodeDB) add(id NodeID, addr *net.UDPAddr, tcpPort uint16) *Node {
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
if db.byID == nil {
|
||||||
|
db.byID = make(map[NodeID]*Node)
|
||||||
|
}
|
||||||
|
n := &Node{ID: id, IP: addr.IP, DiscPort: addr.Port, TCPPort: int(tcpPort)}
|
||||||
|
db.byID[n.ID] = n
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
@ -14,9 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
alpha = 3 // Kademlia concurrency factor
|
alpha = 3 // Kademlia concurrency factor
|
||||||
bucketSize = 16 // Kademlia bucket size
|
bucketSize = 16 // Kademlia bucket size
|
||||||
nBuckets = nodeIDBits + 1 // Number of buckets
|
nBuckets = nodeIDBits + 1 // Number of buckets
|
||||||
|
maxBondingPingPongs = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
type Table struct {
|
type Table struct {
|
||||||
@ -24,27 +25,50 @@ type Table struct {
|
|||||||
buckets [nBuckets]*bucket // index of known nodes by distance
|
buckets [nBuckets]*bucket // index of known nodes by distance
|
||||||
nursery []*Node // bootstrap nodes
|
nursery []*Node // bootstrap nodes
|
||||||
|
|
||||||
|
bondmu sync.Mutex
|
||||||
|
bonding map[NodeID]*bondproc
|
||||||
|
bondslots chan struct{} // limits total number of active bonding processes
|
||||||
|
|
||||||
net transport
|
net transport
|
||||||
self *Node // metadata of the local node
|
self *Node // metadata of the local node
|
||||||
|
db *nodeDB
|
||||||
|
}
|
||||||
|
|
||||||
|
type bondproc struct {
|
||||||
|
err error
|
||||||
|
n *Node
|
||||||
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// transport is implemented by the UDP transport.
|
// transport is implemented by the UDP transport.
|
||||||
// it is an interface so we can test without opening lots of UDP
|
// it is an interface so we can test without opening lots of UDP
|
||||||
// sockets and without generating a private key.
|
// sockets and without generating a private key.
|
||||||
type transport interface {
|
type transport interface {
|
||||||
ping(*Node) error
|
ping(NodeID, *net.UDPAddr) error
|
||||||
findnode(e *Node, target NodeID) ([]*Node, error)
|
waitping(NodeID) error
|
||||||
|
findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, error)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// bucket contains nodes, ordered by their last activity.
|
// bucket contains nodes, ordered by their last activity.
|
||||||
|
// the entry that was most recently active is the last element
|
||||||
|
// in entries.
|
||||||
type bucket struct {
|
type bucket struct {
|
||||||
lastLookup time.Time
|
lastLookup time.Time
|
||||||
entries []*Node
|
entries []*Node
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr) *Table {
|
func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr) *Table {
|
||||||
tab := &Table{net: t, self: newNode(ourID, ourAddr)}
|
tab := &Table{
|
||||||
|
net: t,
|
||||||
|
db: new(nodeDB),
|
||||||
|
self: newNode(ourID, ourAddr),
|
||||||
|
bonding: make(map[NodeID]*bondproc),
|
||||||
|
bondslots: make(chan struct{}, maxBondingPingPongs),
|
||||||
|
}
|
||||||
|
for i := 0; i < cap(tab.bondslots); i++ {
|
||||||
|
tab.bondslots <- struct{}{}
|
||||||
|
}
|
||||||
for i := range tab.buckets {
|
for i := range tab.buckets {
|
||||||
tab.buckets[i] = new(bucket)
|
tab.buckets[i] = new(bucket)
|
||||||
}
|
}
|
||||||
@ -107,8 +131,8 @@ func (tab *Table) Lookup(target NodeID) []*Node {
|
|||||||
asked[n.ID] = true
|
asked[n.ID] = true
|
||||||
pendingQueries++
|
pendingQueries++
|
||||||
go func() {
|
go func() {
|
||||||
result, _ := tab.net.findnode(n, target)
|
r, _ := tab.net.findnode(n.ID, n.addr(), target)
|
||||||
reply <- result
|
reply <- tab.bondall(r)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,13 +140,11 @@ func (tab *Table) Lookup(target NodeID) []*Node {
|
|||||||
// we have asked all closest nodes, stop the search
|
// we have asked all closest nodes, stop the search
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for the next reply
|
// wait for the next reply
|
||||||
for _, n := range <-reply {
|
for _, n := range <-reply {
|
||||||
cn := n
|
if n != nil && !seen[n.ID] {
|
||||||
if !seen[n.ID] {
|
|
||||||
seen[n.ID] = true
|
seen[n.ID] = true
|
||||||
result.push(cn, bucketSize)
|
result.push(n, bucketSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pendingQueries--
|
pendingQueries--
|
||||||
@ -145,8 +167,9 @@ func (tab *Table) refresh() {
|
|||||||
result := tab.Lookup(randomID(tab.self.ID, ld))
|
result := tab.Lookup(randomID(tab.self.ID, ld))
|
||||||
if len(result) == 0 {
|
if len(result) == 0 {
|
||||||
// bootstrap the table with a self lookup
|
// bootstrap the table with a self lookup
|
||||||
|
all := tab.bondall(tab.nursery)
|
||||||
tab.mutex.Lock()
|
tab.mutex.Lock()
|
||||||
tab.add(tab.nursery)
|
tab.add(all)
|
||||||
tab.mutex.Unlock()
|
tab.mutex.Unlock()
|
||||||
tab.Lookup(tab.self.ID)
|
tab.Lookup(tab.self.ID)
|
||||||
// TODO: the Kademlia paper says that we're supposed to perform
|
// TODO: the Kademlia paper says that we're supposed to perform
|
||||||
@ -176,45 +199,105 @@ func (tab *Table) len() (n int) {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// bumpOrAdd updates the activity timestamp for the given node and
|
// bondall bonds with all given nodes concurrently and returns
|
||||||
// attempts to insert the node into a bucket. The returned Node might
|
// those nodes for which bonding has probably succeeded.
|
||||||
// not be part of the table. The caller must hold tab.mutex.
|
func (tab *Table) bondall(nodes []*Node) (result []*Node) {
|
||||||
func (tab *Table) bumpOrAdd(node NodeID, from *net.UDPAddr) (n *Node) {
|
rc := make(chan *Node, len(nodes))
|
||||||
b := tab.buckets[logdist(tab.self.ID, node)]
|
for i := range nodes {
|
||||||
if n = b.bump(node); n == nil {
|
go func(n *Node) {
|
||||||
n = newNode(node, from)
|
nn, _ := tab.bond(false, n.ID, n.addr(), uint16(n.TCPPort))
|
||||||
if len(b.entries) == bucketSize {
|
rc <- nn
|
||||||
tab.pingReplace(n, b)
|
}(nodes[i])
|
||||||
} else {
|
}
|
||||||
b.entries = append(b.entries, n)
|
for _ = range nodes {
|
||||||
|
if n := <-rc; n != nil {
|
||||||
|
result = append(result, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return n
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tab *Table) pingReplace(n *Node, b *bucket) {
|
// bond ensures the local node has a bond with the given remote node.
|
||||||
old := b.entries[bucketSize-1]
|
// It also attempts to insert the node into the table if bonding succeeds.
|
||||||
go func() {
|
// The caller must not hold tab.mutex.
|
||||||
if err := tab.net.ping(old); err == nil {
|
//
|
||||||
// it responded, we don't need to replace it.
|
// A bond is must be established before sending findnode requests.
|
||||||
|
// Both sides must have completed a ping/pong exchange for a bond to
|
||||||
|
// exist. The total number of active bonding processes is limited in
|
||||||
|
// order to restrain network use.
|
||||||
|
//
|
||||||
|
// bond is meant to operate idempotently in that bonding with a remote
|
||||||
|
// node which still remembers a previously established bond will work.
|
||||||
|
// The remote node will simply not send a ping back, causing waitping
|
||||||
|
// to time out.
|
||||||
|
//
|
||||||
|
// If pinged is true, the remote node has just pinged us and one half
|
||||||
|
// of the process can be skipped.
|
||||||
|
func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) {
|
||||||
|
var n *Node
|
||||||
|
if n = tab.db.get(id); n == nil {
|
||||||
|
tab.bondmu.Lock()
|
||||||
|
w := tab.bonding[id]
|
||||||
|
if w != nil {
|
||||||
|
// Wait for an existing bonding process to complete.
|
||||||
|
tab.bondmu.Unlock()
|
||||||
|
<-w.done
|
||||||
|
} else {
|
||||||
|
// Register a new bonding process.
|
||||||
|
w = &bondproc{done: make(chan struct{})}
|
||||||
|
tab.bonding[id] = w
|
||||||
|
tab.bondmu.Unlock()
|
||||||
|
// Do the ping/pong. The result goes into w.
|
||||||
|
tab.pingpong(w, pinged, id, addr, tcpPort)
|
||||||
|
// Unregister the process after it's done.
|
||||||
|
tab.bondmu.Lock()
|
||||||
|
delete(tab.bonding, id)
|
||||||
|
tab.bondmu.Unlock()
|
||||||
|
}
|
||||||
|
n = w.n
|
||||||
|
if w.err != nil {
|
||||||
|
return nil, w.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tab.mutex.Lock()
|
||||||
|
defer tab.mutex.Unlock()
|
||||||
|
if b := tab.buckets[logdist(tab.self.ID, n.ID)]; !b.bump(n) {
|
||||||
|
tab.pingreplace(n, b)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) {
|
||||||
|
<-tab.bondslots
|
||||||
|
defer func() { tab.bondslots <- struct{}{} }()
|
||||||
|
if w.err = tab.net.ping(id, addr); w.err != nil {
|
||||||
|
close(w.done)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !pinged {
|
||||||
|
// Give the remote node a chance to ping us before we start
|
||||||
|
// sending findnode requests. If they still remember us,
|
||||||
|
// waitping will simply time out.
|
||||||
|
tab.net.waitping(id)
|
||||||
|
}
|
||||||
|
w.n = tab.db.add(id, addr, tcpPort)
|
||||||
|
close(w.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tab *Table) pingreplace(new *Node, b *bucket) {
|
||||||
|
if len(b.entries) == bucketSize {
|
||||||
|
oldest := b.entries[bucketSize-1]
|
||||||
|
if err := tab.net.ping(oldest.ID, oldest.addr()); err == nil {
|
||||||
|
// The node responded, we don't need to replace it.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// it didn't respond, replace the node if it is still the oldest node.
|
} else {
|
||||||
tab.mutex.Lock()
|
// Add a slot at the end so the last entry doesn't
|
||||||
if len(b.entries) > 0 && b.entries[len(b.entries)-1] == old {
|
// fall off when adding the new node.
|
||||||
// slide down other entries and put the new one in front.
|
b.entries = append(b.entries, nil)
|
||||||
// TODO: insert in correct position to keep the order
|
}
|
||||||
copy(b.entries[1:], b.entries)
|
copy(b.entries[1:], b.entries)
|
||||||
b.entries[0] = n
|
b.entries[0] = new
|
||||||
}
|
|
||||||
tab.mutex.Unlock()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// bump updates the activity timestamp for the given node.
|
|
||||||
// The caller must hold tab.mutex.
|
|
||||||
func (tab *Table) bump(node NodeID) {
|
|
||||||
tab.buckets[logdist(tab.self.ID, node)].bump(node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add puts the entries into the table if their corresponding
|
// add puts the entries into the table if their corresponding
|
||||||
@ -240,17 +323,17 @@ outer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bucket) bump(id NodeID) *Node {
|
func (b *bucket) bump(n *Node) bool {
|
||||||
for i, n := range b.entries {
|
for i := range b.entries {
|
||||||
if n.ID == id {
|
if b.entries[i].ID == n.ID {
|
||||||
n.active = time.Now()
|
n.bumpActive()
|
||||||
// move it to the front
|
// move it to the front
|
||||||
copy(b.entries[1:], b.entries[:i+1])
|
copy(b.entries[1:], b.entries[:i])
|
||||||
b.entries[0] = n
|
b.entries[0] = n
|
||||||
return n
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodesByDistance is a list of nodes, ordered by
|
// nodesByDistance is a list of nodes, ordered by
|
||||||
|
@ -2,78 +2,109 @@ package discover
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"testing/quick"
|
"testing/quick"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTable_bumpOrAddBucketAssign(t *testing.T) {
|
func TestTable_pingReplace(t *testing.T) {
|
||||||
tab := newTable(nil, NodeID{}, &net.UDPAddr{})
|
doit := func(newNodeIsResponding, lastInBucketIsResponding bool) {
|
||||||
for i := 1; i < len(tab.buckets); i++ {
|
transport := newPingRecorder()
|
||||||
tab.bumpOrAdd(randomID(tab.self.ID, i), &net.UDPAddr{})
|
tab := newTable(transport, NodeID{}, &net.UDPAddr{})
|
||||||
}
|
last := fillBucket(tab, 200)
|
||||||
for i, b := range tab.buckets {
|
pingSender := randomID(tab.self.ID, 200)
|
||||||
if i > 0 && len(b.entries) != 1 {
|
|
||||||
t.Errorf("bucket %d has %d entries, want 1", i, len(b.entries))
|
// this gotPing should replace the last node
|
||||||
|
// if the last node is not responding.
|
||||||
|
transport.responding[last.ID] = lastInBucketIsResponding
|
||||||
|
transport.responding[pingSender] = newNodeIsResponding
|
||||||
|
tab.bond(true, pingSender, &net.UDPAddr{}, 0)
|
||||||
|
|
||||||
|
// first ping goes to sender (bonding pingback)
|
||||||
|
if !transport.pinged[pingSender] {
|
||||||
|
t.Error("table did not ping back sender")
|
||||||
|
}
|
||||||
|
if newNodeIsResponding {
|
||||||
|
// second ping goes to oldest node in bucket
|
||||||
|
// to see whether it is still alive.
|
||||||
|
if !transport.pinged[last.ID] {
|
||||||
|
t.Error("table did not ping last node in bucket")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tab.mutex.Lock()
|
||||||
|
defer tab.mutex.Unlock()
|
||||||
|
if l := len(tab.buckets[200].entries); l != bucketSize {
|
||||||
|
t.Errorf("wrong bucket size after gotPing: got %d, want %d", bucketSize, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastInBucketIsResponding || !newNodeIsResponding {
|
||||||
|
if !contains(tab.buckets[200].entries, last.ID) {
|
||||||
|
t.Error("last entry was removed")
|
||||||
|
}
|
||||||
|
if contains(tab.buckets[200].entries, pingSender) {
|
||||||
|
t.Error("new entry was added")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if contains(tab.buckets[200].entries, last.ID) {
|
||||||
|
t.Error("last entry was not removed")
|
||||||
|
}
|
||||||
|
if !contains(tab.buckets[200].entries, pingSender) {
|
||||||
|
t.Error("new entry was not added")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doit(true, true)
|
||||||
|
doit(false, true)
|
||||||
|
doit(false, true)
|
||||||
|
doit(false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTable_bumpOrAddPingReplace(t *testing.T) {
|
func TestBucket_bumpNoDuplicates(t *testing.T) {
|
||||||
pingC := make(pingC)
|
t.Parallel()
|
||||||
tab := newTable(pingC, NodeID{}, &net.UDPAddr{})
|
cfg := &quick.Config{
|
||||||
last := fillBucket(tab, 200)
|
MaxCount: 1000,
|
||||||
|
Rand: quickrand,
|
||||||
// this bumpOrAdd should not replace the last node
|
Values: func(args []reflect.Value, rand *rand.Rand) {
|
||||||
// because the node replies to ping.
|
// generate a random list of nodes. this will be the content of the bucket.
|
||||||
new := tab.bumpOrAdd(randomID(tab.self.ID, 200), &net.UDPAddr{})
|
n := rand.Intn(bucketSize-1) + 1
|
||||||
|
nodes := make([]*Node, n)
|
||||||
pinged := <-pingC
|
for i := range nodes {
|
||||||
if pinged != last.ID {
|
nodes[i] = &Node{ID: randomID(NodeID{}, 200)}
|
||||||
t.Fatalf("pinged wrong node: %v\nwant %v", pinged, last.ID)
|
}
|
||||||
|
args[0] = reflect.ValueOf(nodes)
|
||||||
|
// generate random bump positions.
|
||||||
|
bumps := make([]int, rand.Intn(100))
|
||||||
|
for i := range bumps {
|
||||||
|
bumps[i] = rand.Intn(len(nodes))
|
||||||
|
}
|
||||||
|
args[1] = reflect.ValueOf(bumps)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tab.mutex.Lock()
|
prop := func(nodes []*Node, bumps []int) (ok bool) {
|
||||||
defer tab.mutex.Unlock()
|
b := &bucket{entries: make([]*Node, len(nodes))}
|
||||||
if l := len(tab.buckets[200].entries); l != bucketSize {
|
copy(b.entries, nodes)
|
||||||
t.Errorf("wrong bucket size after bumpOrAdd: got %d, want %d", bucketSize, l)
|
for i, pos := range bumps {
|
||||||
|
b.bump(b.entries[pos])
|
||||||
|
if hasDuplicates(b.entries) {
|
||||||
|
t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps))
|
||||||
|
for _, n := range b.entries {
|
||||||
|
t.Logf(" %p", n)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
if !contains(tab.buckets[200].entries, last.ID) {
|
if err := quick.Check(prop, cfg); err != nil {
|
||||||
t.Error("last entry was removed")
|
t.Error(err)
|
||||||
}
|
|
||||||
if contains(tab.buckets[200].entries, new.ID) {
|
|
||||||
t.Error("new entry was added")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTable_bumpOrAddPingTimeout(t *testing.T) {
|
|
||||||
tab := newTable(pingC(nil), NodeID{}, &net.UDPAddr{})
|
|
||||||
last := fillBucket(tab, 200)
|
|
||||||
|
|
||||||
// this bumpOrAdd should replace the last node
|
|
||||||
// because the node does not reply to ping.
|
|
||||||
new := tab.bumpOrAdd(randomID(tab.self.ID, 200), &net.UDPAddr{})
|
|
||||||
|
|
||||||
// wait for async bucket update. damn. this needs to go away.
|
|
||||||
time.Sleep(2 * time.Millisecond)
|
|
||||||
|
|
||||||
tab.mutex.Lock()
|
|
||||||
defer tab.mutex.Unlock()
|
|
||||||
if l := len(tab.buckets[200].entries); l != bucketSize {
|
|
||||||
t.Errorf("wrong bucket size after bumpOrAdd: got %d, want %d", bucketSize, l)
|
|
||||||
}
|
|
||||||
if contains(tab.buckets[200].entries, last.ID) {
|
|
||||||
t.Error("last entry was not removed")
|
|
||||||
}
|
|
||||||
if !contains(tab.buckets[200].entries, new.ID) {
|
|
||||||
t.Error("new entry was not added")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,44 +116,27 @@ func fillBucket(tab *Table, ld int) (last *Node) {
|
|||||||
return b.entries[bucketSize-1]
|
return b.entries[bucketSize-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
type pingC chan NodeID
|
type pingRecorder struct{ responding, pinged map[NodeID]bool }
|
||||||
|
|
||||||
func (t pingC) findnode(n *Node, target NodeID) ([]*Node, error) {
|
func newPingRecorder() *pingRecorder {
|
||||||
|
return &pingRecorder{make(map[NodeID]bool), make(map[NodeID]bool)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
|
||||||
panic("findnode called on pingRecorder")
|
panic("findnode called on pingRecorder")
|
||||||
}
|
}
|
||||||
func (t pingC) close() {
|
func (t *pingRecorder) close() {
|
||||||
panic("close called on pingRecorder")
|
panic("close called on pingRecorder")
|
||||||
}
|
}
|
||||||
func (t pingC) ping(n *Node) error {
|
func (t *pingRecorder) waitping(from NodeID) error {
|
||||||
if t == nil {
|
return nil // remote always pings
|
||||||
return errTimeout
|
|
||||||
}
|
|
||||||
t <- n.ID
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error {
|
||||||
func TestTable_bump(t *testing.T) {
|
t.pinged[toid] = true
|
||||||
tab := newTable(nil, NodeID{}, &net.UDPAddr{})
|
if t.responding[toid] {
|
||||||
|
return nil
|
||||||
// add an old entry and two recent ones
|
} else {
|
||||||
oldactive := time.Now().Add(-2 * time.Minute)
|
return errTimeout
|
||||||
old := &Node{ID: randomID(tab.self.ID, 200), active: oldactive}
|
|
||||||
others := []*Node{
|
|
||||||
&Node{ID: randomID(tab.self.ID, 200), active: time.Now()},
|
|
||||||
&Node{ID: randomID(tab.self.ID, 200), active: time.Now()},
|
|
||||||
}
|
|
||||||
tab.add(append(others, old))
|
|
||||||
if tab.buckets[200].entries[0] == old {
|
|
||||||
t.Fatal("old entry is at front of bucket")
|
|
||||||
}
|
|
||||||
|
|
||||||
// bumping the old entry should move it to the front
|
|
||||||
tab.bump(old.ID)
|
|
||||||
if old.active == oldactive {
|
|
||||||
t.Error("activity timestamp not updated")
|
|
||||||
}
|
|
||||||
if tab.buckets[200].entries[0] != old {
|
|
||||||
t.Errorf("bumped entry did not move to the front of bucket")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +224,7 @@ func TestTable_Lookup(t *testing.T) {
|
|||||||
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
||||||
}
|
}
|
||||||
// seed table with initial node (otherwise lookup will terminate immediately)
|
// seed table with initial node (otherwise lookup will terminate immediately)
|
||||||
tab.bumpOrAdd(randomID(target, 200), &net.UDPAddr{Port: 200})
|
tab.add([]*Node{newNode(randomID(target, 200), &net.UDPAddr{Port: 200})})
|
||||||
|
|
||||||
results := tab.Lookup(target)
|
results := tab.Lookup(target)
|
||||||
t.Logf("results:")
|
t.Logf("results:")
|
||||||
@ -238,16 +252,16 @@ type findnodeOracle struct {
|
|||||||
target NodeID
|
target NodeID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t findnodeOracle) findnode(n *Node, target NodeID) ([]*Node, error) {
|
func (t findnodeOracle) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
|
||||||
t.t.Logf("findnode query at dist %d", n.DiscPort)
|
t.t.Logf("findnode query at dist %d", toaddr.Port)
|
||||||
// current log distance is encoded in port number
|
// current log distance is encoded in port number
|
||||||
var result []*Node
|
var result []*Node
|
||||||
switch n.DiscPort {
|
switch toaddr.Port {
|
||||||
case 0:
|
case 0:
|
||||||
panic("query to node at distance 0")
|
panic("query to node at distance 0")
|
||||||
default:
|
default:
|
||||||
// TODO: add more randomness to distances
|
// TODO: add more randomness to distances
|
||||||
next := n.DiscPort - 1
|
next := toaddr.Port - 1
|
||||||
for i := 0; i < bucketSize; i++ {
|
for i := 0; i < bucketSize; i++ {
|
||||||
result = append(result, &Node{ID: randomID(t.target, next), DiscPort: next})
|
result = append(result, &Node{ID: randomID(t.target, next), DiscPort: next})
|
||||||
}
|
}
|
||||||
@ -255,11 +269,9 @@ func (t findnodeOracle) findnode(n *Node, target NodeID) ([]*Node, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t findnodeOracle) close() {}
|
func (t findnodeOracle) close() {}
|
||||||
|
func (t findnodeOracle) waitping(from NodeID) error { return nil }
|
||||||
func (t findnodeOracle) ping(n *Node) error {
|
func (t findnodeOracle) ping(toid NodeID, toaddr *net.UDPAddr) error { return nil }
|
||||||
return errors.New("ping is not supported by this transport")
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasDuplicates(slice []*Node) bool {
|
func hasDuplicates(slice []*Node) bool {
|
||||||
seen := make(map[NodeID]bool)
|
seen := make(map[NodeID]bool)
|
||||||
|
@ -16,13 +16,18 @@ import (
|
|||||||
|
|
||||||
var log = logger.NewLogger("P2P Discovery")
|
var log = logger.NewLogger("P2P Discovery")
|
||||||
|
|
||||||
|
const Version = 3
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
var (
|
var (
|
||||||
errPacketTooSmall = errors.New("too small")
|
errPacketTooSmall = errors.New("too small")
|
||||||
errBadHash = errors.New("bad hash")
|
errBadHash = errors.New("bad hash")
|
||||||
errExpired = errors.New("expired")
|
errExpired = errors.New("expired")
|
||||||
errTimeout = errors.New("RPC timeout")
|
errBadVersion = errors.New("version mismatch")
|
||||||
errClosed = errors.New("socket closed")
|
errUnsolicitedReply = errors.New("unsolicited reply")
|
||||||
|
errUnknownNode = errors.New("unknown node")
|
||||||
|
errTimeout = errors.New("RPC timeout")
|
||||||
|
errClosed = errors.New("socket closed")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Timeouts
|
// Timeouts
|
||||||
@ -45,6 +50,7 @@ const (
|
|||||||
// RPC request structures
|
// RPC request structures
|
||||||
type (
|
type (
|
||||||
ping struct {
|
ping struct {
|
||||||
|
Version uint // must match Version
|
||||||
IP string // our IP
|
IP string // our IP
|
||||||
Port uint16 // our port
|
Port uint16 // our port
|
||||||
Expiration uint64
|
Expiration uint64
|
||||||
@ -76,14 +82,27 @@ type rpcNode struct {
|
|||||||
ID NodeID
|
ID NodeID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type packet interface {
|
||||||
|
handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type conn interface {
|
||||||
|
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
|
||||||
|
WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error)
|
||||||
|
Close() error
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
// udp implements the RPC protocol.
|
// udp implements the RPC protocol.
|
||||||
type udp struct {
|
type udp struct {
|
||||||
conn *net.UDPConn
|
conn conn
|
||||||
priv *ecdsa.PrivateKey
|
priv *ecdsa.PrivateKey
|
||||||
|
|
||||||
addpending chan *pending
|
addpending chan *pending
|
||||||
replies chan reply
|
gotreply chan reply
|
||||||
closing chan struct{}
|
|
||||||
nat nat.Interface
|
closing chan struct{}
|
||||||
|
nat nat.Interface
|
||||||
|
|
||||||
*Table
|
*Table
|
||||||
}
|
}
|
||||||
@ -120,6 +139,9 @@ type reply struct {
|
|||||||
from NodeID
|
from NodeID
|
||||||
ptype byte
|
ptype byte
|
||||||
data interface{}
|
data interface{}
|
||||||
|
// loop indicates whether there was
|
||||||
|
// a matching request by sending on this channel.
|
||||||
|
matched chan<- bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
||||||
@ -132,15 +154,20 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface) (*Table
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
tab, _ := newUDP(priv, conn, natm)
|
||||||
|
log.Infoln("Listening,", tab.self)
|
||||||
|
return tab, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface) (*Table, *udp) {
|
||||||
udp := &udp{
|
udp := &udp{
|
||||||
conn: conn,
|
conn: c,
|
||||||
priv: priv,
|
priv: priv,
|
||||||
closing: make(chan struct{}),
|
closing: make(chan struct{}),
|
||||||
|
gotreply: make(chan reply),
|
||||||
addpending: make(chan *pending),
|
addpending: make(chan *pending),
|
||||||
replies: make(chan reply),
|
|
||||||
}
|
}
|
||||||
|
realaddr := c.LocalAddr().(*net.UDPAddr)
|
||||||
realaddr := conn.LocalAddr().(*net.UDPAddr)
|
|
||||||
if natm != nil {
|
if natm != nil {
|
||||||
if !realaddr.IP.IsLoopback() {
|
if !realaddr.IP.IsLoopback() {
|
||||||
go nat.Map(natm, udp.closing, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
go nat.Map(natm, udp.closing, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
||||||
@ -151,11 +178,9 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface) (*Table
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
udp.Table = newTable(udp, PubkeyID(&priv.PublicKey), realaddr)
|
udp.Table = newTable(udp, PubkeyID(&priv.PublicKey), realaddr)
|
||||||
|
|
||||||
go udp.loop()
|
go udp.loop()
|
||||||
go udp.readLoop()
|
go udp.readLoop()
|
||||||
log.Infoln("Listening, ", udp.self)
|
return udp.Table, udp
|
||||||
return udp.Table, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *udp) close() {
|
func (t *udp) close() {
|
||||||
@ -165,10 +190,11 @@ func (t *udp) close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ping sends a ping message to the given node and waits for a reply.
|
// ping sends a ping message to the given node and waits for a reply.
|
||||||
func (t *udp) ping(e *Node) error {
|
func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error {
|
||||||
// TODO: maybe check for ReplyTo field in callback to measure RTT
|
// TODO: maybe check for ReplyTo field in callback to measure RTT
|
||||||
errc := t.pending(e.ID, pongPacket, func(interface{}) bool { return true })
|
errc := t.pending(toid, pongPacket, func(interface{}) bool { return true })
|
||||||
t.send(e, pingPacket, ping{
|
t.send(toaddr, pingPacket, ping{
|
||||||
|
Version: Version,
|
||||||
IP: t.self.IP.String(),
|
IP: t.self.IP.String(),
|
||||||
Port: uint16(t.self.TCPPort),
|
Port: uint16(t.self.TCPPort),
|
||||||
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
||||||
@ -176,12 +202,16 @@ func (t *udp) ping(e *Node) error {
|
|||||||
return <-errc
|
return <-errc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *udp) waitping(from NodeID) error {
|
||||||
|
return <-t.pending(from, pingPacket, func(interface{}) bool { return true })
|
||||||
|
}
|
||||||
|
|
||||||
// findnode sends a findnode request to the given node and waits until
|
// findnode sends a findnode request to the given node and waits until
|
||||||
// the node has sent up to k neighbors.
|
// the node has sent up to k neighbors.
|
||||||
func (t *udp) findnode(to *Node, target NodeID) ([]*Node, error) {
|
func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
|
||||||
nodes := make([]*Node, 0, bucketSize)
|
nodes := make([]*Node, 0, bucketSize)
|
||||||
nreceived := 0
|
nreceived := 0
|
||||||
errc := t.pending(to.ID, neighborsPacket, func(r interface{}) bool {
|
errc := t.pending(toid, neighborsPacket, func(r interface{}) bool {
|
||||||
reply := r.(*neighbors)
|
reply := r.(*neighbors)
|
||||||
for _, n := range reply.Nodes {
|
for _, n := range reply.Nodes {
|
||||||
nreceived++
|
nreceived++
|
||||||
@ -191,8 +221,7 @@ func (t *udp) findnode(to *Node, target NodeID) ([]*Node, error) {
|
|||||||
}
|
}
|
||||||
return nreceived >= bucketSize
|
return nreceived >= bucketSize
|
||||||
})
|
})
|
||||||
|
t.send(toaddr, findnodePacket, findnode{
|
||||||
t.send(to, findnodePacket, findnode{
|
|
||||||
Target: target,
|
Target: target,
|
||||||
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
||||||
})
|
})
|
||||||
@ -214,6 +243,17 @@ func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
|
||||||
|
matched := make(chan bool)
|
||||||
|
select {
|
||||||
|
case t.gotreply <- reply{from, ptype, req, matched}:
|
||||||
|
// loop will handle it
|
||||||
|
return <-matched
|
||||||
|
case <-t.closing:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// loop runs in its own goroutin. it keeps track of
|
// loop runs in its own goroutin. it keeps track of
|
||||||
// the refresh timer and the pending reply queue.
|
// the refresh timer and the pending reply queue.
|
||||||
func (t *udp) loop() {
|
func (t *udp) loop() {
|
||||||
@ -244,6 +284,7 @@ func (t *udp) loop() {
|
|||||||
for _, p := range pending {
|
for _, p := range pending {
|
||||||
p.errc <- errClosed
|
p.errc <- errClosed
|
||||||
}
|
}
|
||||||
|
pending = nil
|
||||||
return
|
return
|
||||||
|
|
||||||
case p := <-t.addpending:
|
case p := <-t.addpending:
|
||||||
@ -251,18 +292,21 @@ func (t *udp) loop() {
|
|||||||
pending = append(pending, p)
|
pending = append(pending, p)
|
||||||
rearmTimeout()
|
rearmTimeout()
|
||||||
|
|
||||||
case reply := <-t.replies:
|
case r := <-t.gotreply:
|
||||||
// run matching callbacks, remove if they return false.
|
var matched bool
|
||||||
for i := 0; i < len(pending); i++ {
|
for i := 0; i < len(pending); i++ {
|
||||||
p := pending[i]
|
if p := pending[i]; p.from == r.from && p.ptype == r.ptype {
|
||||||
if reply.from == p.from && reply.ptype == p.ptype && p.callback(reply.data) {
|
matched = true
|
||||||
p.errc <- nil
|
if p.callback(r.data) {
|
||||||
copy(pending[i:], pending[i+1:])
|
// callback indicates the request is done, remove it.
|
||||||
pending = pending[:len(pending)-1]
|
p.errc <- nil
|
||||||
i--
|
copy(pending[i:], pending[i+1:])
|
||||||
|
pending = pending[:len(pending)-1]
|
||||||
|
i--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rearmTimeout()
|
r.matched <- matched
|
||||||
|
|
||||||
case now := <-timeout.C:
|
case now := <-timeout.C:
|
||||||
// notify and remove callbacks whose deadline is in the past.
|
// notify and remove callbacks whose deadline is in the past.
|
||||||
@ -287,28 +331,11 @@ const (
|
|||||||
|
|
||||||
var headSpace = make([]byte, headSize)
|
var headSpace = make([]byte, headSize)
|
||||||
|
|
||||||
func (t *udp) send(to *Node, ptype byte, req interface{}) error {
|
func (t *udp) send(toaddr *net.UDPAddr, ptype byte, req interface{}) error {
|
||||||
b := new(bytes.Buffer)
|
packet, err := encodePacket(t.priv, ptype, req)
|
||||||
b.Write(headSpace)
|
|
||||||
b.WriteByte(ptype)
|
|
||||||
if err := rlp.Encode(b, req); err != nil {
|
|
||||||
log.Errorln("error encoding packet:", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
packet := b.Bytes()
|
|
||||||
sig, err := crypto.Sign(crypto.Sha3(packet[headSize:]), t.priv)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("could not sign packet:", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
copy(packet[macSize:], sig)
|
|
||||||
// add the hash to the front. Note: this doesn't protect the
|
|
||||||
// packet in any way. Our public key will be part of this hash in
|
|
||||||
// the future.
|
|
||||||
copy(packet, crypto.Sha3(packet[macSize:]))
|
|
||||||
|
|
||||||
toaddr := &net.UDPAddr{IP: to.IP, Port: to.DiscPort}
|
|
||||||
log.DebugDetailf(">>> %v %T %v\n", toaddr, req, req)
|
log.DebugDetailf(">>> %v %T %v\n", toaddr, req, req)
|
||||||
if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil {
|
if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil {
|
||||||
log.DebugDetailln("UDP send failed:", err)
|
log.DebugDetailln("UDP send failed:", err)
|
||||||
@ -316,6 +343,28 @@ func (t *udp) send(to *Node, ptype byte, req interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte, error) {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
b.Write(headSpace)
|
||||||
|
b.WriteByte(ptype)
|
||||||
|
if err := rlp.Encode(b, req); err != nil {
|
||||||
|
log.Errorln("error encoding packet:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
packet := b.Bytes()
|
||||||
|
sig, err := crypto.Sign(crypto.Sha3(packet[headSize:]), priv)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("could not sign packet:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
copy(packet[macSize:], sig)
|
||||||
|
// add the hash to the front. Note: this doesn't protect the
|
||||||
|
// packet in any way. Our public key will be part of this hash in
|
||||||
|
// The future.
|
||||||
|
copy(packet, crypto.Sha3(packet[macSize:]))
|
||||||
|
return packet, nil
|
||||||
|
}
|
||||||
|
|
||||||
// readLoop runs in its own goroutine. it handles incoming UDP packets.
|
// readLoop runs in its own goroutine. it handles incoming UDP packets.
|
||||||
func (t *udp) readLoop() {
|
func (t *udp) readLoop() {
|
||||||
defer t.conn.Close()
|
defer t.conn.Close()
|
||||||
@ -325,29 +374,34 @@ func (t *udp) readLoop() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := t.packetIn(from, buf[:nbytes]); err != nil {
|
packet, fromID, hash, err := decodePacket(buf[:nbytes])
|
||||||
|
if err != nil {
|
||||||
log.Debugf("Bad packet from %v: %v\n", from, err)
|
log.Debugf("Bad packet from %v: %v\n", from, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
log.DebugDetailf("<<< %v %T %v\n", from, packet, packet)
|
||||||
|
go func() {
|
||||||
|
if err := packet.handle(t, from, fromID, hash); err != nil {
|
||||||
|
log.Debugf("error handling %T from %v: %v", packet, from, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *udp) packetIn(from *net.UDPAddr, buf []byte) error {
|
func decodePacket(buf []byte) (packet, NodeID, []byte, error) {
|
||||||
if len(buf) < headSize+1 {
|
if len(buf) < headSize+1 {
|
||||||
return errPacketTooSmall
|
return nil, NodeID{}, nil, errPacketTooSmall
|
||||||
}
|
}
|
||||||
hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:]
|
hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:]
|
||||||
shouldhash := crypto.Sha3(buf[macSize:])
|
shouldhash := crypto.Sha3(buf[macSize:])
|
||||||
if !bytes.Equal(hash, shouldhash) {
|
if !bytes.Equal(hash, shouldhash) {
|
||||||
return errBadHash
|
return nil, NodeID{}, nil, errBadHash
|
||||||
}
|
}
|
||||||
fromID, err := recoverNodeID(crypto.Sha3(buf[headSize:]), sig)
|
fromID, err := recoverNodeID(crypto.Sha3(buf[headSize:]), sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, NodeID{}, hash, err
|
||||||
}
|
|
||||||
|
|
||||||
var req interface {
|
|
||||||
handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error
|
|
||||||
}
|
}
|
||||||
|
var req packet
|
||||||
switch ptype := sigdata[0]; ptype {
|
switch ptype := sigdata[0]; ptype {
|
||||||
case pingPacket:
|
case pingPacket:
|
||||||
req = new(ping)
|
req = new(ping)
|
||||||
@ -358,31 +412,27 @@ func (t *udp) packetIn(from *net.UDPAddr, buf []byte) error {
|
|||||||
case neighborsPacket:
|
case neighborsPacket:
|
||||||
req = new(neighbors)
|
req = new(neighbors)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown type: %d", ptype)
|
return nil, fromID, hash, fmt.Errorf("unknown type: %d", ptype)
|
||||||
}
|
}
|
||||||
if err := rlp.Decode(bytes.NewReader(sigdata[1:]), req); err != nil {
|
err = rlp.Decode(bytes.NewReader(sigdata[1:]), req)
|
||||||
return err
|
return req, fromID, hash, err
|
||||||
}
|
|
||||||
log.DebugDetailf("<<< %v %T %v\n", from, req, req)
|
|
||||||
return req.handle(t, from, fromID, hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
|
func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
|
||||||
if expired(req.Expiration) {
|
if expired(req.Expiration) {
|
||||||
return errExpired
|
return errExpired
|
||||||
}
|
}
|
||||||
t.mutex.Lock()
|
if req.Version != Version {
|
||||||
// Note: we're ignoring the provided IP address right now
|
return errBadVersion
|
||||||
n := t.bumpOrAdd(fromID, from)
|
|
||||||
if req.Port != 0 {
|
|
||||||
n.TCPPort = int(req.Port)
|
|
||||||
}
|
}
|
||||||
t.mutex.Unlock()
|
t.send(from, pongPacket, pong{
|
||||||
|
|
||||||
t.send(n, pongPacket, pong{
|
|
||||||
ReplyTok: mac,
|
ReplyTok: mac,
|
||||||
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
||||||
})
|
})
|
||||||
|
if !t.handleReply(fromID, pingPacket, req) {
|
||||||
|
// Note: we're ignoring the provided IP address right now
|
||||||
|
t.bond(true, fromID, from, req.Port)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,11 +440,9 @@ func (req *pong) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) er
|
|||||||
if expired(req.Expiration) {
|
if expired(req.Expiration) {
|
||||||
return errExpired
|
return errExpired
|
||||||
}
|
}
|
||||||
t.mutex.Lock()
|
if !t.handleReply(fromID, pongPacket, req) {
|
||||||
t.bump(fromID)
|
return errUnsolicitedReply
|
||||||
t.mutex.Unlock()
|
}
|
||||||
|
|
||||||
t.replies <- reply{fromID, pongPacket, req}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,12 +450,21 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
|
|||||||
if expired(req.Expiration) {
|
if expired(req.Expiration) {
|
||||||
return errExpired
|
return errExpired
|
||||||
}
|
}
|
||||||
|
if t.db.get(fromID) == nil {
|
||||||
|
// No bond exists, we don't process the packet. This prevents
|
||||||
|
// an attack vector where the discovery protocol could be used
|
||||||
|
// to amplify traffic in a DDOS attack. A malicious actor
|
||||||
|
// would send a findnode request with the IP address and UDP
|
||||||
|
// port of the target as the source address. The recipient of
|
||||||
|
// the findnode packet would then send a neighbors packet
|
||||||
|
// (which is a much bigger packet than findnode) to the victim.
|
||||||
|
return errUnknownNode
|
||||||
|
}
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
e := t.bumpOrAdd(fromID, from)
|
|
||||||
closest := t.closest(req.Target, bucketSize).entries
|
closest := t.closest(req.Target, bucketSize).entries
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
|
|
||||||
t.send(e, neighborsPacket, neighbors{
|
t.send(from, neighborsPacket, neighbors{
|
||||||
Nodes: closest,
|
Nodes: closest,
|
||||||
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
||||||
})
|
})
|
||||||
@ -418,12 +475,9 @@ func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byt
|
|||||||
if expired(req.Expiration) {
|
if expired(req.Expiration) {
|
||||||
return errExpired
|
return errExpired
|
||||||
}
|
}
|
||||||
t.mutex.Lock()
|
if !t.handleReply(fromID, neighborsPacket, req) {
|
||||||
t.bump(fromID)
|
return errUnsolicitedReply
|
||||||
t.add(req.Nodes)
|
}
|
||||||
t.mutex.Unlock()
|
|
||||||
|
|
||||||
t.replies <- reply{fromID, neighborsPacket, req}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
package discover
|
package discover
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
logpkg "log"
|
logpkg "log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -15,22 +23,243 @@ func init() {
|
|||||||
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel))
|
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_ping(t *testing.T) {
|
type udpTest struct {
|
||||||
|
t *testing.T
|
||||||
|
pipe *dgramPipe
|
||||||
|
table *Table
|
||||||
|
udp *udp
|
||||||
|
sent [][]byte
|
||||||
|
localkey, remotekey *ecdsa.PrivateKey
|
||||||
|
remoteaddr *net.UDPAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUDPTest(t *testing.T) *udpTest {
|
||||||
|
test := &udpTest{
|
||||||
|
t: t,
|
||||||
|
pipe: newpipe(),
|
||||||
|
localkey: newkey(),
|
||||||
|
remotekey: newkey(),
|
||||||
|
remoteaddr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 30303},
|
||||||
|
}
|
||||||
|
test.table, test.udp = newUDP(test.localkey, test.pipe, nil)
|
||||||
|
return test
|
||||||
|
}
|
||||||
|
|
||||||
|
// handles a packet as if it had been sent to the transport.
|
||||||
|
func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error {
|
||||||
|
enc, err := encodePacket(test.remotekey, ptype, data)
|
||||||
|
if err != nil {
|
||||||
|
return test.errorf("packet (%d) encode error: %v", err)
|
||||||
|
}
|
||||||
|
test.sent = append(test.sent, enc)
|
||||||
|
err = data.handle(test.udp, test.remoteaddr, PubkeyID(&test.remotekey.PublicKey), enc[:macSize])
|
||||||
|
if err != wantError {
|
||||||
|
return test.errorf("error mismatch: got %q, want %q", err, wantError)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// waits for a packet to be sent by the transport.
|
||||||
|
// validate should have type func(*udpTest, X) error, where X is a packet type.
|
||||||
|
func (test *udpTest) waitPacketOut(validate interface{}) error {
|
||||||
|
dgram := test.pipe.waitPacketOut()
|
||||||
|
p, _, _, err := decodePacket(dgram)
|
||||||
|
if err != nil {
|
||||||
|
return test.errorf("sent packet decode error: %v", err)
|
||||||
|
}
|
||||||
|
fn := reflect.ValueOf(validate)
|
||||||
|
exptype := fn.Type().In(0)
|
||||||
|
if reflect.TypeOf(p) != exptype {
|
||||||
|
return test.errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype)
|
||||||
|
}
|
||||||
|
fn.Call([]reflect.Value{reflect.ValueOf(p)})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (test *udpTest) errorf(format string, args ...interface{}) error {
|
||||||
|
_, file, line, ok := runtime.Caller(2) // errorf + waitPacketOut
|
||||||
|
if ok {
|
||||||
|
file = path.Base(file)
|
||||||
|
} else {
|
||||||
|
file = "???"
|
||||||
|
line = 1
|
||||||
|
}
|
||||||
|
err := fmt.Errorf(format, args...)
|
||||||
|
fmt.Printf("\t%s:%d: %v\n", file, line, err)
|
||||||
|
test.t.Fail()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// shared test variables
|
||||||
|
var (
|
||||||
|
futureExp = uint64(time.Now().Add(10 * time.Hour).Unix())
|
||||||
|
testTarget = MustHexID("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUDP_packetErrors(t *testing.T) {
|
||||||
|
test := newUDPTest(t)
|
||||||
|
defer test.table.Close()
|
||||||
|
|
||||||
|
test.packetIn(errExpired, pingPacket, &ping{IP: "foo", Port: 99, Version: Version})
|
||||||
|
test.packetIn(errBadVersion, pingPacket, &ping{IP: "foo", Port: 99, Version: 99, Expiration: futureExp})
|
||||||
|
test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp})
|
||||||
|
test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp})
|
||||||
|
test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUDP_pingTimeout(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
test := newUDPTest(t)
|
||||||
|
defer test.table.Close()
|
||||||
|
|
||||||
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
|
toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
|
||||||
n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
|
toid := NodeID{1, 2, 3, 4}
|
||||||
defer n1.Close()
|
if err := test.udp.ping(toid, toaddr); err != errTimeout {
|
||||||
defer n2.Close()
|
t.Error("expected timeout error, got", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := n1.net.ping(n2.self); err != nil {
|
func TestUDP_findnodeTimeout(t *testing.T) {
|
||||||
t.Fatalf("ping error: %v", err)
|
t.Parallel()
|
||||||
|
test := newUDPTest(t)
|
||||||
|
defer test.table.Close()
|
||||||
|
|
||||||
|
toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
|
||||||
|
toid := NodeID{1, 2, 3, 4}
|
||||||
|
target := NodeID{4, 5, 6, 7}
|
||||||
|
result, err := test.udp.findnode(toid, toaddr, target)
|
||||||
|
if err != errTimeout {
|
||||||
|
t.Error("expected timeout error, got", err)
|
||||||
}
|
}
|
||||||
if find(n2, n1.self.ID) == nil {
|
if len(result) > 0 {
|
||||||
t.Errorf("node 2 does not contain id of node 1")
|
t.Error("expected empty result, got", result)
|
||||||
}
|
}
|
||||||
if e := find(n1, n2.self.ID); e != nil {
|
}
|
||||||
t.Errorf("node 1 does contains id of node 2: %v", e)
|
|
||||||
|
func TestUDP_findnode(t *testing.T) {
|
||||||
|
test := newUDPTest(t)
|
||||||
|
defer test.table.Close()
|
||||||
|
|
||||||
|
// put a few nodes into the table. their exact
|
||||||
|
// distribution shouldn't matter much, altough we need to
|
||||||
|
// take care not to overflow any bucket.
|
||||||
|
target := testTarget
|
||||||
|
nodes := &nodesByDistance{target: target}
|
||||||
|
for i := 0; i < bucketSize; i++ {
|
||||||
|
nodes.push(&Node{
|
||||||
|
IP: net.IP{1, 2, 3, byte(i)},
|
||||||
|
DiscPort: i + 2,
|
||||||
|
TCPPort: i + 2,
|
||||||
|
ID: randomID(test.table.self.ID, i+2),
|
||||||
|
}, bucketSize)
|
||||||
|
}
|
||||||
|
test.table.add(nodes.entries)
|
||||||
|
|
||||||
|
// ensure there's a bond with the test node,
|
||||||
|
// findnode won't be accepted otherwise.
|
||||||
|
test.table.db.add(PubkeyID(&test.remotekey.PublicKey), test.remoteaddr, 99)
|
||||||
|
|
||||||
|
// check that closest neighbors are returned.
|
||||||
|
test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
|
||||||
|
test.waitPacketOut(func(p *neighbors) {
|
||||||
|
expected := test.table.closest(testTarget, bucketSize)
|
||||||
|
if len(p.Nodes) != bucketSize {
|
||||||
|
t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
|
||||||
|
}
|
||||||
|
for i := range p.Nodes {
|
||||||
|
if p.Nodes[i].ID != expected.entries[i].ID {
|
||||||
|
t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUDP_findnodeMultiReply(t *testing.T) {
|
||||||
|
test := newUDPTest(t)
|
||||||
|
defer test.table.Close()
|
||||||
|
|
||||||
|
// queue a pending findnode request
|
||||||
|
resultc, errc := make(chan []*Node), make(chan error)
|
||||||
|
go func() {
|
||||||
|
rid := PubkeyID(&test.remotekey.PublicKey)
|
||||||
|
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
|
||||||
|
if err != nil && len(ns) == 0 {
|
||||||
|
errc <- err
|
||||||
|
} else {
|
||||||
|
resultc <- ns
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait for the findnode to be sent.
|
||||||
|
// after it is sent, the transport is waiting for a reply
|
||||||
|
test.waitPacketOut(func(p *findnode) {
|
||||||
|
if p.Target != testTarget {
|
||||||
|
t.Errorf("wrong target: got %v, want %v", p.Target, testTarget)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// send the reply as two packets.
|
||||||
|
list := []*Node{
|
||||||
|
MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303"),
|
||||||
|
MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
|
||||||
|
MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301"),
|
||||||
|
MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
|
||||||
|
}
|
||||||
|
test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: list[:2]})
|
||||||
|
test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: list[2:]})
|
||||||
|
|
||||||
|
// check that the sent neighbors are all returned by findnode
|
||||||
|
select {
|
||||||
|
case result := <-resultc:
|
||||||
|
if !reflect.DeepEqual(result, list) {
|
||||||
|
t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, list)
|
||||||
|
}
|
||||||
|
case err := <-errc:
|
||||||
|
t.Errorf("findnode error: %v", err)
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Error("findnode did not return within 5 seconds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUDP_successfulPing(t *testing.T) {
|
||||||
|
test := newUDPTest(t)
|
||||||
|
defer test.table.Close()
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
test.packetIn(nil, pingPacket, &ping{IP: "foo", Port: 99, Version: Version, Expiration: futureExp})
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// the ping is replied to.
|
||||||
|
test.waitPacketOut(func(p *pong) {
|
||||||
|
pinghash := test.sent[0][:macSize]
|
||||||
|
if !bytes.Equal(p.ReplyTok, pinghash) {
|
||||||
|
t.Errorf("got ReplyTok %x, want %x", p.ReplyTok, pinghash)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// remote is unknown, the table pings back.
|
||||||
|
test.waitPacketOut(func(p *ping) error { return nil })
|
||||||
|
test.packetIn(nil, pongPacket, &pong{Expiration: futureExp})
|
||||||
|
|
||||||
|
// ping should return shortly after getting the pong packet.
|
||||||
|
<-done
|
||||||
|
|
||||||
|
// check that the node was added.
|
||||||
|
rid := PubkeyID(&test.remotekey.PublicKey)
|
||||||
|
rnode := find(test.table, rid)
|
||||||
|
if rnode == nil {
|
||||||
|
t.Fatalf("node %v not found in table", rid)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(rnode.IP, test.remoteaddr.IP) {
|
||||||
|
t.Errorf("node has wrong IP: got %v, want: %v", rnode.IP, test.remoteaddr.IP)
|
||||||
|
}
|
||||||
|
if rnode.DiscPort != test.remoteaddr.Port {
|
||||||
|
t.Errorf("node has wrong Port: got %v, want: %v", rnode.DiscPort, test.remoteaddr.Port)
|
||||||
|
}
|
||||||
|
if rnode.TCPPort != 99 {
|
||||||
|
t.Errorf("node has wrong Port: got %v, want: %v", rnode.TCPPort, 99)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,167 +274,66 @@ func find(tab *Table, id NodeID) *Node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_findnode(t *testing.T) {
|
// dgramPipe is a fake UDP socket. It queues all sent datagrams.
|
||||||
t.Parallel()
|
type dgramPipe struct {
|
||||||
|
mu *sync.Mutex
|
||||||
|
cond *sync.Cond
|
||||||
|
closing chan struct{}
|
||||||
|
closed bool
|
||||||
|
queue [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
|
func newpipe() *dgramPipe {
|
||||||
n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
|
mu := new(sync.Mutex)
|
||||||
defer n1.Close()
|
return &dgramPipe{
|
||||||
defer n2.Close()
|
closing: make(chan struct{}),
|
||||||
|
cond: &sync.Cond{L: mu},
|
||||||
// put a few nodes into n2. the exact distribution shouldn't
|
mu: mu,
|
||||||
// matter much, altough we need to take care not to overflow
|
|
||||||
// any bucket.
|
|
||||||
target := randomID(n1.self.ID, 100)
|
|
||||||
nodes := &nodesByDistance{target: target}
|
|
||||||
for i := 0; i < bucketSize; i++ {
|
|
||||||
n2.add([]*Node{&Node{
|
|
||||||
IP: net.IP{1, 2, 3, byte(i)},
|
|
||||||
DiscPort: i + 2,
|
|
||||||
TCPPort: i + 2,
|
|
||||||
ID: randomID(n2.self.ID, i+2),
|
|
||||||
}})
|
|
||||||
}
|
|
||||||
n2.add(nodes.entries)
|
|
||||||
n2.bumpOrAdd(n1.self.ID, &net.UDPAddr{IP: n1.self.IP, Port: n1.self.DiscPort})
|
|
||||||
expected := n2.closest(target, bucketSize)
|
|
||||||
|
|
||||||
err := runUDP(10, func() error {
|
|
||||||
result, _ := n1.net.findnode(n2.self, target)
|
|
||||||
if len(result) != bucketSize {
|
|
||||||
return fmt.Errorf("wrong number of results: got %d, want %d", len(result), bucketSize)
|
|
||||||
}
|
|
||||||
for i := range result {
|
|
||||||
if result[i].ID != expected.entries[i].ID {
|
|
||||||
return fmt.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, result[i], expected.entries[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_replytimeout(t *testing.T) {
|
// WriteToUDP queues a datagram.
|
||||||
t.Parallel()
|
func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) {
|
||||||
|
msg := make([]byte, len(b))
|
||||||
// reserve a port so we don't talk to an existing service by accident
|
copy(msg, b)
|
||||||
addr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
c.mu.Lock()
|
||||||
fd, err := net.ListenUDP("udp", addr)
|
defer c.mu.Unlock()
|
||||||
if err != nil {
|
if c.closed {
|
||||||
t.Fatal(err)
|
return 0, errors.New("closed")
|
||||||
}
|
|
||||||
defer fd.Close()
|
|
||||||
|
|
||||||
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
|
|
||||||
defer n1.Close()
|
|
||||||
n2 := n1.bumpOrAdd(randomID(n1.self.ID, 10), fd.LocalAddr().(*net.UDPAddr))
|
|
||||||
|
|
||||||
if err := n1.net.ping(n2); err != errTimeout {
|
|
||||||
t.Error("expected timeout error, got", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result, err := n1.net.findnode(n2, n1.self.ID); err != errTimeout {
|
|
||||||
t.Error("expected timeout error, got", err)
|
|
||||||
} else if len(result) > 0 {
|
|
||||||
t.Error("expected empty result, got", result)
|
|
||||||
}
|
}
|
||||||
|
c.queue = append(c.queue, msg)
|
||||||
|
c.cond.Signal()
|
||||||
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUDP_findnodeMultiReply(t *testing.T) {
|
// ReadFromUDP just hangs until the pipe is closed.
|
||||||
t.Parallel()
|
func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
||||||
|
<-c.closing
|
||||||
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
|
return 0, nil, io.EOF
|
||||||
n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
|
|
||||||
udp2 := n2.net.(*udp)
|
|
||||||
defer n1.Close()
|
|
||||||
defer n2.Close()
|
|
||||||
|
|
||||||
err := runUDP(10, func() error {
|
|
||||||
nodes := make([]*Node, bucketSize)
|
|
||||||
for i := range nodes {
|
|
||||||
nodes[i] = &Node{
|
|
||||||
IP: net.IP{1, 2, 3, 4},
|
|
||||||
DiscPort: i + 1,
|
|
||||||
TCPPort: i + 1,
|
|
||||||
ID: randomID(n2.self.ID, i+1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ask N2 for neighbors. it will send an empty reply back.
|
|
||||||
// the request will wait for up to bucketSize replies.
|
|
||||||
resultc := make(chan []*Node)
|
|
||||||
errc := make(chan error)
|
|
||||||
go func() {
|
|
||||||
ns, err := n1.net.findnode(n2.self, n1.self.ID)
|
|
||||||
if err != nil {
|
|
||||||
errc <- err
|
|
||||||
} else {
|
|
||||||
resultc <- ns
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// send a few more neighbors packets to N1.
|
|
||||||
// it should collect those.
|
|
||||||
for end := 0; end < len(nodes); {
|
|
||||||
off := end
|
|
||||||
if end = end + 5; end > len(nodes) {
|
|
||||||
end = len(nodes)
|
|
||||||
}
|
|
||||||
udp2.send(n1.self, neighborsPacket, neighbors{
|
|
||||||
Nodes: nodes[off:end],
|
|
||||||
Expiration: uint64(time.Now().Add(10 * time.Second).Unix()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that they are all returned. we cannot just check for
|
|
||||||
// equality because they might not be returned in the order they
|
|
||||||
// were sent.
|
|
||||||
var result []*Node
|
|
||||||
select {
|
|
||||||
case result = <-resultc:
|
|
||||||
case err := <-errc:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if hasDuplicates(result) {
|
|
||||||
return fmt.Errorf("result slice contains duplicates")
|
|
||||||
}
|
|
||||||
if len(result) != len(nodes) {
|
|
||||||
return fmt.Errorf("wrong number of nodes returned: got %d, want %d", len(result), len(nodes))
|
|
||||||
}
|
|
||||||
matched := make(map[NodeID]bool)
|
|
||||||
for _, n := range result {
|
|
||||||
for _, expn := range nodes {
|
|
||||||
if n.ID == expn.ID { // && bytes.Equal(n.Addr.IP, expn.Addr.IP) && n.Addr.Port == expn.Addr.Port {
|
|
||||||
matched[n.ID] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(matched) != len(nodes) {
|
|
||||||
return fmt.Errorf("wrong number of matching nodes: got %d, want %d", len(matched), len(nodes))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runUDP runs a test n times and returns an error if the test failed
|
func (c *dgramPipe) Close() error {
|
||||||
// in all n runs. This is necessary because UDP is unreliable even for
|
c.mu.Lock()
|
||||||
// connections on the local machine, causing test failures.
|
defer c.mu.Unlock()
|
||||||
func runUDP(n int, test func() error) error {
|
if !c.closed {
|
||||||
errcount := 0
|
close(c.closing)
|
||||||
errors := ""
|
c.closed = true
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if err := test(); err != nil {
|
|
||||||
errors += fmt.Sprintf("\n#%d: %v", i, err)
|
|
||||||
errcount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if errcount == n {
|
|
||||||
return fmt.Errorf("failed on all %d iterations:%s", n, errors)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *dgramPipe) LocalAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dgramPipe) waitPacketOut() []byte {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
for len(c.queue) == 0 {
|
||||||
|
c.cond.Wait()
|
||||||
|
}
|
||||||
|
p := c.queue[0]
|
||||||
|
copy(c.queue, c.queue[1:])
|
||||||
|
c.queue = c.queue[:len(c.queue)-1]
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
54
params/protocol_params.go
Executable file
54
params/protocol_params.go
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
// DO NOT EDIT!!!
|
||||||
|
// AUTOGENERATED FROM generators/defaults.go
|
||||||
|
|
||||||
|
package params
|
||||||
|
|
||||||
|
import "math/big"
|
||||||
|
|
||||||
|
var (
|
||||||
|
MaximumExtraDataSize = big.NewInt(1024) // Maximum size extra data may be after Genesis.
|
||||||
|
ExpByteGas = big.NewInt(10) // Times ceil(log256(exponent)) for the EXP instruction.
|
||||||
|
SloadGas = big.NewInt(50) // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
|
||||||
|
CallValueTransferGas = big.NewInt(9000) // Paid for CALL when the value transfor is non-zero.
|
||||||
|
CallNewAccountGas = big.NewInt(25000) // Paid for CALL when the destination address didn't exist prior.
|
||||||
|
TxGas = big.NewInt(21000) // Per transaction. NOTE: Not payable on data of calls between transactions.
|
||||||
|
TxDataZeroGas = big.NewInt(4) // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
|
||||||
|
GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
|
||||||
|
DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations.
|
||||||
|
QuadCoeffDiv = big.NewInt(512) // Divisor for the quadratic particle of the memory cost equation.
|
||||||
|
GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block.
|
||||||
|
DurationLimit = big.NewInt(8) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
|
||||||
|
SstoreSetGas = big.NewInt(20000) // Once per SLOAD operation.
|
||||||
|
LogDataGas = big.NewInt(8) // Per byte in a LOG* operation's data.
|
||||||
|
CallStipend = big.NewInt(2300) // Free gas given at beginning of call.
|
||||||
|
EcrecoverGas = big.NewInt(3000) //
|
||||||
|
Sha256WordGas = big.NewInt(12) //
|
||||||
|
MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
|
||||||
|
Sha3Gas = big.NewInt(30) // Once per SHA3 operation.
|
||||||
|
Sha256Gas = big.NewInt(60) //
|
||||||
|
IdentityWordGas = big.NewInt(3) //
|
||||||
|
Sha3WordGas = big.NewInt(6) // Once per word of the SHA3 operation's data.
|
||||||
|
SstoreResetGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness changes from zero.
|
||||||
|
SstoreClearGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness doesn't change.
|
||||||
|
SstoreRefundGas = big.NewInt(15000) // Once per SSTORE operation if the zeroness changes to zero.
|
||||||
|
JumpdestGas = big.NewInt(1) // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
|
||||||
|
IdentityGas = big.NewInt(15) //
|
||||||
|
GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations.
|
||||||
|
EpochDuration = big.NewInt(30000) // Duration between proof-of-work epochs.
|
||||||
|
CallGas = big.NewInt(40) // Once per CALL operation & message call transaction.
|
||||||
|
CreateDataGas = big.NewInt(200) //
|
||||||
|
Ripemd160Gas = big.NewInt(600) //
|
||||||
|
Ripemd160WordGas = big.NewInt(120) //
|
||||||
|
MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
|
||||||
|
CallCreateDepth = big.NewInt(1024) // Maximum depth of call/create stack.
|
||||||
|
ExpGas = big.NewInt(10) // Once per EXP instuction.
|
||||||
|
LogGas = big.NewInt(375) // Per LOG* operation.
|
||||||
|
CopyGas = big.NewInt(3) //
|
||||||
|
StackLimit = big.NewInt(1024) // Maximum size of VM stack allowed.
|
||||||
|
TierStepGas = big.NewInt(0) // Once per operation, for a selection of them.
|
||||||
|
LogTopicGas = big.NewInt(375) // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
|
||||||
|
CreateGas = big.NewInt(32000) // Once per CREATE operation & contract-creation transaction.
|
||||||
|
SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation.
|
||||||
|
MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
||||||
|
TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
||||||
|
)
|
10
rpc/api.go
10
rpc/api.go
@ -80,8 +80,9 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance()
|
*reply = api.xethAtStateNum(args.BlockNumber).BalanceAt(args.Address)
|
||||||
*reply = common.ToHex(v.Bytes())
|
//v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance()
|
||||||
|
//*reply = common.ToHex(v.Bytes())
|
||||||
case "eth_getStorage", "eth_storageAt":
|
case "eth_getStorage", "eth_storageAt":
|
||||||
args := new(GetStorageArgs)
|
args := new(GetStorageArgs)
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
if err := json.Unmarshal(req.Params, &args); err != nil {
|
||||||
@ -95,10 +96,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
state := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address)
|
*reply = api.xethAtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key)
|
||||||
value := state.StorageString(args.Key)
|
|
||||||
|
|
||||||
*reply = common.ToHex(value.Bytes())
|
|
||||||
case "eth_getTransactionCount":
|
case "eth_getTransactionCount":
|
||||||
args := new(GetTxCountArgs)
|
args := new(GetTxCountArgs)
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil {
|
if err := json.Unmarshal(req.Params, &args); err != nil {
|
||||||
|
@ -114,7 +114,7 @@ func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sync objects to trie
|
// sync objects to trie
|
||||||
statedb.Update(nil)
|
statedb.Update()
|
||||||
// sync trie to disk
|
// sync trie to disk
|
||||||
statedb.Sync()
|
statedb.Sync()
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"genesis_rlp_hex": "f90219f90214a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a09178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808080a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000088000000000000002ac0c0",
|
"genesis_rlp_hex": "f901f8f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a09178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808080a0000000000000000000000000000000000000000000000000000000000000000088000000000000002ac0c0",
|
||||||
"genesis_state_root": "9178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4e",
|
"genesis_state_root": "9178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4e",
|
||||||
"genesis_hash": "b5d6d8402156c5c1dfadaa4b87c676b5bcadb17ef9bc8e939606daaa0d35f55d"
|
"genesis_hash": "fd4af92a79c7fc2fd8bf0d342f2e832e1d4f485c85b9152d2039e03bc604fdca"
|
||||||
}
|
}
|
||||||
|
71
tests/files/StateTests/RandomTests/st201503261401CPPJIT.json
Normal file
71
tests/files/StateTests/RandomTests/st201503261401CPPJIT.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"randomStatetest" : {
|
||||||
|
"env" : {
|
||||||
|
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
|
||||||
|
"currentDifficulty" : "5623894562375",
|
||||||
|
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
|
||||||
|
"currentNumber" : "0",
|
||||||
|
"currentTimestamp" : "1",
|
||||||
|
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
|
||||||
|
},
|
||||||
|
"logs" : [
|
||||||
|
],
|
||||||
|
"out" : "0x",
|
||||||
|
"post" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "972201916",
|
||||||
|
"code" : "0x7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000019e7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c35036017f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b51916f2620a6e7d187a5560005155",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "912450255",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "999999998115347875",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "1",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postStateRoot" : "24e7dcfb7ff0269a9000bedfeafad33c3ccc3610e3031c88bace245f3cbe64d2",
|
||||||
|
"pre" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0",
|
||||||
|
"code" : "0x7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000019e7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c35036017f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b51916f2620a6e7d187a5560005155",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "46",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "1000000000000000000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transaction" : {
|
||||||
|
"data" : "0x7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000019e7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c35036017f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b51916f2620a6e7d187a",
|
||||||
|
"gasLimit" : "0x3662e2a1",
|
||||||
|
"gasPrice" : "1",
|
||||||
|
"nonce" : "0",
|
||||||
|
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
|
||||||
|
"value" : "972201916"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
tests/files/StateTests/RandomTests/st201504011535GO.json
Normal file
71
tests/files/StateTests/RandomTests/st201504011535GO.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"randomStatetest" : {
|
||||||
|
"env" : {
|
||||||
|
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
|
||||||
|
"currentDifficulty" : "5623894562375",
|
||||||
|
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
|
||||||
|
"currentNumber" : "0",
|
||||||
|
"currentTimestamp" : "1",
|
||||||
|
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
|
||||||
|
},
|
||||||
|
"logs" : [
|
||||||
|
],
|
||||||
|
"out" : "0x",
|
||||||
|
"post" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0",
|
||||||
|
"code" : "0x31417f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0315208a675944747f7430661519049a55",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "704316238",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "999999999295683808",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "1",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postStateRoot" : "ddb8f27abb51685caa793be133df9db3912648c02498b74a0ae874294acce6f0",
|
||||||
|
"pre" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0",
|
||||||
|
"code" : "0x31417f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0315208a675944747f7430661519049a55",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "46",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "1000000000000000000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transaction" : {
|
||||||
|
"data" : "0x31417f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0315208a675944747f7430661519049a",
|
||||||
|
"gasLimit" : "0x29fb0320",
|
||||||
|
"gasPrice" : "1",
|
||||||
|
"nonce" : "0",
|
||||||
|
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
|
||||||
|
"value" : "1758540724"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
tests/files/StateTests/RandomTests/st201504011536GO.json
Normal file
71
tests/files/StateTests/RandomTests/st201504011536GO.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"randomStatetest" : {
|
||||||
|
"env" : {
|
||||||
|
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
|
||||||
|
"currentDifficulty" : "5623894562375",
|
||||||
|
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
|
||||||
|
"currentNumber" : "0",
|
||||||
|
"currentTimestamp" : "1",
|
||||||
|
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
|
||||||
|
},
|
||||||
|
"logs" : [
|
||||||
|
],
|
||||||
|
"out" : "0x",
|
||||||
|
"post" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0",
|
||||||
|
"code" : "0x44207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000001145344846604627f5560005155",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "620442430",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "999999999379557616",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "1",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postStateRoot" : "da70f5af0d1545ec9f0f0e28a55d3f2896bb3d64c71b4908f72ed26c345aafe4",
|
||||||
|
"pre" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0",
|
||||||
|
"code" : "0x44207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000001145344846604627f5560005155",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "46",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "1000000000000000000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transaction" : {
|
||||||
|
"data" : "0x44207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000001145344846604627f",
|
||||||
|
"gasLimit" : "0x24fb3310",
|
||||||
|
"gasPrice" : "1",
|
||||||
|
"nonce" : "0",
|
||||||
|
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
|
||||||
|
"value" : "1561176030"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
tests/files/StateTests/RandomTests/st201504011547GO.json
Normal file
71
tests/files/StateTests/RandomTests/st201504011547GO.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"randomStatetest" : {
|
||||||
|
"env" : {
|
||||||
|
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
|
||||||
|
"currentDifficulty" : "5623894562375",
|
||||||
|
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
|
||||||
|
"currentNumber" : "0",
|
||||||
|
"currentTimestamp" : "1",
|
||||||
|
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
|
||||||
|
},
|
||||||
|
"logs" : [
|
||||||
|
],
|
||||||
|
"out" : "0x",
|
||||||
|
"post" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0",
|
||||||
|
"code" : "0x7f0000000000000000000000000000000000000000000000000000000000000001207f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe406f",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "1258533548",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "999999998741466498",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "1",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postStateRoot" : "e1881131f80068947922f01c78464f93c46e6e11314d0deceb55809e594aec0a",
|
||||||
|
"pre" : {
|
||||||
|
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
|
||||||
|
"balance" : "0",
|
||||||
|
"code" : "0x7f0000000000000000000000000000000000000000000000000000000000000001207f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe406f",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
|
||||||
|
"balance" : "46",
|
||||||
|
"code" : "0x6000355415600957005b60203560003555",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "1000000000000000000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transaction" : {
|
||||||
|
"data" : "0x7f0000000000000000000000000000000000000000000000000000000000000001207f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe406f",
|
||||||
|
"gasLimit" : "0x4b03b27e",
|
||||||
|
"gasPrice" : "1",
|
||||||
|
"nonce" : "0",
|
||||||
|
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||||
|
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
|
||||||
|
"value" : "781711523"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -868,7 +868,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"6295ee1b4f6dd65047762f924ecd367c17eabf8f" : {
|
"6295ee1b4f6dd65047762f924ecd367c17eabf8f" : {
|
||||||
"balance" : "100000",
|
"balance" : "200000",
|
||||||
"code" : "0x60003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b5056",
|
"code" : "0x60003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b5056",
|
||||||
"nonce" : "0",
|
"nonce" : "0",
|
||||||
"storage" : {
|
"storage" : {
|
||||||
@ -880,17 +880,29 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
"balance" : "999999999999533644",
|
"balance" : "9999999533644",
|
||||||
"code" : "0x",
|
"code" : "0x",
|
||||||
"nonce" : "1",
|
"nonce" : "1",
|
||||||
"storage" : {
|
"storage" : {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"postStateRoot" : "239c509594811741c8f3ed0a2d89abb00c0398098c80f88a82cebc153dec5c4b",
|
"postStateRoot" : "5500215cdbf8165ca47720ad088ae49c2441560cdf267f1c946ae7b9807cb1d4",
|
||||||
"pre" : {
|
"pre" : {
|
||||||
|
"6295ee1b4f6dd65047762f924ecd367c17eabf8f" : {
|
||||||
|
"balance" : "100000",
|
||||||
|
"code" : "0x60003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b5056",
|
||||||
|
"nonce" : "0",
|
||||||
|
"storage" : {
|
||||||
|
"0x" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"0x01" : "0x42",
|
||||||
|
"0x02" : "0x23",
|
||||||
|
"0x03" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"0x05" : "0x54c98c81"
|
||||||
|
}
|
||||||
|
},
|
||||||
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
"balance" : "1000000000000000000",
|
"balance" : "10000000000000",
|
||||||
"code" : "0x",
|
"code" : "0x",
|
||||||
"nonce" : "0",
|
"nonce" : "0",
|
||||||
"storage" : {
|
"storage" : {
|
||||||
|
@ -185,7 +185,7 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, state.
|
|||||||
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) {
|
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) {
|
||||||
statedb.Set(snapshot)
|
statedb.Set(snapshot)
|
||||||
}
|
}
|
||||||
statedb.Update(vmenv.Gas)
|
statedb.Update()
|
||||||
|
|
||||||
return ret, vmenv.logs, vmenv.Gas, err
|
return ret, vmenv.logs, vmenv.Gas, err
|
||||||
}
|
}
|
||||||
|
10
xeth/xeth.go
10
xeth/xeth.go
@ -308,21 +308,19 @@ func (self *XEth) NumberToHuman(balance string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) StorageAt(addr, storageAddr string) string {
|
func (self *XEth) StorageAt(addr, storageAddr string) string {
|
||||||
storage := self.State().SafeGet(addr).StorageString(storageAddr)
|
return common.ToHex(self.State().state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)))
|
||||||
|
|
||||||
return common.ToHex(storage.Bytes())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) BalanceAt(addr string) string {
|
func (self *XEth) BalanceAt(addr string) string {
|
||||||
return self.State().SafeGet(addr).Balance().String()
|
return common.ToHex(self.State().state.GetBalance(common.HexToAddress(addr)).Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) TxCountAt(address string) int {
|
func (self *XEth) TxCountAt(address string) int {
|
||||||
return int(self.State().SafeGet(address).Nonce())
|
return int(self.State().state.GetNonce(common.HexToAddress(address)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) CodeAt(address string) string {
|
func (self *XEth) CodeAt(address string) string {
|
||||||
return common.ToHex(self.State().SafeGet(address).Code())
|
return common.ToHex(self.State().state.GetCode(common.HexToAddress(address)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *XEth) CodeAtBytes(address string) []byte {
|
func (self *XEth) CodeAtBytes(address string) []byte {
|
||||||
|
Loading…
Reference in New Issue
Block a user