mirror of https://github.com/poanetwork/quorum.git
Squashed commits.
* Remove old note about testing with constellation. * Review: forceParseRfc3339 -> mustParseRfc3339 * Add isQuorum parameter to SignTx. * Re-enable TestLogReorgs. * Fix `SignTx` calls in executables other than geth. * Bring vm closer to public ethereum implementation. * Restore all original Ethereum tests. * Update for new event interface. * Tweak core / EVM. * Improve "TX failed" error message. * Fix "err creating contract" bug. * Don't log "invalid mix digest" in Quorum. * Fix public / private bug. A private contract calling a method on a public contract would create a ghost of the private contract on public state due to a value transfer of 0. * Clearer naming in minter / work.commitTransactions. * Remove unused log import. * Remove unused `enableQuorumChecks` flag.
This commit is contained in:
parent
3f18431248
commit
875c10b2d3
27
HACKING.md
27
HACKING.md
|
@ -1,32 +1,5 @@
|
|||
# Hacking on Quorum / various notes
|
||||
|
||||
## Testing with Constellation
|
||||
|
||||
### `tm.conf`
|
||||
|
||||
Replace with appropriate absolute paths:
|
||||
|
||||
TODO(joel): figure out how to use relative paths
|
||||
|
||||
```
|
||||
url = "http://127.0.0.1:9000/"
|
||||
port = 9000
|
||||
socket = "/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/tm.ipc"
|
||||
othernodes = []
|
||||
storage = "/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/constellation"
|
||||
publickeys = ["/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/test.pub"]
|
||||
privatekeys = ["/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/test.key"]
|
||||
```
|
||||
|
||||
Run constellation:
|
||||
|
||||
```
|
||||
> mkdir qdata
|
||||
> constellation-node tm.conf
|
||||
```
|
||||
|
||||
Now you should be able to run the private state tests as well: `env PRIVATE_CONFIG=(pwd)/tm.conf go test ./...`.
|
||||
|
||||
## How does private state work?
|
||||
|
||||
Let's look at the EVM structure:
|
||||
|
|
|
@ -111,7 +111,7 @@ type Wallet interface {
|
|||
// about which fields or actions are needed. The user may retry by providing
|
||||
// the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
|
||||
// the account in a keystore).
|
||||
SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
|
||||
SignTx(account Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error)
|
||||
|
||||
// SignHashWithPassphrase requests the wallet to sign the given hash with the
|
||||
// given passphrase as extra authentication information.
|
||||
|
|
|
@ -268,7 +268,7 @@ func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
// SignTx signs the given transaction with the requested account.
|
||||
func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error) {
|
||||
// Look up the key to sign with and abort if it cannot be found
|
||||
ks.mu.RLock()
|
||||
defer ks.mu.RUnlock()
|
||||
|
@ -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 { // && !params.IsQuorum {
|
||||
if chainID != nil && !isQuorum {
|
||||
return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
|
||||
}
|
||||
return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
|
||||
|
|
|
@ -98,7 +98,7 @@ func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte
|
|||
// with the given account. If the wallet does not wrap this particular account,
|
||||
// an error is returned to avoid account leakage (even though in theory we may
|
||||
// be able to sign via our shared keystore backend).
|
||||
func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error) {
|
||||
// Make sure the requested account is contained within
|
||||
if account.Address != w.account.Address {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
|
@ -107,7 +107,7 @@ func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction,
|
|||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
// Account seems valid, request the keystore to sign
|
||||
return w.keystore.SignTx(account, tx, chainID)
|
||||
return w.keystore.SignTx(account, tx, chainID, isQuorum)
|
||||
}
|
||||
|
||||
// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
@ -508,10 +509,14 @@ func (w *wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error)
|
|||
// Note, if the version of the Ethereum application running on the Ledger wallet is
|
||||
// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
|
||||
// will be returned opposed to silently signing in Homestead mode.
|
||||
func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int, isQuorum bool) (*types.Transaction, error) {
|
||||
w.stateLock.RLock() // Comms have own mutex, this is for the state fields
|
||||
defer w.stateLock.RUnlock()
|
||||
|
||||
if isQuorum {
|
||||
return nil, errors.New("Signing Quorum transactions with a USB wallet not yet supported")
|
||||
}
|
||||
|
||||
// If the wallet is closed, abort
|
||||
if w.device == nil {
|
||||
return nil, accounts.ErrWalletClosed
|
||||
|
@ -558,5 +563,5 @@ func (w *wallet) SignHashWithPassphrase(account accounts.Account, passphrase str
|
|||
// transaction with the given account using passphrase as extra authentication.
|
||||
// Since USB wallets don't rely on passphrases, these are silently ignored.
|
||||
func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
return w.SignTx(account, tx, chainID)
|
||||
return w.SignTx(account, tx, chainID, false)
|
||||
}
|
||||
|
|
|
@ -446,7 +446,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
|
|||
amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil))
|
||||
|
||||
tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, big.NewInt(21000), f.price, nil)
|
||||
signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainId)
|
||||
signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainId, false)
|
||||
if err != nil {
|
||||
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
|
||||
f.lock.Unlock()
|
||||
|
|
|
@ -30,7 +30,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
set "gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
@ -40,7 +39,7 @@ var (
|
|||
blockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
|
||||
maxUncles = 2 // Maximum number of uncles allowed in a single block
|
||||
|
||||
nanosecond2017Timestamp = forceParseRfc3339("2017-01-01T00:00:00+00:00").UnixNano()
|
||||
nanosecond2017Timestamp = mustParseRfc3339("2017-01-01T00:00:00+00:00").UnixNano()
|
||||
)
|
||||
|
||||
// Various error messages to mark blocks invalid. These should be private to
|
||||
|
@ -60,7 +59,7 @@ var (
|
|||
errInvalidPoW = errors.New("invalid proof-of-work")
|
||||
)
|
||||
|
||||
func forceParseRfc3339(str string) time.Time {
|
||||
func mustParseRfc3339(str string) time.Time {
|
||||
time, err := time.Parse(time.RFC3339, str)
|
||||
if err != nil {
|
||||
panic("unexpected failure to parse rfc3339 timestamp: " + str)
|
||||
|
@ -505,13 +504,8 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
|
|||
size = 32 * 1024
|
||||
}
|
||||
digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64())
|
||||
if !bytes.Equal(header.MixDigest[:], digest) {
|
||||
if isQuorum {
|
||||
log.Info("invalid mix digest", "calculated", fmt.Sprintf("%x", digest), "in header", fmt.Sprintf("%x", header.MixDigest[:]))
|
||||
} else {
|
||||
return errInvalidMixDigest
|
||||
}
|
||||
|
||||
if !isQuorum && !bytes.Equal(header.MixDigest[:], digest) {
|
||||
return errInvalidMixDigest
|
||||
}
|
||||
target := new(big.Int).Div(maxUint256, header.Difficulty)
|
||||
if new(big.Int).SetBytes(result).Cmp(target) > 0 {
|
||||
|
|
|
@ -35,8 +35,6 @@ type BlockValidator struct {
|
|||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
engine consensus.Engine // Consensus engine used for validating
|
||||
|
||||
enableQuorumChecks bool // indication if the signature and vote count is checked (disabled for testing purposes)
|
||||
}
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
|
@ -102,43 +100,6 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
|
|||
return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
|
||||
}
|
||||
|
||||
// TODO(joel)
|
||||
/*
|
||||
if v.enableQuorumChecks {
|
||||
// Ensure that the parent block was indeed the one that was voted for in the state of this block.
|
||||
// The contract enforces that there are enough votes and only votes from parties that are allowed to vote.
|
||||
var (
|
||||
gp = new(GasPool).AddGas(common.MaxBig)
|
||||
to = common.HexToAddress("0x0000000000000000000000000000000000000020")
|
||||
stateCopy = statedb.Copy()
|
||||
msg = callmsg{
|
||||
from: stateCopy.GetOrNewStateObject(common.HexToAddress("0x0000000000000000000000000000000000000000")),
|
||||
to: &to,
|
||||
gas: big.NewInt(500000),
|
||||
gasPrice: common.Big0,
|
||||
value: common.Big0,
|
||||
data: common.Hex2Bytes(fmt.Sprintf("559c390c%064x", block.Number())), // call getCanonHash(uint256)
|
||||
}
|
||||
vmenv = NewEnv(stateCopy, stateCopy, v.config, v.bc, msg, block.Header(), v.config.VmConfig)
|
||||
)
|
||||
|
||||
result, _, _, err := NewStateTransition(vmenv, msg, gp).TransitionDb()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// result holds the hash that was the winning hash according the voting contract
|
||||
parentHash := common.BytesToHash(result)
|
||||
if parentHash == (common.Hash{}) {
|
||||
// too little votes
|
||||
return fmt.Errorf("block parent could not be verified, ignore block (%d)", block.Number())
|
||||
}
|
||||
if block.ParentHash() != parentHash {
|
||||
return fmt.Errorf("build on top of unexpected parent, expected %s, got %s", parentHash.Hex(), block.ParentHash().Hex())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -118,8 +118,7 @@ type BlockChain struct {
|
|||
|
||||
badBlocks *lru.Cache // Bad block cache
|
||||
|
||||
privateStateCache state.Database // Private state database to reuse between imports (contains state cache)
|
||||
chainEvents chan interface{} // Serialized chain insertion events
|
||||
privateStateCache state.Database // Private state database to reuse between imports (contains state cache)
|
||||
}
|
||||
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
|
@ -146,7 +145,6 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
|
|||
badBlocks: badBlocks,
|
||||
|
||||
privateStateCache: state.NewDatabase(chainDb),
|
||||
chainEvents: make(chan interface{}, 20), // Buffered for async publishing
|
||||
}
|
||||
bc.SetValidator(NewBlockValidator(config, bc, engine))
|
||||
bc.SetProcessor(NewStateProcessor(config, bc, engine))
|
||||
|
@ -1094,12 +1092,14 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
stats.report(chain, i)
|
||||
}
|
||||
|
||||
// TODO(joel/bryan/quorum):
|
||||
//
|
||||
// This should remain *synchronous* so that we can control ordering of
|
||||
// ChainHeadEvents. This is important for supporting low latency
|
||||
// (non-Proof-of-Work) consensus mechanisms.
|
||||
//
|
||||
bc.PostChainEvents(events, coalescedLogs)
|
||||
// We currently deadlock when running this synchronously. Fix.
|
||||
go bc.PostChainEvents(events, coalescedLogs)
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
|
@ -856,7 +857,6 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestLogReorgs(t *testing.T) {
|
||||
|
||||
var (
|
||||
|
@ -986,7 +986,6 @@ done:
|
|||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// Tests if the canonical block can be fetched from the database during chain insertion.
|
||||
func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
|
|
|
@ -77,7 +77,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb, privateState *stat
|
|||
|
||||
receipt, privateReceipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, privateState, header, tx, totalUsedGas, cfg)
|
||||
if err != nil {
|
||||
return nil, nil, nil, totalUsedGas, err // TODO(joel) s/totalUsedGas/nil ?
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
receipts = append(receipts, receipt)
|
||||
allLogs = append(allLogs, receipt.Logs...)
|
||||
|
@ -100,7 +100,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb, privateState *stat
|
|||
// for the transaction, gas used and an error if the transaction failed,
|
||||
// indicating the block was invalid.
|
||||
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb, privateState *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *types.Receipt, *big.Int, error) {
|
||||
if !tx.IsPrivate() {
|
||||
if !config.IsQuorum || !tx.IsPrivate() {
|
||||
privateState = statedb
|
||||
}
|
||||
|
||||
|
|
|
@ -519,13 +519,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
if !isQuorum && !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
|
||||
return ErrUnderpriced
|
||||
}
|
||||
|
||||
// Ensure the transaction adheres to nonce ordering
|
||||
currentState, _, err := pool.blockChain.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if currentState.GetNonce(from) > tx.Nonce() {
|
||||
return ErrNonceTooLow
|
||||
}
|
||||
|
|
|
@ -29,6 +29,48 @@ import (
|
|||
|
||||
// from bcValidBlockTest.json, "SimpleTx"
|
||||
func TestBlockEncoding(t *testing.T) {
|
||||
blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
|
||||
var block Block
|
||||
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
|
||||
t.Fatal("decode error: ", err)
|
||||
}
|
||||
|
||||
check := func(f string, got, want interface{}) {
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s mismatch: got %v, want %v", f, got, want)
|
||||
}
|
||||
}
|
||||
check("Difficulty", block.Difficulty(), big.NewInt(131072))
|
||||
check("GasLimit", block.GasLimit(), big.NewInt(3141592))
|
||||
check("GasUsed", block.GasUsed(), big.NewInt(21000))
|
||||
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
|
||||
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
|
||||
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
|
||||
check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
|
||||
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
|
||||
check("Time", block.Time(), big.NewInt(1426516743))
|
||||
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
|
||||
|
||||
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
|
||||
|
||||
tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
|
||||
fmt.Println(block.Transactions()[0].Hash())
|
||||
fmt.Println(tx1.data)
|
||||
fmt.Println(tx1.Hash())
|
||||
check("len(Transactions)", len(block.Transactions()), 1)
|
||||
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
|
||||
|
||||
ourBlockEnc, err := rlp.EncodeToBytes(&block)
|
||||
if err != nil {
|
||||
t.Fatal("encode error: ", err)
|
||||
}
|
||||
if !bytes.Equal(ourBlockEnc, blockEnc) {
|
||||
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBlockEncoding from the original Quorum implementation
|
||||
func TestBlockEncoding2(t *testing.T) {
|
||||
blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a08bba9707f73a6c9801825706d0814d4ae310e4ff0a16fcd94e72174a176930e4a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f808082c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
|
||||
var block Block
|
||||
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
|
||||
|
|
|
@ -39,6 +39,18 @@ var (
|
|||
)
|
||||
|
||||
rightvrsTx, _ = NewTransaction(
|
||||
3,
|
||||
common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
|
||||
big.NewInt(10),
|
||||
big.NewInt(2000),
|
||||
big.NewInt(1),
|
||||
common.FromHex("5544"),
|
||||
).WithSignature(
|
||||
HomesteadSigner{},
|
||||
common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
|
||||
)
|
||||
|
||||
rightvrsTx2, _ = NewTransaction(
|
||||
3,
|
||||
common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
|
||||
big.NewInt(10),
|
||||
|
@ -55,7 +67,7 @@ func TestTransactionSigHash(t *testing.T) {
|
|||
if emptyTx.SigHash(HomesteadSigner{}) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
|
||||
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
|
||||
}
|
||||
if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("c75e06c2a1b4e254e869653871436fdfa752fd613152b474e6dd36b73a13dae2") {
|
||||
if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
|
||||
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +77,28 @@ func TestTransactionEncode(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("encode error: %v", err)
|
||||
}
|
||||
should := common.FromHex("f86103018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3")
|
||||
if !bytes.Equal(txb, should) {
|
||||
t.Errorf("encoded RLP mismatch, got %x", txb)
|
||||
}
|
||||
}
|
||||
|
||||
// Test from the original quorum implementation
|
||||
func TestTransactionSigHash2(t *testing.T) {
|
||||
if emptyTx.SigHash(HomesteadSigner{}) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
|
||||
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
|
||||
}
|
||||
if rightvrsTx2.SigHash(HomesteadSigner{}) != common.HexToHash("c75e06c2a1b4e254e869653871436fdfa752fd613152b474e6dd36b73a13dae2") {
|
||||
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx2.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
// Test from the original quorum implementation
|
||||
func TestTransactionEncode2(t *testing.T) {
|
||||
txb, err := rlp.EncodeToBytes(rightvrsTx2)
|
||||
if err != nil {
|
||||
t.Fatalf("encode error: %v", err)
|
||||
}
|
||||
should := common.FromHex("f86103808207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3")
|
||||
if !bytes.Equal(txb, should) {
|
||||
t.Errorf("encoded RLP mismatch, got %x", txb)
|
||||
|
|
|
@ -24,4 +24,6 @@ var (
|
|||
ErrDepth = errors.New("max call depth exceeded")
|
||||
ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
|
||||
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||
|
||||
ErrReadOnlyValueTransfer = errors.New("VM in read-only mode. Value transfer prohibited.")
|
||||
)
|
||||
|
|
130
core/vm/evm.go
130
core/vm/evm.go
|
@ -26,6 +26,17 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// note: Quorum, States, and Value Transfer
|
||||
//
|
||||
// In Quorum there is a tricky issue in one specific case when there is call from private state to public state:
|
||||
// * The state db is selected based on the callee (public)
|
||||
// * With every call there is an associated value transfer -- in our case this is 0
|
||||
// * Thus, there is an implicit transfer of 0 value from the caller to callee on the public state
|
||||
// * However in our scenario the caller is private
|
||||
// * Thus, the transfer creates a ghost of the private account on the public state with no value, code, or storage
|
||||
//
|
||||
// The solution is to skip this transfer of 0 value under Quorum
|
||||
|
||||
type (
|
||||
CanTransferFunc func(StateDB, common.Address, *big.Int) bool
|
||||
TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
|
||||
|
@ -110,8 +121,10 @@ type EVM struct {
|
|||
privateState PrivateState
|
||||
states [1027]*state.StateDB // TODO(joel) we should be able to get away with 1024 or maybe 1025
|
||||
currentStateDepth uint
|
||||
readOnly bool
|
||||
readOnlyDepth uint
|
||||
// This flag has different semantics from the `Interpreter:readOnly` flag (though they interact and could maybe
|
||||
// be simplified). This is set by Quorum when it's inside a Private State -> Public State read.
|
||||
quorumReadOnly bool
|
||||
readOnlyDepth uint
|
||||
}
|
||||
|
||||
// NewEVM retutrns a new EVM . The returned EVM is not thread safe and should
|
||||
|
@ -161,33 +174,32 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
return nil, gas, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
// TODO(joel) there's still some work to untangle this
|
||||
var createAccount bool
|
||||
if addr == (common.Address{}) {
|
||||
addr = createAddressAndIncrementNonce(evm, caller)
|
||||
createAccount = true
|
||||
}
|
||||
|
||||
var (
|
||||
to = AccountRef(addr)
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
)
|
||||
if createAccount {
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
} else {
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
precompiles := PrecompiledContractsHomestead
|
||||
if evm.ChainConfig().IsMetropolis(evm.BlockNumber) {
|
||||
precompiles = PrecompiledContractsMetropolis
|
||||
}
|
||||
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
|
||||
return nil, gas, nil
|
||||
}
|
||||
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
precompiles := PrecompiledContractsHomestead
|
||||
if evm.ChainConfig().IsMetropolis(evm.BlockNumber) {
|
||||
precompiles = PrecompiledContractsMetropolis
|
||||
}
|
||||
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
|
||||
return nil, gas, nil
|
||||
}
|
||||
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
}
|
||||
if evm.ChainConfig().IsQuorum {
|
||||
// skip transfer if value /= 0 (see note: Quorum, States, and Value Transfer)
|
||||
if value.Sign() != 0 {
|
||||
if evm.quorumReadOnly {
|
||||
return nil, gas, ErrReadOnlyValueTransfer
|
||||
}
|
||||
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
||||
}
|
||||
} else {
|
||||
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
||||
}
|
||||
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
|
@ -228,11 +240,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
|||
return nil, gas, ErrDepth
|
||||
}
|
||||
// Fail if we're trying to transfer more than the available balance
|
||||
if !evm.CanTransfer(caller.Address(), value) {
|
||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
// TODO(joel) the old version did createAccount / createAddressAndIncrementNonce like Call, but I think unnecessary?
|
||||
var (
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
to = AccountRef(caller.Address())
|
||||
|
@ -344,18 +355,46 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
|||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, common.Address{}, gas, ErrDepth
|
||||
}
|
||||
if !evm.CanTransfer(caller.Address(), value) {
|
||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
return nil, common.Address{}, gas, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
contractAddr = createAddressAndIncrementNonce(evm, caller)
|
||||
// Get the right state in case of a dual state environment. If a sender
|
||||
// is a transaction (depth == 0) use the public state to derive the address
|
||||
// and increment the nonce of the public state. If the sender is a contract
|
||||
// (depth > 0) use the private state to derive the nonce and increment the
|
||||
// nonce on the private state only.
|
||||
//
|
||||
// If the transaction went to a public contract the private and public state
|
||||
// are the same.
|
||||
var creatorStateDb StateDB
|
||||
if evm.Depth() > 0 {
|
||||
creatorStateDb = evm.privateState
|
||||
} else {
|
||||
creatorStateDb = evm.publicState
|
||||
}
|
||||
|
||||
// Create a new account on the state
|
||||
nonce := creatorStateDb.GetNonce(caller.Address())
|
||||
creatorStateDb.SetNonce(caller.Address(), nonce+1)
|
||||
|
||||
snapshot := evm.StateDB.Snapshot()
|
||||
contractAddr = crypto.CreateAddress(caller.Address(), nonce)
|
||||
evm.StateDB.CreateAccount(contractAddr)
|
||||
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
|
||||
evm.StateDB.SetNonce(contractAddr, 1)
|
||||
}
|
||||
evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value)
|
||||
if evm.ChainConfig().IsQuorum {
|
||||
// skip transfer if value /= 0 (see note: Quorum, States, and Value Transfer)
|
||||
if value.Sign() != 0 {
|
||||
if evm.quorumReadOnly {
|
||||
return nil, common.Address{}, gas, ErrReadOnlyValueTransfer
|
||||
}
|
||||
evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value)
|
||||
}
|
||||
} else {
|
||||
evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value)
|
||||
}
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped evmironment for this execution context
|
||||
|
@ -416,36 +455,11 @@ func getDualState(env *EVM, addr common.Address) StateDB {
|
|||
return state
|
||||
}
|
||||
|
||||
// createAddressAndIncrementNonce returns an address based on the caller address and nonce.
|
||||
//
|
||||
// It also gets the right state in case of a dual state environment. If a sender
|
||||
// is a transaction (depth == 0) use the public state to derive the address
|
||||
// and increment the nonce of the public state. If the sender is a contract
|
||||
// (depth > 0) use the private state to derive the nonce and increment the
|
||||
// nonce on the private state only.
|
||||
//
|
||||
// If the transaction went to a public contract the private and public state
|
||||
// are the same.
|
||||
func createAddressAndIncrementNonce(env *EVM, caller ContractRef) common.Address {
|
||||
var db StateDB
|
||||
// check for a dual state in case of quorum.
|
||||
if env.Depth() > 0 {
|
||||
db = env.privateState
|
||||
} else {
|
||||
db = env.publicState
|
||||
}
|
||||
// Increment the callers nonce on the state based on the current depth
|
||||
nonce := db.GetNonce(caller.Address())
|
||||
db.SetNonce(caller.Address(), nonce+1)
|
||||
|
||||
return crypto.CreateAddress(caller.Address(), nonce)
|
||||
}
|
||||
|
||||
func (env *EVM) PublicState() PublicState { return env.publicState }
|
||||
func (env *EVM) PrivateState() PrivateState { return env.privateState }
|
||||
func (env *EVM) Push(statedb StateDB) {
|
||||
if env.privateState != statedb {
|
||||
env.readOnly = true
|
||||
env.quorumReadOnly = true
|
||||
env.readOnlyDepth = env.currentStateDepth
|
||||
}
|
||||
|
||||
|
@ -458,18 +472,14 @@ func (env *EVM) Push(statedb StateDB) {
|
|||
}
|
||||
func (env *EVM) Pop() {
|
||||
env.currentStateDepth--
|
||||
if env.readOnly && env.currentStateDepth == env.readOnlyDepth {
|
||||
env.readOnly = false
|
||||
if env.quorumReadOnly && env.currentStateDepth == env.readOnlyDepth {
|
||||
env.quorumReadOnly = false
|
||||
}
|
||||
env.StateDB = env.states[env.currentStateDepth-1]
|
||||
}
|
||||
|
||||
func (env *EVM) Depth() int { return env.depth }
|
||||
|
||||
func (self *EVM) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||
return self.StateDB.GetBalance(from).Cmp(balance) >= 0
|
||||
}
|
||||
|
||||
// We only need to revert the current state because when we call from private
|
||||
// public state it's read only, there wouldn't be anything to reset.
|
||||
// (A)->(B)->C->(B): A failure in (B) wouldn't need to reset C, as C was flagged
|
||||
|
|
|
@ -154,7 +154,7 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
|
|||
// Get the memory location of pc
|
||||
op = contract.GetOp(pc)
|
||||
|
||||
if in.evm.readOnly && op.isMutating() {
|
||||
if in.evm.quorumReadOnly && op.isMutating() {
|
||||
return nil, fmt.Errorf("VM in read-only mode. Mutating opcode prohibited")
|
||||
}
|
||||
|
||||
|
|
|
@ -521,6 +521,7 @@ func StringToOp(str string) OpCode {
|
|||
|
||||
func (op OpCode) isMutating() bool {
|
||||
switch op {
|
||||
// TODO(joel): REVERT?
|
||||
case SELFDESTRUCT, CREATE, SSTORE, LOG0, LOG1, LOG2, LOG3, LOG4:
|
||||
return true
|
||||
default:
|
||||
|
|
|
@ -223,18 +223,30 @@ type EthApiState struct {
|
|||
}
|
||||
|
||||
func (s EthApiState) GetBalance(addr common.Address) *big.Int {
|
||||
if s.privateState.Exist(addr) {
|
||||
return s.privateState.GetBalance(addr)
|
||||
}
|
||||
return s.state.GetBalance(addr)
|
||||
}
|
||||
|
||||
func (s EthApiState) GetCode(addr common.Address) []byte {
|
||||
if s.privateState.Exist(addr) {
|
||||
return s.privateState.GetCode(addr)
|
||||
}
|
||||
return s.state.GetCode(addr)
|
||||
}
|
||||
|
||||
func (s EthApiState) GetState(a common.Address, b common.Hash) common.Hash {
|
||||
if s.privateState.Exist(a) {
|
||||
return s.privateState.GetState(a, b)
|
||||
}
|
||||
return s.state.GetState(a, b)
|
||||
}
|
||||
|
||||
func (s EthApiState) GetNonce(addr common.Address) uint64 {
|
||||
if s.privateState.Exist(addr) {
|
||||
return s.privateState.GetNonce(addr)
|
||||
}
|
||||
return s.state.GetNonce(addr)
|
||||
}
|
||||
|
||||
|
|
|
@ -830,7 +830,7 @@ 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{}
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
|
@ -1000,7 +1000,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[
|
|||
}
|
||||
receipt, _, _, _ := core.GetReceipt(s.b.ChainDb(), hash) // Old receipts don't have the lookup data available
|
||||
|
||||
var signer types.Signer = types.FrontierSigner{}
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
|
@ -1041,10 +1041,12 @@ 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
|
||||
}
|
||||
return wallet.SignTx(account, tx, chainID)
|
||||
return wallet.SignTx(account, tx, chainID, isQuorum)
|
||||
}
|
||||
|
||||
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
||||
|
@ -1155,10 +1157,12 @@ 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
|
||||
}
|
||||
signed, err := wallet.SignTx(account, tx, chainID)
|
||||
signed, err := wallet.SignTx(account, tx, chainID, isQuorum)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ func TestAccountManagement(t *testing.T) {
|
|||
if err := ks.Unlock(signer, "Signer password"); err != nil {
|
||||
t.Fatalf("Failed to unlock account: %v", err)
|
||||
}
|
||||
if _, err := ks.SignTx(signer, tx, chain); err != nil {
|
||||
if _, err := ks.SignTx(signer, tx, chain, false); err != nil {
|
||||
t.Fatalf("Failed to sign with unlocked account: %v", err)
|
||||
}
|
||||
if err := ks.Lock(signer.Address); err != nil {
|
||||
|
@ -95,7 +95,7 @@ func TestAccountManagement(t *testing.T) {
|
|||
if err := ks.TimedUnlock(signer, "Signer password", time.Second); err != nil {
|
||||
t.Fatalf("Failed to time unlock account: %v", err)
|
||||
}
|
||||
if _, err := ks.SignTx(signer, tx, chain); err != nil {
|
||||
if _, err := ks.SignTx(signer, tx, chain, false); err != nil {
|
||||
t.Fatalf("Failed to sign with time unlocked account: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (
|
|||
if chainID == nil { // Null passed from mobile app
|
||||
chainID = new(BigInt)
|
||||
}
|
||||
signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
|
||||
signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -659,7 +659,7 @@ func (pm *ProtocolManager) applyNewChainHead(block *types.Block) {
|
|||
|
||||
log.Info("Non-extending block", "block", block.Hash(), "parent", block.ParentHash(), "head", headBlock.Hash())
|
||||
|
||||
pm.eventMux.Post(InvalidRaftOrdering{headBlock: headBlock, invalidBlock: block})
|
||||
pm.minter.invalidRaftOrderingChan <- InvalidRaftOrdering{headBlock: headBlock, invalidBlock: block}
|
||||
} else {
|
||||
if existingBlock := pm.blockchain.GetBlockByHash(block.Hash()); nil == existingBlock {
|
||||
if err := pm.blockchain.Validator().ValidateBody(block); err != nil {
|
||||
|
|
|
@ -58,6 +58,12 @@ type minter struct {
|
|||
shouldMine *channels.RingChannel
|
||||
blockTime time.Duration
|
||||
speculativeChain *speculativeChain
|
||||
|
||||
invalidRaftOrderingChan chan InvalidRaftOrdering
|
||||
chainHeadChan chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
txPreChan chan core.TxPreEvent
|
||||
txPreSub event.Subscription
|
||||
}
|
||||
|
||||
func newMinter(config *params.ChainConfig, eth *RaftService, blockTime time.Duration) *minter {
|
||||
|
@ -70,16 +76,18 @@ func newMinter(config *params.ChainConfig, eth *RaftService, blockTime time.Dura
|
|||
shouldMine: channels.NewRingChannel(1),
|
||||
blockTime: blockTime,
|
||||
speculativeChain: newSpeculativeChain(),
|
||||
|
||||
invalidRaftOrderingChan: make(chan InvalidRaftOrdering, 1),
|
||||
chainHeadChan: make(chan core.ChainHeadEvent, 1),
|
||||
txPreChan: make(chan core.TxPreEvent, 4096),
|
||||
}
|
||||
events := minter.mux.Subscribe(
|
||||
core.ChainHeadEvent{},
|
||||
core.TxPreEvent{},
|
||||
InvalidRaftOrdering{},
|
||||
)
|
||||
|
||||
minter.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(minter.chainHeadChan)
|
||||
minter.txPreSub = eth.TxPool().SubscribeTxPreEvent(minter.txPreChan)
|
||||
|
||||
minter.speculativeChain.clear(minter.chain.CurrentBlock())
|
||||
|
||||
go minter.eventLoop(events.Chan())
|
||||
go minter.eventLoop()
|
||||
go minter.mintingLoop()
|
||||
|
||||
return minter
|
||||
|
@ -132,10 +140,13 @@ func (minter *minter) updateSpeculativeChainPerInvalidOrdering(headBlock *types.
|
|||
minter.speculativeChain.unwindFrom(invalidHash, headBlock)
|
||||
}
|
||||
|
||||
func (minter *minter) eventLoop(events <-chan *event.TypeMuxEvent) {
|
||||
for event := range events {
|
||||
switch ev := event.Data.(type) {
|
||||
case core.ChainHeadEvent:
|
||||
func (minter *minter) eventLoop() {
|
||||
defer minter.chainHeadSub.Unsubscribe()
|
||||
defer minter.txPreSub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev := <-minter.chainHeadChan:
|
||||
newHeadBlock := ev.Block
|
||||
|
||||
if atomic.LoadInt32(&minter.minting) == 1 {
|
||||
|
@ -154,16 +165,22 @@ func (minter *minter) eventLoop(events <-chan *event.TypeMuxEvent) {
|
|||
minter.mu.Unlock()
|
||||
}
|
||||
|
||||
case core.TxPreEvent:
|
||||
case <-minter.txPreChan:
|
||||
if atomic.LoadInt32(&minter.minting) == 1 {
|
||||
minter.requestMinting()
|
||||
}
|
||||
|
||||
case InvalidRaftOrdering:
|
||||
case ev := <-minter.invalidRaftOrderingChan:
|
||||
headBlock := ev.headBlock
|
||||
invalidBlock := ev.invalidBlock
|
||||
|
||||
minter.updateSpeculativeChainPerInvalidOrdering(headBlock, invalidBlock)
|
||||
|
||||
// system stopped
|
||||
case <-minter.chainHeadSub.Err():
|
||||
return
|
||||
case <-minter.txPreSub.Err():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +350,7 @@ func (minter *minter) mintNewBlock() {
|
|||
}
|
||||
|
||||
func (env *work) commitTransactions(txes *types.TransactionsByPriceAndNonce, bc *core.BlockChain) (types.Transactions, types.Receipts, types.Receipts, []*types.Log) {
|
||||
var logs []*types.Log
|
||||
var allLogs []*types.Log
|
||||
var committedTxes types.Transactions
|
||||
var publicReceipts types.Receipts
|
||||
var privateReceipts types.Receipts
|
||||
|
@ -352,25 +369,25 @@ func (env *work) commitTransactions(txes *types.TransactionsByPriceAndNonce, bc
|
|||
publicReceipt, privateReceipt, err := env.commitTransaction(tx, bc, gp)
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Info("TX failed, will be removed", "hash", tx.Hash().Bytes()[:4], "err", err)
|
||||
log.Info("TX failed, will be removed", "hash", tx.Hash(), "err", err)
|
||||
txes.Pop() // skip rest of txes from this account
|
||||
default:
|
||||
txCount++
|
||||
committedTxes = append(committedTxes, tx)
|
||||
|
||||
logs = append(logs, publicReceipt.Logs...)
|
||||
publicReceipts = append(publicReceipts, publicReceipt)
|
||||
allLogs = append(allLogs, publicReceipt.Logs...)
|
||||
|
||||
if privateReceipt != nil {
|
||||
logs = append(logs, privateReceipt.Logs...)
|
||||
privateReceipts = append(privateReceipts, privateReceipt)
|
||||
allLogs = append(allLogs, privateReceipt.Logs...)
|
||||
}
|
||||
|
||||
txes.Shift()
|
||||
}
|
||||
}
|
||||
|
||||
return committedTxes, publicReceipts, privateReceipts, logs
|
||||
return committedTxes, publicReceipts, privateReceipts, allLogs
|
||||
}
|
||||
|
||||
func (env *work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (*types.Receipt, *types.Receipt, error) {
|
||||
|
|
|
@ -38,8 +38,8 @@ func TestBlockchain(t *testing.T) {
|
|||
// Still failing tests
|
||||
bt.skipLoad(`^bcWalletTest.*_Byzantium$`)
|
||||
|
||||
// Skip invalid receipt root hash / invalid nonce quorum failures
|
||||
bt.skipLoad(`(TransactionSendingToZero|wrongParentHash|wrongMixHash|wrongStateRoot|timestampTooHigh|timestampTooLow|nonceWrong|gasLimitTooLowExactBound|gasLimitTooLow|gasLimitTooHighExactBound|gasLimitTooHigh|diffTooLow2|diffTooLow|diffTooHigh|suicideCoinbase|InternlCallStoreClearsSucces|StoreClearsAndInternlCallStoreClearsOOG|failed_tx_xcf416c53|TransactionSendingToZero|SuicidesAndInternlCallSuicidesSuccess|SuicidesAndInternlCallSuicidesOOG|SuicidesAndInternlCallSuicidesBonusGasAtCallFailed|SuicidesAndInternlCallSuicidesBonusGasAtCall|StoreClearsAndInternlCallStoreClearsSuccess|CallContractToCreateContractOOG).json`)
|
||||
// TODO(joel): fix Byzantium tests for Quorum
|
||||
bt.skipLoad(`Byzantium`)
|
||||
|
||||
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
|
||||
if err := bt.checkFailure(t, name, test.Run()); err != nil {
|
||||
|
|
|
@ -44,9 +44,6 @@ func TestState(t *testing.T) {
|
|||
st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/Byzantium`, "bug in test")
|
||||
st.fails(`^stRevertTest/RevertPrefoundEmptyOOG\.json/Byzantium`, "bug in test")
|
||||
|
||||
// Skip invalid receipt root hash / invalid nonce quorum failures
|
||||
st.skipLoad(`(StoreClearsAndInternlCallStoreClearsOOG|TransactionSendingToZero|SuicidesAndInternlCallSuicidesOOG|SuicidesAndInternlCallSuicidesSuccess|SuicidesAndInternlCallSuicidesBonusGasAtCallFailed|SuicidesAndInternlCallSuicidesBonusGasAtCall|StoreClearsAndInternlCallStoreClearsSuccess|InternlCallStoreClearsSucces|InternlCallStoreClearsOOG|failed_tx_xcf416c53|CallContractToCreateContractOOG)\.json`) // EIP-86 is not supported yet
|
||||
|
||||
st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) {
|
||||
for _, subtest := range test.Subtests() {
|
||||
subtest := subtest
|
||||
|
|
114
tests/util.go
114
tests/util.go
|
@ -17,31 +17,14 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
ForceJit bool
|
||||
EnableJit bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlCrit, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
if os.Getenv("JITVM") == "true" {
|
||||
ForceJit = true
|
||||
EnableJit = true
|
||||
}
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
|
@ -68,100 +51,3 @@ func (self Log) Topics() [][]byte {
|
|||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func insertAccount(state *state.StateDB, saddr string, account Account) {
|
||||
if common.IsHex(account.Code) {
|
||||
account.Code = account.Code[2:]
|
||||
}
|
||||
addr := common.HexToAddress(saddr)
|
||||
state.SetCode(addr, common.Hex2Bytes(account.Code))
|
||||
state.SetNonce(addr, math.MustParseUint64(account.Nonce))
|
||||
state.SetBalance(addr, math.MustParseBig256(account.Balance))
|
||||
for a, v := range account.Storage {
|
||||
state.SetState(addr, common.HexToHash(a), common.HexToHash(v))
|
||||
}
|
||||
}
|
||||
|
||||
type VmEnv struct {
|
||||
CurrentCoinbase string
|
||||
CurrentDifficulty string
|
||||
CurrentGasLimit string
|
||||
CurrentNumber string
|
||||
CurrentTimestamp interface{}
|
||||
PreviousHash string
|
||||
}
|
||||
|
||||
type VmTest struct {
|
||||
Callcreates interface{}
|
||||
//Env map[string]string
|
||||
Env VmEnv
|
||||
Exec map[string]string
|
||||
Transaction map[string]string
|
||||
Logs []Log
|
||||
Gas string
|
||||
Out string
|
||||
Post map[string]Account
|
||||
Pre map[string]Account
|
||||
PostStateRoot string
|
||||
}
|
||||
|
||||
func NewEVMEnvironment(vmTest bool, chainConfig *params.ChainConfig, statedb *state.StateDB, envValues map[string]string, tx map[string]string) (*vm.EVM, core.Message) {
|
||||
var (
|
||||
data = common.FromHex(tx["data"])
|
||||
gas = math.MustParseBig256(tx["gasLimit"])
|
||||
price = math.MustParseBig256(tx["gasPrice"])
|
||||
value = math.MustParseBig256(tx["value"])
|
||||
nonce = math.MustParseUint64(tx["nonce"])
|
||||
)
|
||||
|
||||
origin := common.HexToAddress(tx["caller"])
|
||||
if len(tx["secretKey"]) > 0 {
|
||||
key, _ := crypto.HexToECDSA(tx["secretKey"])
|
||||
origin = crypto.PubkeyToAddress(key.PublicKey)
|
||||
}
|
||||
|
||||
var to *common.Address
|
||||
if len(tx["to"]) > 2 {
|
||||
t := common.HexToAddress(tx["to"])
|
||||
to = &t
|
||||
}
|
||||
|
||||
msg := types.NewMessage(origin, to, nonce, value, gas, price, data, true)
|
||||
|
||||
initialCall := true
|
||||
canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool {
|
||||
if vmTest {
|
||||
if initialCall {
|
||||
initialCall = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
return core.CanTransfer(db, address, amount)
|
||||
}
|
||||
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
|
||||
if vmTest {
|
||||
return
|
||||
}
|
||||
core.Transfer(db, sender, recipient, amount)
|
||||
}
|
||||
|
||||
context := vm.Context{
|
||||
CanTransfer: canTransfer,
|
||||
Transfer: transfer,
|
||||
GetHash: func(n uint64) common.Hash {
|
||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||
},
|
||||
|
||||
Origin: origin,
|
||||
Coinbase: common.HexToAddress(envValues["currentCoinbase"]),
|
||||
BlockNumber: math.MustParseBig256(envValues["currentNumber"]),
|
||||
Time: math.MustParseBig256(envValues["currentTimestamp"]),
|
||||
GasLimit: math.MustParseBig256(envValues["currentGasLimit"]),
|
||||
Difficulty: math.MustParseBig256(envValues["currentDifficulty"]),
|
||||
GasPrice: price,
|
||||
}
|
||||
if context.GasPrice == nil {
|
||||
context.GasPrice = new(big.Int)
|
||||
}
|
||||
return vm.NewEVM(context, statedb, statedb, chainConfig, vm.Config{NoRecursion: vmTest}), msg
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue