mirror of https://github.com/poanetwork/quorum.git
merge 1.8.12 with upstream/master to apply the fixes/changes added newly
This commit is contained in:
commit
e6773756fb
|
@ -1,6 +1,6 @@
|
|||
# Quorum
|
||||
|
||||
<a href="https://quorumslack.azurewebsites.net" target="_blank" rel="noopener"><img title="Quorum Slack" src="https://quorumslack.azurewebsites.net/badge.svg" alt="Quorum Slack" /></a>
|
||||
<a href="https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/" target="_blank" rel="noopener"><img title="Quorum Slack" src="https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/badge.svg" alt="Quorum Slack" /></a>
|
||||
|
||||
Quorum is an Ethereum-based distributed ledger protocol with transaction/contract privacy and new consensus mechanisms.
|
||||
|
||||
|
@ -143,6 +143,7 @@ Further documentation can be found in the [docs](docs/) folder and on the [wiki]
|
|||
* [quorum-examples](https://github.com/jpmorganchase/quorum-examples): example quorum clusters
|
||||
* [quorum-tools](https://github.com/jpmorganchase/quorum-tools): local cluster orchestration, and integration testing tool
|
||||
* [Quorum Wiki](https://github.com/jpmorganchase/quorum/wiki)
|
||||
* [Quorum Community Slack Inviter](https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/): Quorum Slack community entry point
|
||||
|
||||
## Third Party Tools/Libraries
|
||||
|
||||
|
|
|
@ -278,7 +278,7 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
|
|||
return nil, ErrLocked
|
||||
}
|
||||
// Depending on the presence of the chain ID, sign with EIP155 or homestead
|
||||
if chainID != nil && !isQuorum {
|
||||
if chainID != nil && !tx.IsPrivate() {
|
||||
return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
|
||||
}
|
||||
return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
|
||||
|
|
|
@ -562,6 +562,9 @@ func (ethash *Ethash) SetThreads(threads int) {
|
|||
// Hashrate implements PoW, returning the measured rate of the search invocations
|
||||
// per second over the last minute.
|
||||
func (ethash *Ethash) Hashrate() float64 {
|
||||
if(ethash.hashrate == nil){
|
||||
return 0
|
||||
}
|
||||
return ethash.hashrate.Rate1()
|
||||
}
|
||||
|
||||
|
|
|
@ -781,7 +781,7 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
db = ethdb.NewMemDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
GasLimit: 31415920,
|
||||
GasLimit: 700000000,
|
||||
Alloc: GenesisAlloc{
|
||||
addr1: {Balance: big.NewInt(1000000)},
|
||||
addr2: {Balance: big.NewInt(1000000)},
|
||||
|
@ -1068,7 +1068,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
funds = big.NewInt(1000000000)
|
||||
deleteAddr = common.Address{1}
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{ChainID: big.NewInt(10), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
|
||||
Config: ¶ms.ChainConfig{ChainId: big.NewInt(10), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
|
@ -1139,7 +1139,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
|
||||
// generate an invalid chain id transaction
|
||||
config := ¶ms.ChainConfig{ChainID: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
|
@ -1173,7 +1173,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
theAddr = common.Address{1}
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(10),
|
||||
ChainId: big.NewInt(10),
|
||||
HomesteadBlock: new(big.Int),
|
||||
EIP155Block: new(big.Int),
|
||||
EIP158Block: big.NewInt(2),
|
||||
|
@ -1189,7 +1189,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
||||
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
|
|
|
@ -161,6 +161,10 @@ func (c *stateObject) getTrie(db Database) Trie {
|
|||
return c.trie
|
||||
}
|
||||
|
||||
func (so *stateObject) storageRoot(db Database) common.Hash {
|
||||
return so.getTrie(db).Hash()
|
||||
}
|
||||
|
||||
// GetState returns a value in account storage.
|
||||
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
||||
value, exists := self.cachedStorage[key]
|
||||
|
|
|
@ -268,6 +268,15 @@ func (self *StateDB) HasSuicided(addr common.Address) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// GetStorageRoot returns the root of the storage associated with the given address.
|
||||
func (self *StateDB) GetStorageRoot(addr common.Address) (common.Hash, error) {
|
||||
so := self.getStateObject(addr)
|
||||
if so == nil {
|
||||
return common.Hash{}, fmt.Errorf("can't find state object")
|
||||
}
|
||||
return so.storageRoot(self.db), nil
|
||||
}
|
||||
|
||||
/*
|
||||
* SETTERS
|
||||
*/
|
||||
|
|
|
@ -117,6 +117,37 @@ func TestIntermediateLeaks(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStorageRoot(t *testing.T) {
|
||||
var (
|
||||
mem, _ = ethdb.NewMemDatabase()
|
||||
db = NewDatabase(mem)
|
||||
state, _ = New(common.Hash{}, db)
|
||||
addr = common.Address{1}
|
||||
key = common.Hash{1}
|
||||
value = common.Hash{42}
|
||||
|
||||
empty = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||
)
|
||||
|
||||
so := state.GetOrNewStateObject(addr)
|
||||
|
||||
emptyRoot := so.storageRoot(db)
|
||||
if emptyRoot != empty {
|
||||
t.Errorf("Invalid empty storage root, expected %x, got %x", empty, emptyRoot)
|
||||
}
|
||||
|
||||
// add a bit of state
|
||||
so.SetState(db, key, value)
|
||||
state.CommitTo(mem, false)
|
||||
|
||||
root := so.storageRoot(db)
|
||||
expected := common.HexToHash("63511abd258fa907afa30cb118b53744a4f49055bb3f531da512c6b866fc2ffb")
|
||||
|
||||
if expected != root {
|
||||
t.Errorf("Invalid storage root, expected %x, got %x", expected, root)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCopy tests that copying a statedb object indeed makes the original and
|
||||
// the copy independent of each other. This test is a regression test against
|
||||
// https://github.com/ethereum/go-ethereum/pull/15549.
|
||||
|
|
|
@ -253,6 +253,10 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
|
|||
if len(data) == 0 && isPrivate {
|
||||
return nil, 0, false, nil
|
||||
}
|
||||
//if input is empty for a private smart contract call, return
|
||||
if len(data) == 0 && isPrivate{
|
||||
return nil, new(big.Int), new(big.Int), false, nil
|
||||
}
|
||||
ret, st.gas, vmerr = evm.Call(sender, to, data, st.gas, st.value)
|
||||
}
|
||||
if vmerr != nil {
|
||||
|
|
|
@ -78,6 +78,10 @@ var (
|
|||
ErrOversizedData = errors.New("oversized data")
|
||||
|
||||
ErrInvalidGasPrice = errors.New("Gas price not 0")
|
||||
|
||||
// ErrEtherValueUnsupported is returned if a transaction specifies an Ether Value
|
||||
// for a private Quorum transaction.
|
||||
ErrEtherValueUnsupported = errors.New("ether value is not supported for private transactions")
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -587,6 +591,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
if pool.currentState.GetNonce(from) > tx.Nonce() {
|
||||
return ErrNonceTooLow
|
||||
}
|
||||
// Ether value is not currently supported on private transactions
|
||||
if tx.IsPrivate() && (tx.Value().Sign() != 0) {
|
||||
return ErrEtherValueUnsupported;
|
||||
}
|
||||
// Transactor should have enough funds to cover the costs
|
||||
// cost == V + GP * GL
|
||||
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
|
||||
|
@ -596,7 +604,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isQuorum && tx.Gas() < intrGas {
|
||||
if tx.Gas() < intrGas {
|
||||
return ErrIntrinsicGas
|
||||
}
|
||||
return nil
|
||||
|
@ -626,7 +634,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
|||
// If the transaction pool is full, discard underpriced transactions
|
||||
if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
|
||||
// If the new transaction is underpriced, don't accept it
|
||||
if !pool.chainconfig.IsQuorum && !local && pool.priced.Underpriced(tx, pool.locals) {
|
||||
if !pool.chainconfig.IsQuorum && pool.priced.Underpriced(tx, pool.locals) {
|
||||
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
|
||||
underpricedTxCounter.Inc(1)
|
||||
return false, ErrUnderpriced
|
||||
|
@ -760,7 +768,6 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
|
|||
return true
|
||||
}
|
||||
|
||||
|
||||
// AddLocal enqueues a single transaction into the pool if it is valid, marking
|
||||
// the sender as a local one in the mean time, ensuring it goes around the local
|
||||
// pricing constraints.
|
||||
|
@ -817,8 +824,8 @@ func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error {
|
|||
|
||||
// addTxsLocked attempts to queue a batch of transactions if they are valid,
|
||||
// whilst assuming the transaction pool lock is already held.
|
||||
func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) []error {
|
||||
// Add the batch of transaction, tracking the accepted ones
|
||||
func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) error {
|
||||
// Add the batch of transactions, tracking the accepted ones
|
||||
dirty := make(map[common.Address]struct{})
|
||||
errs := make([]error, len(txs))
|
||||
|
||||
|
@ -1120,7 +1127,7 @@ func (pool *TxPool) demoteUnexecutables() {
|
|||
log.Trace("Demoting pending transaction", "hash", hash)
|
||||
pool.enqueueTx(hash, tx)
|
||||
}
|
||||
// If there's a gap in front, alert (should never happen) and postpone all transactions
|
||||
// If there's a gap in front, warn (should never happen) and postpone all transactions
|
||||
if list.Len() > 0 && list.txs.Get(nonce) == nil {
|
||||
for _, tx := range list.Cap(0) {
|
||||
hash := tx.Hash()
|
||||
|
|
|
@ -19,13 +19,8 @@ package core
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
@ -33,6 +28,10 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"math/rand"
|
||||
"time"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// testTxPoolConfig is a transaction pool configuration without stateful disk
|
||||
|
@ -88,6 +87,17 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
|||
return pool, key
|
||||
}
|
||||
|
||||
func setupQuorumTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
blockchain := &testBlockChain{statedb, big.NewInt(1000000), new(event.Feed)}
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
pool := NewTxPool(testTxPoolConfig, params.QuorumTestChainConfig, blockchain)
|
||||
|
||||
return pool, key
|
||||
}
|
||||
|
||||
// validateTxPoolInternals checks various consistency invariants within the pool.
|
||||
func validateTxPoolInternals(pool *TxPool) error {
|
||||
pool.mu.RLock()
|
||||
|
@ -223,6 +233,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
//Test for transactions that are invalid on Ethereum
|
||||
func TestInvalidTransactions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -234,30 +245,64 @@ func TestInvalidTransactions(t *testing.T) {
|
|||
|
||||
pool.currentState.AddBalance(from, big.NewInt(1))
|
||||
if err := pool.AddRemote(tx); err != ErrInsufficientFunds {
|
||||
t.Error("expected", ErrInsufficientFunds)
|
||||
t.Error("expected", ErrInsufficientFunds, "; got", err)
|
||||
}
|
||||
|
||||
balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()))
|
||||
pool.currentState.AddBalance(from, balance)
|
||||
if err := pool.AddRemote(tx); err != ErrIntrinsicGas {
|
||||
t.Error("expected", ErrIntrinsicGas, "got", err)
|
||||
t.Error("expected", ErrIntrinsicGas, "; got", err)
|
||||
}
|
||||
|
||||
pool.currentState.SetNonce(from, 1)
|
||||
pool.currentState.AddBalance(from, big.NewInt(0xffffffffffffff))
|
||||
tx = transaction(0, 100000, key)
|
||||
if err := pool.AddRemote(tx); err != ErrNonceTooLow {
|
||||
t.Error("expected", ErrNonceTooLow)
|
||||
t.Error("expected", ErrNonceTooLow, "; got", err)
|
||||
}
|
||||
|
||||
tx = transaction(1, 100000, key)
|
||||
pool.gasPrice = big.NewInt(1000)
|
||||
if err := pool.AddRemote(tx); err != ErrUnderpriced {
|
||||
t.Error("expected", ErrUnderpriced, "got", err)
|
||||
t.Error("expected", ErrUnderpriced, "; got", err)
|
||||
}
|
||||
if err := pool.AddLocal(tx); err != nil {
|
||||
t.Error("expected", nil, "got", err)
|
||||
t.Error("expected", nil, "; got", err)
|
||||
}
|
||||
|
||||
tooMuchGas := big.NewInt(0).Add(pool.currentMaxGas, big.NewInt(1))
|
||||
tx1 := transaction(2, tooMuchGas, key)
|
||||
if err := pool.AddRemote(tx1); err != ErrGasLimit {
|
||||
t.Error("expected", ErrGasLimit, "; got", err)
|
||||
}
|
||||
|
||||
data := make([]byte, (32*1024)+1)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), data), types.HomesteadSigner{}, key)
|
||||
if err := pool.AddRemote(tx2); err != ErrOversizedData {
|
||||
t.Error("expected", ErrOversizedData, "; got", err)
|
||||
}
|
||||
|
||||
tx3, _ := types.SignTx(types.NewTransaction(1, common.Address{}, big.NewInt(100), common.Big0, big.NewInt(0), nil), types.HomesteadSigner{}, key)
|
||||
|
||||
balance = new(big.Int).Add(tx3.Value(), new(big.Int).Mul(tx3.Gas(), tx3.GasPrice()))
|
||||
from, _ = deriveSender(tx3)
|
||||
pool.currentState.AddBalance(from, balance)
|
||||
tx3.SetPrivate()
|
||||
if err := pool.AddRemote(tx3); err != ErrEtherValueUnsupported {
|
||||
t.Error("expected", ErrEtherValueUnsupported, "; got", err)
|
||||
}
|
||||
}
|
||||
|
||||
//Test for transactions that are only invalid on Quorum
|
||||
func TestQuorumInvalidTransactions(t *testing.T) {
|
||||
pool, key := setupQuorumTxPool()
|
||||
defer pool.Stop()
|
||||
|
||||
tx := transaction(0, common.Big0, key)
|
||||
if err := pool.AddRemote(tx); err != ErrInvalidGasPrice {
|
||||
t.Error("expected", ErrInvalidGasPrice, "; got", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTransactionQueue(t *testing.T) {
|
||||
|
|
|
@ -129,9 +129,7 @@ func isProtectedV(V *big.Int) bool {
|
|||
if V.BitLen() <= 8 {
|
||||
v := V.Uint64()
|
||||
// 27 / 28 are pre eip 155 -- ie unprotected.
|
||||
// TODO(joel): this is a hack. Everywhere else we maintain vanilla ethereum
|
||||
// compatibility and we should figure out how to extend that to here
|
||||
return !(v == 27 || v == 28 || v == 37 || v == 38)
|
||||
return !(v == 27 || v == 28)
|
||||
}
|
||||
// anything not 27 or 28 are considered unprotected
|
||||
return true
|
||||
|
|
27
eth/api.go
27
eth/api.go
|
@ -62,6 +62,33 @@ func (api *PublicEthereumAPI) Coinbase() (common.Address, error) {
|
|||
return api.Etherbase()
|
||||
}
|
||||
|
||||
// StorageRoot returns the storage root of an account on the the given (optional) block height.
|
||||
// If block number is not given the latest block is used.
|
||||
func (s *PublicEthereumAPI) StorageRoot(addr common.Address, blockNr *rpc.BlockNumber) (common.Hash, error) {
|
||||
var (
|
||||
pub, priv *state.StateDB
|
||||
err error
|
||||
)
|
||||
|
||||
if blockNr == nil || blockNr.Int64() == rpc.LatestBlockNumber.Int64() {
|
||||
pub, priv, err = s.e.blockchain.State()
|
||||
} else {
|
||||
if ch := s.e.blockchain.GetHeaderByNumber(uint64(blockNr.Int64())); ch != nil {
|
||||
pub, priv, err = s.e.blockchain.StateAt(ch.Root)
|
||||
} else {
|
||||
return common.Hash{}, fmt.Errorf("invalid block number")
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
if priv.Exist(addr) {
|
||||
return priv.GetStorageRoot(addr)
|
||||
}
|
||||
return pub.GetStorageRoot(addr)
|
||||
}
|
||||
// Hashrate returns the POW hashrate
|
||||
func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 {
|
||||
return hexutil.Uint64(api.e.Miner().HashRate())
|
||||
|
|
|
@ -361,7 +361,7 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs
|
|||
|
||||
var chainID *big.Int
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
|
||||
chainID = config.ChainID
|
||||
chainID = config.ChainId
|
||||
}
|
||||
return wallet.SignTxWithPassphrase(account, passwd, tx, chainID)
|
||||
}
|
||||
|
@ -392,6 +392,18 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
|||
args.Data = &d
|
||||
}
|
||||
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Assemble the transaction and sign with the wallet
|
||||
tx := args.toTransaction()
|
||||
|
||||
var chainID *big.Int
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) && !isPrivate {
|
||||
chainID = config.ChainId
|
||||
}
|
||||
|
||||
signed, err := s.signTransaction(ctx, args, passwd)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
@ -900,8 +912,9 @@ type RPCTransaction struct {
|
|||
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||
// representation, with the given location metadata set (if available).
|
||||
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction {
|
||||
var signer types.Signer = types.FrontierSigner{}
|
||||
if tx.Protected() {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
// joel: this is one of the two places we used a wrong signer to print txes
|
||||
if tx.Protected() && !tx.IsPrivate() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
@ -1077,8 +1090,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
|||
}
|
||||
receipt := receipts[index]
|
||||
|
||||
var signer types.Signer = types.FrontierSigner{}
|
||||
if tx.Protected() {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if tx.Protected() && !tx.IsPrivate() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
@ -1124,10 +1137,9 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
|
|||
}
|
||||
// Request the wallet to sign the transaction
|
||||
var chainID *big.Int
|
||||
isQuorum := false
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
|
||||
chainID = config.ChainID
|
||||
isQuorum = true
|
||||
isQuorum := tx.IsPrivate()
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) && !tx.IsPrivate() {
|
||||
chainID = config.ChainId
|
||||
}
|
||||
return wallet.SignTx(account, tx, chainID, isQuorum)
|
||||
}
|
||||
|
@ -1251,6 +1263,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
|||
|
||||
if isPrivate {
|
||||
data := []byte(*args.Data)
|
||||
//Send private transaction to local Constellation node
|
||||
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
|
||||
data, err = private.P.Send(data, args.PrivateFrom, args.PrivateFor)
|
||||
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
|
||||
|
@ -1271,10 +1284,9 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
|||
tx := args.toTransaction()
|
||||
|
||||
var chainID *big.Int
|
||||
isQuorum := false
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
|
||||
chainID = config.ChainID
|
||||
isQuorum = true
|
||||
isQuorum := tx.IsPrivate()
|
||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) && !isPrivate {
|
||||
chainID = config.ChainId
|
||||
}
|
||||
signed, err := wallet.SignTx(account, tx, chainID, isQuorum)
|
||||
if err != nil {
|
||||
|
@ -1367,7 +1379,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
|
|||
transactions := make([]*RPCTransaction, 0, len(pending))
|
||||
for _, tx := range pending {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if tx.Protected() {
|
||||
if tx.Protected() && !tx.IsPrivate() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
@ -1395,7 +1407,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
|
|||
|
||||
for _, p := range pending {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if p.Protected() {
|
||||
if p.Protected() && !p.IsPrivate() {
|
||||
signer = types.NewEIP155Signer(p.ChainId())
|
||||
}
|
||||
wantSigHash := signer.Hash(matchTx)
|
||||
|
|
|
@ -433,6 +433,12 @@ web3._extend({
|
|||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.utils.toHex]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'storageRoot',
|
||||
call: 'eth_storageRoot',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null]
|
||||
})
|
||||
],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
|
@ -655,6 +661,10 @@ web3._extend({
|
|||
name: 'removePeer',
|
||||
call: 'raft_removePeer',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'leader',
|
||||
getter: 'raft_leader'
|
||||
})
|
||||
]
|
||||
})
|
||||
|
|
|
@ -193,7 +193,6 @@ func (self *worker) pending() (*types.Block, *state.StateDB, *state.StateDB) {
|
|||
self.currentMu.Lock()
|
||||
defer self.currentMu.Unlock()
|
||||
return self.current.Block, self.current.state.Copy(), self.current.privateState.Copy()
|
||||
|
||||
}
|
||||
|
||||
func (self *worker) pendingBlock() *types.Block {
|
||||
|
@ -209,7 +208,6 @@ func (self *worker) pendingBlock() *types.Block {
|
|||
return self.current.Block
|
||||
}
|
||||
|
||||
|
||||
func (self *worker) start() {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
@ -327,15 +325,22 @@ func (self *worker) wait() {
|
|||
|
||||
// Update the block hash in all logs since it is now available and not when the
|
||||
// receipt/log of individual transactions were created.
|
||||
for _, r := range work.receipts {
|
||||
for _, r := range append(work.receipts, work.privateReceipts...) {
|
||||
for _, l := range r.Logs {
|
||||
l.BlockHash = block.Hash()
|
||||
}
|
||||
}
|
||||
for _, log := range work.state.Logs() {
|
||||
for _, log := range append(work.state.Logs(), work.privateState.Logs()...) {
|
||||
log.BlockHash = block.Hash()
|
||||
}
|
||||
stat, err := self.chain.WriteBlockWithState(block, work.receipts, work.state)
|
||||
|
||||
// write private transacions
|
||||
privateStateRoot, _ := work.privateState.CommitTo(self.chainDb, self.config.IsEIP158(block.Number()))
|
||||
core.WritePrivateStateRoot(self.chainDb, block.Root(), privateStateRoot)
|
||||
allReceipts := mergeReceipts(work.receipts, work.privateReceipts)
|
||||
|
||||
stat, err := self.chain.WriteBlockAndState(block, allReceipts, work.state)
|
||||
if err != nil {
|
||||
log.Error("Failed writing block to chain", "err", err)
|
||||
continue
|
||||
|
@ -344,7 +349,7 @@ func (self *worker) wait() {
|
|||
self.mux.Post(core.NewMinedBlockEvent{Block: block})
|
||||
var (
|
||||
events []interface{}
|
||||
logs = work.state.Logs()
|
||||
logs = append(work.state.Logs(), work.privateState.Logs()...)
|
||||
)
|
||||
events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
||||
if stat == core.CanonStatTy {
|
||||
|
@ -358,6 +363,27 @@ func (self *worker) wait() {
|
|||
}
|
||||
}
|
||||
|
||||
// Given a slice of public receipts and an overlapping (smaller) slice of
|
||||
// private receipts, return a new slice where the default for each location is
|
||||
// the public receipt but we take the private receipt in each place we have
|
||||
// one.
|
||||
func mergeReceipts(pub, priv types.Receipts) types.Receipts {
|
||||
m := make(map[common.Hash]*types.Receipt)
|
||||
for _, receipt := range pub {
|
||||
m[receipt.TxHash] = receipt
|
||||
}
|
||||
for _, receipt := range priv {
|
||||
m[receipt.TxHash] = receipt
|
||||
}
|
||||
|
||||
ret := make(types.Receipts, 0, len(pub))
|
||||
for _, pubReceipt := range pub {
|
||||
ret = append(ret, m[pubReceipt.TxHash])
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// push sends a new work task to currently live miner agents.
|
||||
func (self *worker) push(work *Work) {
|
||||
if atomic.LoadInt32(&self.mining) != 1 {
|
||||
|
@ -571,6 +597,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
|||
}
|
||||
// Start executing the transaction
|
||||
env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
|
||||
env.privateState.Prepare(tx.Hash(), common.Hash{}, env.tcount)
|
||||
|
||||
err, logs := env.commitTransaction(tx, bc, coinbase, env.gasPool)
|
||||
switch err {
|
||||
|
@ -641,6 +668,5 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, c
|
|||
logs = append(receipt.Logs, privateReceipt.Logs...)
|
||||
env.privateReceipts = append(env.privateReceipts, privateReceipt)
|
||||
}
|
||||
|
||||
return nil, logs
|
||||
}
|
||||
|
|
|
@ -24,16 +24,15 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// Genesis hashes to enforce below configs on.
|
||||
var (
|
||||
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||
TestnetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") // Mainnet genesis hash to enforce below configs on
|
||||
TestnetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet genesis hash to enforce below configs on
|
||||
)
|
||||
|
||||
var (
|
||||
// MainnetChainConfig is the chain parameters to run a node on the main network.
|
||||
MainnetChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(1150000),
|
||||
DAOForkBlock: big.NewInt(1920000),
|
||||
DAOForkSupport: true,
|
||||
|
@ -42,13 +41,13 @@ var (
|
|||
EIP155Block: big.NewInt(2675000),
|
||||
EIP158Block: big.NewInt(2675000),
|
||||
ByzantiumBlock: big.NewInt(4370000),
|
||||
ConstantinopleBlock: nil,
|
||||
|
||||
Ethash: new(EthashConfig),
|
||||
}
|
||||
|
||||
// TestnetChainConfig contains the chain parameters to run a node on the Ropsten test network.
|
||||
TestnetChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(3),
|
||||
ChainId: big.NewInt(3),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
|
@ -57,13 +56,13 @@ var (
|
|||
EIP155Block: big.NewInt(10),
|
||||
EIP158Block: big.NewInt(10),
|
||||
ByzantiumBlock: big.NewInt(1700000),
|
||||
ConstantinopleBlock: nil,
|
||||
|
||||
Ethash: new(EthashConfig),
|
||||
}
|
||||
|
||||
// RinkebyChainConfig contains the chain parameters to run a node on the Rinkeby test network.
|
||||
RinkebyChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(4),
|
||||
ChainId: big.NewInt(4),
|
||||
HomesteadBlock: big.NewInt(1),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
|
@ -72,7 +71,7 @@ var (
|
|||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(1035301),
|
||||
ConstantinopleBlock: nil,
|
||||
|
||||
Clique: &CliqueConfig{
|
||||
Period: 15,
|
||||
Epoch: 30000,
|
||||
|
@ -81,7 +80,7 @@ var (
|
|||
|
||||
// OttomanChainConfig contains the chain parameters to run a node on the Ottoman test network.
|
||||
OttomanChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(5),
|
||||
ChainId: big.NewInt(5),
|
||||
HomesteadBlock: big.NewInt(1),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
|
@ -90,7 +89,6 @@ var (
|
|||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(math.MaxInt64), // Don't enable yet
|
||||
ConstantinopleBlock: nil,
|
||||
|
||||
Istanbul: &IstanbulConfig{
|
||||
Epoch: 30000,
|
||||
|
@ -103,19 +101,19 @@ var (
|
|||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false}
|
||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false}
|
||||
|
||||
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
||||
// and accepted by the Ethereum core developers into the Clique consensus.
|
||||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false}
|
||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false}
|
||||
|
||||
TestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false}
|
||||
TestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), new(EthashConfig), nil, nil, false}
|
||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||
|
||||
QuorumTestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, nil, common.Hash{}, nil, nil, nil, nil, new(EthashConfig), nil, nil, true}
|
||||
QuorumTestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, nil, common.Hash{}, nil, nil, nil, new(EthashConfig), nil, nil, true}
|
||||
)
|
||||
|
||||
// ChainConfig is the core config which determines the blockchain settings.
|
||||
|
@ -124,7 +122,7 @@ var (
|
|||
// that any network, identified by its genesis block, can have its own
|
||||
// set of configuration options.
|
||||
type ChainConfig struct {
|
||||
ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection
|
||||
ChainId *big.Int `json:"chainId"` // Chain id identifies the current chain and is used for replay protection
|
||||
|
||||
HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead)
|
||||
|
||||
|
@ -139,7 +137,6 @@ type ChainConfig struct {
|
|||
EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block
|
||||
|
||||
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
|
||||
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
|
||||
|
||||
// Various consensus engines
|
||||
Ethash *EthashConfig `json:"ethash,omitempty"`
|
||||
|
@ -192,8 +189,8 @@ func (c *ChainConfig) String() string {
|
|||
default:
|
||||
engine = "unknown"
|
||||
}
|
||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v IsQuorum: %v Constantinople: %v Engine: %v}",
|
||||
c.ChainID,
|
||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v IsQuorum: %v Engine: %v}",
|
||||
c.ChainId,
|
||||
c.HomesteadBlock,
|
||||
c.DAOForkBlock,
|
||||
c.DAOForkSupport,
|
||||
|
@ -202,7 +199,6 @@ func (c *ChainConfig) String() string {
|
|||
c.EIP158Block,
|
||||
c.ByzantiumBlock,
|
||||
c.IsQuorum,
|
||||
c.ConstantinopleBlock,
|
||||
engine,
|
||||
)
|
||||
}
|
||||
|
@ -237,11 +233,6 @@ func (c *ChainConfig) IsByzantium(num *big.Int) bool {
|
|||
return isForked(c.ByzantiumBlock, num)
|
||||
}
|
||||
|
||||
// IsConstantinople returns whether num is either equal to the Constantinople fork block or greater.
|
||||
func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
|
||||
return isForked(c.ConstantinopleBlock, num)
|
||||
}
|
||||
|
||||
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
|
||||
//
|
||||
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
|
||||
|
@ -296,15 +287,12 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
|
|||
if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) {
|
||||
return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block)
|
||||
}
|
||||
if c.IsEIP158(head) && !configNumEqual(c.ChainID, newcfg.ChainID) {
|
||||
if c.IsEIP158(head) && !configNumEqual(c.ChainId, newcfg.ChainId) {
|
||||
return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block)
|
||||
}
|
||||
if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) {
|
||||
return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock)
|
||||
}
|
||||
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
|
||||
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -369,16 +357,16 @@ func (err *ConfigCompatError) Error() string {
|
|||
// Rules is a one time interface meaning that it shouldn't be used in between transition
|
||||
// phases.
|
||||
type Rules struct {
|
||||
ChainID *big.Int
|
||||
ChainId *big.Int
|
||||
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
||||
IsByzantium bool
|
||||
}
|
||||
|
||||
// Rules ensures c's ChainID is not nil.
|
||||
func (c *ChainConfig) Rules(num *big.Int) Rules {
|
||||
chainID := c.ChainID
|
||||
if chainID == nil {
|
||||
chainID = new(big.Int)
|
||||
chainId := c.ChainId
|
||||
if chainId == nil {
|
||||
chainId = new(big.Int)
|
||||
}
|
||||
return Rules{ChainID: new(big.Int).Set(chainID), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num)}
|
||||
return Rules{ChainId: new(big.Int).Set(chainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num)}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,30 @@
|
|||
package constellation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
type Constellation struct {
|
||||
node *Client
|
||||
c *cache.Cache
|
||||
isConstellationNotInUse bool
|
||||
}
|
||||
|
||||
var (
|
||||
ErrConstellationIsntInit = errors.New("Constellation not in use")
|
||||
)
|
||||
|
||||
func (g *Constellation) Send(data []byte, from string, to []string) (out []byte, err error) {
|
||||
if g.isConstellationNotInUse {
|
||||
return nil, ErrConstellationIsntInit
|
||||
}
|
||||
out, err = g.node.SendPayload(data, from, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -23,6 +34,9 @@ func (g *Constellation) Send(data []byte, from string, to []string) (out []byte,
|
|||
}
|
||||
|
||||
func (g *Constellation) Receive(data []byte) ([]byte, error) {
|
||||
if g.isConstellationNotInUse {
|
||||
return nil, nil
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return data, nil
|
||||
}
|
||||
|
@ -47,7 +61,7 @@ func New(path string) (*Constellation, error) {
|
|||
}
|
||||
// We accept either the socket or a configuration file that points to
|
||||
// a socket.
|
||||
isSocket := info.Mode() & os.ModeSocket != 0
|
||||
isSocket := info.Mode()&os.ModeSocket != 0
|
||||
if !isSocket {
|
||||
cfg, err := LoadConfig(path)
|
||||
if err != nil {
|
||||
|
@ -66,10 +80,18 @@ func New(path string) (*Constellation, error) {
|
|||
return &Constellation{
|
||||
node: n,
|
||||
c: cache.New(5*time.Minute, 5*time.Minute),
|
||||
isConstellationNotInUse: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func MustNew(path string) *Constellation {
|
||||
if strings.EqualFold(path, "ignore") {
|
||||
return &Constellation{
|
||||
node: nil,
|
||||
c: nil,
|
||||
isConstellationNotInUse: true,
|
||||
}
|
||||
}
|
||||
g, err := New(path)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("MustNew: Failed to connect to Constellation (%s): %v", path, err))
|
||||
|
|
|
@ -29,3 +29,11 @@ func (s *PublicRaftAPI) AddPeer(enodeId string) (uint16, error) {
|
|||
func (s *PublicRaftAPI) RemovePeer(raftId uint16) {
|
||||
s.raftService.raftProtocolManager.ProposePeerRemoval(raftId)
|
||||
}
|
||||
|
||||
func (s *PublicRaftAPI) Leader() (string, error) {
|
||||
addr, err := s.raftService.raftProtocolManager.LeaderAddress()
|
||||
if nil != err {
|
||||
return "", err
|
||||
}
|
||||
return addr.nodeId.String(), nil
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ Consider the following example where this might occur, where Raft entries attemp
|
|||
|
||||
`[ 0xbeda Parent: 0xacaa ]`
|
||||
|
||||
Where `0xbeda` is the ID of new block, and `0xaa` is the ID of its parent. Here, the initial minter (node 1) is partitioned, and node 2 takes over as the minter.
|
||||
Where `0xbeda` is the ID of new block, and `0xacaa` is the ID of its parent. Here, the initial minter (node 1) is partitioned, and node 2 takes over as the minter.
|
||||
|
||||
```
|
||||
time block submissions
|
||||
|
@ -89,7 +89,7 @@ Where `0xbeda` is the ID of new block, and `0xaa` is the ID of its parent. Here,
|
|||
|
|
||||
| -- 1 rejoins --
|
||||
|
|
||||
v [ 0x8b37 Parent: 0x8b37 ]
|
||||
v [ 0x8b37 Parent: 0x839c ]
|
||||
```
|
||||
|
||||
Once the partition heals, at the Raft layer node1 will resubmit `0x2c52`, and the resulting serialized log might look as follows:
|
||||
|
@ -99,7 +99,7 @@ Once the partition heals, at the Raft layer node1 will resubmit `0x2c52`, and th
|
|||
[ 0xf0ec Parent: 0xbeda - Extends! ] (due to node 2; let's call this the "winner")
|
||||
[ 0x839c Parent: 0xf0ec - Extends! ] (due to node 2)
|
||||
[ 0x2c52 Parent: 0xbeda - NO-OP. ] (due to node 1; let's call this the "loser")
|
||||
[ 0x8b37 Parent: 0x8b37 - Extends! ] (due to node 2)
|
||||
[ 0x8b37 Parent: 0x839c - Extends! ] (due to node 2)
|
||||
```
|
||||
|
||||
Due to being serialized after the "winner," the "loser" entry will not extend the chain, because its parent (`0xbeda`) is no longer at the head of the chain when we apply the entry. The "winner" extended the same parent (`0xbeda`) earlier (and then `0x839c` extended it further.)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package raft
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -50,6 +51,7 @@ type ProtocolManager struct {
|
|||
snapshotIndex uint64 // The index of the latest snapshot.
|
||||
|
||||
// Remote peer state (protected by mu vs concurrent access via JS)
|
||||
leader uint16
|
||||
peers map[uint16]*Peer
|
||||
removedPeers *set.Set // *Permanently removed* peers
|
||||
|
||||
|
@ -101,6 +103,7 @@ func NewProtocolManager(raftId uint16, raftPort uint16, blockchain *core.BlockCh
|
|||
manager := &ProtocolManager{
|
||||
bootstrapNodes: bootstrapNodes,
|
||||
peers: make(map[uint16]*Peer),
|
||||
leader: uint16(etcdRaft.None),
|
||||
removedPeers: set.New(),
|
||||
joinExisting: joinExisting,
|
||||
blockchain: blockchain,
|
||||
|
@ -680,6 +683,10 @@ func (pm *ProtocolManager) eventLoop() {
|
|||
case rd := <-pm.rawNode().Ready():
|
||||
pm.wal.Save(rd.HardState, rd.Entries)
|
||||
|
||||
if rd.SoftState != nil {
|
||||
pm.updateLeader(rd.SoftState.Lead)
|
||||
}
|
||||
|
||||
if snap := rd.Snapshot; !etcdRaft.IsEmptySnap(snap) {
|
||||
pm.saveRaftSnapshot(snap)
|
||||
pm.applyRaftSnapshot(snap)
|
||||
|
@ -881,3 +888,24 @@ func (pm *ProtocolManager) advanceAppliedIndex(index uint64) {
|
|||
pm.appliedIndex = index
|
||||
pm.mu.Unlock()
|
||||
}
|
||||
|
||||
func (pm *ProtocolManager) updateLeader(leader uint64) {
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
|
||||
pm.leader = uint16(leader)
|
||||
}
|
||||
|
||||
// The Address for the current leader, or an error if no leader is elected.
|
||||
func (pm *ProtocolManager) LeaderAddress() (*Address, error) {
|
||||
pm.mu.RLock()
|
||||
defer pm.mu.RUnlock()
|
||||
|
||||
if minterRole == pm.role {
|
||||
return pm.address, nil
|
||||
} else if l, ok := pm.peers[pm.leader]; ok {
|
||||
return l.address, nil
|
||||
}
|
||||
// We expect to reach this if pm.leader is 0, which is how etcd denotes the lack of a leader.
|
||||
return nil, errors.New("no leader is currently elected")
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func (chain *speculativeChain) setHead(block *types.Block) {
|
|||
chain.head = block
|
||||
}
|
||||
|
||||
// Accept this block, removing it from the head of the speculative chain
|
||||
// Accept this block, removing it from the speculative chain
|
||||
func (chain *speculativeChain) accept(acceptedBlock *types.Block) {
|
||||
earliestProposedI := chain.unappliedBlocks.Shift()
|
||||
var earliestProposed *types.Block
|
||||
|
@ -63,7 +63,16 @@ func (chain *speculativeChain) accept(acceptedBlock *types.Block) {
|
|||
earliestProposed = earliestProposedI.(*types.Block)
|
||||
}
|
||||
|
||||
if expectedBlock := earliestProposed == nil || earliestProposed.Hash() == acceptedBlock.Hash(); expectedBlock {
|
||||
// There are three possible scenarios:
|
||||
// 1. We don't have a record of this block (or any proposed blocks), meaning someone else minted it and we should
|
||||
// add it as the new head of our speculative chain. New blocks from the old leader are still coming in.
|
||||
// 2. This block was the first outstanding one we proposed.
|
||||
// 3. This block is different from the block we proposed, (also) meaning new blocks are still coming in from the old
|
||||
// leader, but unlike the first scenario, we need to clear all of the speculative chain state because the
|
||||
// `acceptedBlock` takes precedence over our speculative state.
|
||||
if earliestProposed == nil {
|
||||
chain.head = acceptedBlock
|
||||
} else if expectedBlock := earliestProposed.Hash() == acceptedBlock.Hash(); expectedBlock {
|
||||
// Remove the txes in this accepted block from our blacklist.
|
||||
chain.removeProposedTxes(acceptedBlock)
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue