forked from cerc-io/plugeth
Merge tag 'v1.10.9' into develop
Notes: the AppendAncient plugin hook is broken by this commit. This adds CaptureEnter() and CaptureExit() as no-ops for interface compliance, but these capabilities should be added for plugin tracers soon.
This commit is contained in:
+1
-1
@@ -342,7 +342,7 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
|
||||
} else {
|
||||
blockRlp = fmt.Sprintf("0x%x", rlpBytes)
|
||||
}
|
||||
if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.engine); err != nil {
|
||||
if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true); err != nil {
|
||||
blockJSON = map[string]interface{}{"error": err.Error()}
|
||||
}
|
||||
results = append(results, &BadBlockArgs{
|
||||
|
||||
+10
-9
@@ -21,6 +21,7 @@ import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
@@ -30,7 +31,6 @@ import (
|
||||
"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/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
@@ -181,13 +181,14 @@ func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (type
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
|
||||
receipts := b.eth.blockchain.GetReceiptsByHash(hash)
|
||||
if receipts == nil {
|
||||
return nil, nil
|
||||
db := b.eth.ChainDb()
|
||||
number := rawdb.ReadHeaderNumber(db, hash)
|
||||
if number == nil {
|
||||
return nil, errors.New("failed to get block number from hash")
|
||||
}
|
||||
logs := make([][]*types.Log, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
logs[i] = receipt.Logs
|
||||
logs := rawdb.ReadLogs(db, hash, *number)
|
||||
if logs == nil {
|
||||
return nil, errors.New("failed to get logs for block")
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
@@ -279,8 +280,8 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
|
||||
return b.eth.TxPool().SubscribeNewTxsEvent(ch)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) Downloader() *downloader.Downloader {
|
||||
return b.eth.Downloader()
|
||||
func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
|
||||
return b.eth.Downloader().Progress()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
|
||||
@@ -448,8 +448,8 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
|
||||
d.mux.Post(DoneEvent{latest})
|
||||
}
|
||||
}()
|
||||
if p.version < eth.ETH65 {
|
||||
return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH65)
|
||||
if p.version < eth.ETH66 {
|
||||
return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH66)
|
||||
}
|
||||
mode := d.getMode()
|
||||
|
||||
|
||||
@@ -522,10 +522,6 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalSynchronisation65Full(t *testing.T) { testCanonSync(t, eth.ETH65, FullSync) }
|
||||
func TestCanonicalSynchronisation65Fast(t *testing.T) { testCanonSync(t, eth.ETH65, FastSync) }
|
||||
func TestCanonicalSynchronisation65Light(t *testing.T) { testCanonSync(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, eth.ETH66, FullSync) }
|
||||
func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, eth.ETH66, FastSync) }
|
||||
func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, eth.ETH66, LightSync) }
|
||||
@@ -549,9 +545,6 @@ func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that if a large batch of blocks are being downloaded, it is throttled
|
||||
// until the cached blocks are retrieved.
|
||||
func TestThrottling65Full(t *testing.T) { testThrottling(t, eth.ETH65, FullSync) }
|
||||
func TestThrottling65Fast(t *testing.T) { testThrottling(t, eth.ETH65, FastSync) }
|
||||
|
||||
func TestThrottling66Full(t *testing.T) { testThrottling(t, eth.ETH66, FullSync) }
|
||||
func TestThrottling66Fast(t *testing.T) { testThrottling(t, eth.ETH66, FastSync) }
|
||||
|
||||
@@ -634,10 +627,6 @@ func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
|
||||
// Tests that simple synchronization against a forked chain works correctly. In
|
||||
// this test common ancestor lookup should *not* be short circuited, and a full
|
||||
// binary search should be executed.
|
||||
func TestForkedSync65Full(t *testing.T) { testForkedSync(t, eth.ETH65, FullSync) }
|
||||
func TestForkedSync65Fast(t *testing.T) { testForkedSync(t, eth.ETH65, FastSync) }
|
||||
func TestForkedSync65Light(t *testing.T) { testForkedSync(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestForkedSync66Full(t *testing.T) { testForkedSync(t, eth.ETH66, FullSync) }
|
||||
func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, eth.ETH66, FastSync) }
|
||||
func TestForkedSync66Light(t *testing.T) { testForkedSync(t, eth.ETH66, LightSync) }
|
||||
@@ -667,10 +656,6 @@ func testForkedSync(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that synchronising against a much shorter but much heavyer fork works
|
||||
// corrently and is not dropped.
|
||||
func TestHeavyForkedSync65Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH65, FullSync) }
|
||||
func TestHeavyForkedSync65Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH65, FastSync) }
|
||||
func TestHeavyForkedSync65Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FullSync) }
|
||||
func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FastSync) }
|
||||
func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, LightSync) }
|
||||
@@ -702,10 +687,6 @@ func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
|
||||
// Tests that chain forks are contained within a certain interval of the current
|
||||
// chain head, ensuring that malicious peers cannot waste resources by feeding
|
||||
// long dead chains.
|
||||
func TestBoundedForkedSync65Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH65, FullSync) }
|
||||
func TestBoundedForkedSync65Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH65, FastSync) }
|
||||
func TestBoundedForkedSync65Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FullSync) }
|
||||
func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FastSync) }
|
||||
func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, LightSync) }
|
||||
@@ -736,16 +717,6 @@ func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) {
|
||||
// Tests that chain forks are contained within a certain interval of the current
|
||||
// chain head for short but heavy forks too. These are a bit special because they
|
||||
// take different ancestor lookup paths.
|
||||
func TestBoundedHeavyForkedSync65Full(t *testing.T) {
|
||||
testBoundedHeavyForkedSync(t, eth.ETH65, FullSync)
|
||||
}
|
||||
func TestBoundedHeavyForkedSync65Fast(t *testing.T) {
|
||||
testBoundedHeavyForkedSync(t, eth.ETH65, FastSync)
|
||||
}
|
||||
func TestBoundedHeavyForkedSync65Light(t *testing.T) {
|
||||
testBoundedHeavyForkedSync(t, eth.ETH65, LightSync)
|
||||
}
|
||||
|
||||
func TestBoundedHeavyForkedSync66Full(t *testing.T) {
|
||||
testBoundedHeavyForkedSync(t, eth.ETH66, FullSync)
|
||||
}
|
||||
@@ -800,10 +771,6 @@ func TestInactiveDownloader63(t *testing.T) {
|
||||
}
|
||||
|
||||
// Tests that a canceled download wipes all previously accumulated state.
|
||||
func TestCancel65Full(t *testing.T) { testCancel(t, eth.ETH65, FullSync) }
|
||||
func TestCancel65Fast(t *testing.T) { testCancel(t, eth.ETH65, FastSync) }
|
||||
func TestCancel65Light(t *testing.T) { testCancel(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestCancel66Full(t *testing.T) { testCancel(t, eth.ETH66, FullSync) }
|
||||
func TestCancel66Fast(t *testing.T) { testCancel(t, eth.ETH66, FastSync) }
|
||||
func TestCancel66Light(t *testing.T) { testCancel(t, eth.ETH66, LightSync) }
|
||||
@@ -833,10 +800,6 @@ func testCancel(t *testing.T, protocol uint, mode SyncMode) {
|
||||
}
|
||||
|
||||
// Tests that synchronisation from multiple peers works as intended (multi thread sanity test).
|
||||
func TestMultiSynchronisation65Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH65, FullSync) }
|
||||
func TestMultiSynchronisation65Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH65, FastSync) }
|
||||
func TestMultiSynchronisation65Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FullSync) }
|
||||
func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FastSync) }
|
||||
func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, LightSync) }
|
||||
@@ -863,10 +826,6 @@ func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that synchronisations behave well in multi-version protocol environments
|
||||
// and not wreak havoc on other nodes in the network.
|
||||
func TestMultiProtoSynchronisation65Full(t *testing.T) { testMultiProtoSync(t, eth.ETH65, FullSync) }
|
||||
func TestMultiProtoSynchronisation65Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH65, FastSync) }
|
||||
func TestMultiProtoSynchronisation65Light(t *testing.T) { testMultiProtoSync(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FullSync) }
|
||||
func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FastSync) }
|
||||
func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, eth.ETH66, LightSync) }
|
||||
@@ -881,8 +840,8 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
|
||||
chain := testChainBase.shorten(blockCacheMaxItems - 15)
|
||||
|
||||
// Create peers of every type
|
||||
tester.newPeer("peer 65", eth.ETH65, chain)
|
||||
tester.newPeer("peer 66", eth.ETH66, chain)
|
||||
//tester.newPeer("peer 65", eth.ETH67, chain)
|
||||
|
||||
// Synchronise with the requested peer and make sure all blocks were retrieved
|
||||
if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil {
|
||||
@@ -891,7 +850,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
|
||||
assertOwnChain(t, tester, chain.len())
|
||||
|
||||
// Check that no peers have been dropped off
|
||||
for _, version := range []int{65, 66} {
|
||||
for _, version := range []int{66} {
|
||||
peer := fmt.Sprintf("peer %d", version)
|
||||
if _, ok := tester.peers[peer]; !ok {
|
||||
t.Errorf("%s dropped", peer)
|
||||
@@ -901,10 +860,6 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that if a block is empty (e.g. header only), no body request should be
|
||||
// made, and instead the header should be assembled into a whole block in itself.
|
||||
func TestEmptyShortCircuit65Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH65, FullSync) }
|
||||
func TestEmptyShortCircuit65Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH65, FastSync) }
|
||||
func TestEmptyShortCircuit65Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FullSync) }
|
||||
func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FastSync) }
|
||||
func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, LightSync) }
|
||||
@@ -955,10 +910,6 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that headers are enqueued continuously, preventing malicious nodes from
|
||||
// stalling the downloader by feeding gapped header chains.
|
||||
func TestMissingHeaderAttack65Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH65, FullSync) }
|
||||
func TestMissingHeaderAttack65Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH65, FastSync) }
|
||||
func TestMissingHeaderAttack65Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FullSync) }
|
||||
func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FastSync) }
|
||||
func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, LightSync) }
|
||||
@@ -987,10 +938,6 @@ func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that if requested headers are shifted (i.e. first is missing), the queue
|
||||
// detects the invalid numbering.
|
||||
func TestShiftedHeaderAttack65Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH65, FullSync) }
|
||||
func TestShiftedHeaderAttack65Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH65, FastSync) }
|
||||
func TestShiftedHeaderAttack65Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FullSync) }
|
||||
func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FastSync) }
|
||||
func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, LightSync) }
|
||||
@@ -1024,7 +971,6 @@ func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
|
||||
// Tests that upon detecting an invalid header, the recent ones are rolled back
|
||||
// for various failure scenarios. Afterwards a full sync is attempted to make
|
||||
// sure no state was corrupted.
|
||||
func TestInvalidHeaderRollback65Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH65, FastSync) }
|
||||
func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, FastSync) }
|
||||
|
||||
func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
|
||||
@@ -1115,16 +1061,6 @@ func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that a peer advertising a high TD doesn't get to stall the downloader
|
||||
// afterwards by not sending any useful hashes.
|
||||
func TestHighTDStarvationAttack65Full(t *testing.T) {
|
||||
testHighTDStarvationAttack(t, eth.ETH65, FullSync)
|
||||
}
|
||||
func TestHighTDStarvationAttack65Fast(t *testing.T) {
|
||||
testHighTDStarvationAttack(t, eth.ETH65, FastSync)
|
||||
}
|
||||
func TestHighTDStarvationAttack65Light(t *testing.T) {
|
||||
testHighTDStarvationAttack(t, eth.ETH65, LightSync)
|
||||
}
|
||||
|
||||
func TestHighTDStarvationAttack66Full(t *testing.T) {
|
||||
testHighTDStarvationAttack(t, eth.ETH66, FullSync)
|
||||
}
|
||||
@@ -1149,7 +1085,6 @@ func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) {
|
||||
}
|
||||
|
||||
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
|
||||
func TestBlockHeaderAttackerDropping65(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH65) }
|
||||
func TestBlockHeaderAttackerDropping66(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH66) }
|
||||
|
||||
func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
|
||||
@@ -1202,10 +1137,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
|
||||
|
||||
// Tests that synchronisation progress (origin block number, current block number
|
||||
// and highest block number) is tracked and updated correctly.
|
||||
func TestSyncProgress65Full(t *testing.T) { testSyncProgress(t, eth.ETH65, FullSync) }
|
||||
func TestSyncProgress65Fast(t *testing.T) { testSyncProgress(t, eth.ETH65, FastSync) }
|
||||
func TestSyncProgress65Light(t *testing.T) { testSyncProgress(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, eth.ETH66, FullSync) }
|
||||
func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, eth.ETH66, FastSync) }
|
||||
func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, eth.ETH66, LightSync) }
|
||||
@@ -1286,10 +1217,6 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync
|
||||
// Tests that synchronisation progress (origin block number and highest block
|
||||
// number) is tracked and updated correctly in case of a fork (or manual head
|
||||
// revertal).
|
||||
func TestForkedSyncProgress65Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH65, FullSync) }
|
||||
func TestForkedSyncProgress65Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH65, FastSync) }
|
||||
func TestForkedSyncProgress65Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FullSync) }
|
||||
func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FastSync) }
|
||||
func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, LightSync) }
|
||||
@@ -1362,10 +1289,6 @@ func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
|
||||
// Tests that if synchronisation is aborted due to some failure, then the progress
|
||||
// origin is not updated in the next sync cycle, as it should be considered the
|
||||
// continuation of the previous sync and not a new instance.
|
||||
func TestFailedSyncProgress65Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH65, FullSync) }
|
||||
func TestFailedSyncProgress65Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH65, FastSync) }
|
||||
func TestFailedSyncProgress65Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FullSync) }
|
||||
func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FastSync) }
|
||||
func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, LightSync) }
|
||||
@@ -1435,10 +1358,6 @@ func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// Tests that if an attacker fakes a chain height, after the attack is detected,
|
||||
// the progress height is successfully reduced at the next sync invocation.
|
||||
func TestFakedSyncProgress65Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH65, FullSync) }
|
||||
func TestFakedSyncProgress65Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH65, FastSync) }
|
||||
func TestFakedSyncProgress65Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FullSync) }
|
||||
func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FastSync) }
|
||||
func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, LightSync) }
|
||||
@@ -1512,10 +1431,6 @@ func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
|
||||
|
||||
// This test reproduces an issue where unexpected deliveries would
|
||||
// block indefinitely if they arrived at the right time.
|
||||
func TestDeliverHeadersHang65Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH65, FullSync) }
|
||||
func TestDeliverHeadersHang65Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH65, FastSync) }
|
||||
func TestDeliverHeadersHang65Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH65, LightSync) }
|
||||
|
||||
func TestDeliverHeadersHang66Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FullSync) }
|
||||
func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FastSync) }
|
||||
func TestDeliverHeadersHang66Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, LightSync) }
|
||||
@@ -1673,12 +1588,6 @@ func TestRemoteHeaderRequestSpan(t *testing.T) {
|
||||
|
||||
// Tests that peers below a pre-configured checkpoint block are prevented from
|
||||
// being fast-synced from, avoiding potential cheap eclipse attacks.
|
||||
func TestCheckpointEnforcement65Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH65, FullSync) }
|
||||
func TestCheckpointEnforcement65Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH65, FastSync) }
|
||||
func TestCheckpointEnforcement65Light(t *testing.T) {
|
||||
testCheckpointEnforcement(t, eth.ETH65, LightSync)
|
||||
}
|
||||
|
||||
func TestCheckpointEnforcement66Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FullSync) }
|
||||
func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FastSync) }
|
||||
func TestCheckpointEnforcement66Light(t *testing.T) {
|
||||
|
||||
@@ -413,7 +413,7 @@ func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) {
|
||||
throughput := func(p *peerConnection) int {
|
||||
return p.rates.Capacity(eth.BlockHeadersMsg, time.Second)
|
||||
}
|
||||
return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
|
||||
return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
|
||||
}
|
||||
|
||||
// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within
|
||||
@@ -425,7 +425,7 @@ func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) {
|
||||
throughput := func(p *peerConnection) int {
|
||||
return p.rates.Capacity(eth.BlockBodiesMsg, time.Second)
|
||||
}
|
||||
return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
|
||||
return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
|
||||
}
|
||||
|
||||
// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers
|
||||
@@ -437,7 +437,7 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) {
|
||||
throughput := func(p *peerConnection) int {
|
||||
return p.rates.Capacity(eth.ReceiptsMsg, time.Second)
|
||||
}
|
||||
return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
|
||||
return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
|
||||
}
|
||||
|
||||
// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle
|
||||
@@ -449,7 +449,7 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) {
|
||||
throughput := func(p *peerConnection) int {
|
||||
return p.rates.Capacity(eth.NodeDataMsg, time.Second)
|
||||
}
|
||||
return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
|
||||
return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
|
||||
}
|
||||
|
||||
// idlePeers retrieves a flat list of all currently idle peers satisfying the
|
||||
|
||||
+1
-4
@@ -117,7 +117,6 @@ type handler struct {
|
||||
whitelist map[uint64]common.Hash
|
||||
|
||||
// channels for fetcher, syncer, txsyncLoop
|
||||
txsyncCh chan *txsync
|
||||
quitSync chan struct{}
|
||||
|
||||
chainSync *chainSyncer
|
||||
@@ -140,7 +139,6 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
||||
chain: config.Chain,
|
||||
peers: newPeerSet(),
|
||||
whitelist: config.Whitelist,
|
||||
txsyncCh: make(chan *txsync),
|
||||
quitSync: make(chan struct{}),
|
||||
}
|
||||
if config.Sync == downloader.FullSync {
|
||||
@@ -408,9 +406,8 @@ func (h *handler) Start(maxPeers int) {
|
||||
go h.minedBroadcastLoop()
|
||||
|
||||
// start sync handlers
|
||||
h.wg.Add(2)
|
||||
h.wg.Add(1)
|
||||
go h.chainSync.loop()
|
||||
go h.txsyncLoop64() // TODO(karalabe): Legacy initial tx echange, drop with eth/64.
|
||||
}
|
||||
|
||||
func (h *handler) Stop() {
|
||||
|
||||
+21
-17
@@ -80,7 +80,6 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
|
||||
|
||||
// Tests that peers are correctly accepted (or rejected) based on the advertised
|
||||
// fork IDs in the protocol handshake.
|
||||
func TestForkIDSplit65(t *testing.T) { testForkIDSplit(t, eth.ETH65) }
|
||||
func TestForkIDSplit66(t *testing.T) { testForkIDSplit(t, eth.ETH66) }
|
||||
|
||||
func testForkIDSplit(t *testing.T, protocol uint) {
|
||||
@@ -236,7 +235,6 @@ func testForkIDSplit(t *testing.T, protocol uint) {
|
||||
}
|
||||
|
||||
// Tests that received transactions are added to the local pool.
|
||||
func TestRecvTransactions65(t *testing.T) { testRecvTransactions(t, eth.ETH65) }
|
||||
func TestRecvTransactions66(t *testing.T) { testRecvTransactions(t, eth.ETH66) }
|
||||
|
||||
func testRecvTransactions(t *testing.T, protocol uint) {
|
||||
@@ -294,7 +292,6 @@ func testRecvTransactions(t *testing.T, protocol uint) {
|
||||
}
|
||||
|
||||
// This test checks that pending transactions are sent.
|
||||
func TestSendTransactions65(t *testing.T) { testSendTransactions(t, eth.ETH65) }
|
||||
func TestSendTransactions66(t *testing.T) { testSendTransactions(t, eth.ETH66) }
|
||||
|
||||
func testSendTransactions(t *testing.T, protocol uint) {
|
||||
@@ -306,7 +303,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
|
||||
|
||||
insert := make([]*types.Transaction, 100)
|
||||
for nonce := range insert {
|
||||
tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, txsyncPackSize/10))
|
||||
tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, 10240))
|
||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
|
||||
insert[nonce] = tx
|
||||
@@ -380,7 +377,6 @@ func testSendTransactions(t *testing.T, protocol uint) {
|
||||
|
||||
// Tests that transactions get propagated to all attached peers, either via direct
|
||||
// broadcasts or via announcements/retrievals.
|
||||
func TestTransactionPropagation65(t *testing.T) { testTransactionPropagation(t, eth.ETH65) }
|
||||
func TestTransactionPropagation66(t *testing.T) { testTransactionPropagation(t, eth.ETH66) }
|
||||
|
||||
func testTransactionPropagation(t *testing.T, protocol uint) {
|
||||
@@ -521,8 +517,8 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
|
||||
defer p2pLocal.Close()
|
||||
defer p2pRemote.Close()
|
||||
|
||||
local := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool)
|
||||
remote := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool)
|
||||
local := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool)
|
||||
remote := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool)
|
||||
defer local.Close()
|
||||
defer remote.Close()
|
||||
|
||||
@@ -543,30 +539,39 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
|
||||
if err := remote.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
|
||||
t.Fatalf("failed to run protocol handshake")
|
||||
}
|
||||
|
||||
// Connect a new peer and check that we receive the checkpoint challenge.
|
||||
if checkpoint {
|
||||
if err := remote.ExpectRequestHeadersByNumber(response.Number.Uint64(), 1, 0, false); err != nil {
|
||||
t.Fatalf("challenge mismatch: %v", err)
|
||||
msg, err := p2pRemote.ReadMsg()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read checkpoint challenge: %v", err)
|
||||
}
|
||||
request := new(eth.GetBlockHeadersPacket66)
|
||||
if err := msg.Decode(request); err != nil {
|
||||
t.Fatalf("failed to decode checkpoint challenge: %v", err)
|
||||
}
|
||||
query := request.GetBlockHeadersPacket
|
||||
if query.Origin.Number != response.Number.Uint64() || query.Amount != 1 || query.Skip != 0 || query.Reverse {
|
||||
t.Fatalf("challenge mismatch: have [%d, %d, %d, %v] want [%d, %d, %d, %v]",
|
||||
query.Origin.Number, query.Amount, query.Skip, query.Reverse,
|
||||
response.Number.Uint64(), 1, 0, false)
|
||||
}
|
||||
// Create a block to reply to the challenge if no timeout is simulated.
|
||||
if !timeout {
|
||||
if empty {
|
||||
if err := remote.SendBlockHeaders([]*types.Header{}); err != nil {
|
||||
if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{}); err != nil {
|
||||
t.Fatalf("failed to answer challenge: %v", err)
|
||||
}
|
||||
} else if match {
|
||||
if err := remote.SendBlockHeaders([]*types.Header{response}); err != nil {
|
||||
if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{response}); err != nil {
|
||||
t.Fatalf("failed to answer challenge: %v", err)
|
||||
}
|
||||
} else {
|
||||
if err := remote.SendBlockHeaders([]*types.Header{{Number: response.Number}}); err != nil {
|
||||
if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{{Number: response.Number}}); err != nil {
|
||||
t.Fatalf("failed to answer challenge: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the test timeout passes to ensure proper cleanup
|
||||
time.Sleep(syncChallengeTimeout + 300*time.Millisecond)
|
||||
|
||||
@@ -619,8 +624,8 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
|
||||
defer sourcePipe.Close()
|
||||
defer sinkPipe.Close()
|
||||
|
||||
sourcePeer := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil)
|
||||
sinkPeer := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil)
|
||||
sourcePeer := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil)
|
||||
sinkPeer := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil)
|
||||
defer sourcePeer.Close()
|
||||
defer sinkPeer.Close()
|
||||
|
||||
@@ -671,7 +676,6 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
|
||||
|
||||
// Tests that a propagated malformed block (uncles or transactions don't match
|
||||
// with the hashes in the header) gets discarded and not broadcast forward.
|
||||
func TestBroadcastMalformedBlock65(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH65) }
|
||||
func TestBroadcastMalformedBlock66(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH66) }
|
||||
|
||||
func testBroadcastMalformedBlock(t *testing.T, protocol uint) {
|
||||
|
||||
@@ -37,6 +37,9 @@ func (mt *metaTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
|
||||
tracer.CaptureEnd(output, gasUsed, t, err)
|
||||
}
|
||||
}
|
||||
// TODO: Align these with PluGeth-utils
|
||||
func (mt *metaTracer) CaptureEnter(vm.OpCode, common.Address, common.Address, []byte, uint64, *big.Int) {}
|
||||
func (mt *metaTracer) CaptureExit([]byte, uint64, error) {}
|
||||
|
||||
func PluginUpdateBlockchainVMConfig(pl *plugins.PluginLoader, cfg *vm.Config) {
|
||||
tracerList := plugins.Lookup("LiveTracer", func(item interface{}) bool {
|
||||
|
||||
@@ -171,39 +171,21 @@ type Decoder interface {
|
||||
Time() time.Time
|
||||
}
|
||||
|
||||
var eth65 = map[uint64]msgHandler{
|
||||
GetBlockHeadersMsg: handleGetBlockHeaders,
|
||||
BlockHeadersMsg: handleBlockHeaders,
|
||||
GetBlockBodiesMsg: handleGetBlockBodies,
|
||||
BlockBodiesMsg: handleBlockBodies,
|
||||
GetNodeDataMsg: handleGetNodeData,
|
||||
NodeDataMsg: handleNodeData,
|
||||
GetReceiptsMsg: handleGetReceipts,
|
||||
ReceiptsMsg: handleReceipts,
|
||||
NewBlockHashesMsg: handleNewBlockhashes,
|
||||
NewBlockMsg: handleNewBlock,
|
||||
TransactionsMsg: handleTransactions,
|
||||
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
|
||||
GetPooledTransactionsMsg: handleGetPooledTransactions,
|
||||
PooledTransactionsMsg: handlePooledTransactions,
|
||||
}
|
||||
|
||||
var eth66 = map[uint64]msgHandler{
|
||||
NewBlockHashesMsg: handleNewBlockhashes,
|
||||
NewBlockMsg: handleNewBlock,
|
||||
TransactionsMsg: handleTransactions,
|
||||
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
|
||||
// eth66 messages with request-id
|
||||
GetBlockHeadersMsg: handleGetBlockHeaders66,
|
||||
BlockHeadersMsg: handleBlockHeaders66,
|
||||
GetBlockBodiesMsg: handleGetBlockBodies66,
|
||||
BlockBodiesMsg: handleBlockBodies66,
|
||||
GetNodeDataMsg: handleGetNodeData66,
|
||||
NodeDataMsg: handleNodeData66,
|
||||
GetReceiptsMsg: handleGetReceipts66,
|
||||
ReceiptsMsg: handleReceipts66,
|
||||
GetPooledTransactionsMsg: handleGetPooledTransactions66,
|
||||
PooledTransactionsMsg: handlePooledTransactions66,
|
||||
GetBlockHeadersMsg: handleGetBlockHeaders66,
|
||||
BlockHeadersMsg: handleBlockHeaders66,
|
||||
GetBlockBodiesMsg: handleGetBlockBodies66,
|
||||
BlockBodiesMsg: handleBlockBodies66,
|
||||
GetNodeDataMsg: handleGetNodeData66,
|
||||
NodeDataMsg: handleNodeData66,
|
||||
GetReceiptsMsg: handleGetReceipts66,
|
||||
ReceiptsMsg: handleReceipts66,
|
||||
GetPooledTransactionsMsg: handleGetPooledTransactions66,
|
||||
PooledTransactionsMsg: handlePooledTransactions66,
|
||||
}
|
||||
|
||||
// handleMessage is invoked whenever an inbound message is received from a remote
|
||||
@@ -219,10 +201,11 @@ func handleMessage(backend Backend, peer *Peer) error {
|
||||
}
|
||||
defer msg.Discard()
|
||||
|
||||
var handlers = eth65
|
||||
if peer.Version() >= ETH66 {
|
||||
handlers = eth66
|
||||
}
|
||||
var handlers = eth66
|
||||
//if peer.Version() >= ETH67 { // Left in as a sample when new protocol is added
|
||||
// handlers = eth67
|
||||
//}
|
||||
|
||||
// Track the amount of time it takes to serve the request and run the handler
|
||||
if metrics.Enabled {
|
||||
h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code)
|
||||
|
||||
@@ -110,7 +110,6 @@ func (b *testBackend) Handle(*Peer, Packet) error {
|
||||
}
|
||||
|
||||
// Tests that block headers can be retrieved from a remote chain based on user queries.
|
||||
func TestGetBlockHeaders65(t *testing.T) { testGetBlockHeaders(t, ETH65) }
|
||||
func TestGetBlockHeaders66(t *testing.T) { testGetBlockHeaders(t, ETH66) }
|
||||
|
||||
func testGetBlockHeaders(t *testing.T, protocol uint) {
|
||||
@@ -254,44 +253,30 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
|
||||
headers = append(headers, backend.chain.GetBlockByHash(hash).Header())
|
||||
}
|
||||
// Send the hash request and verify the response
|
||||
if protocol <= ETH65 {
|
||||
p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
|
||||
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil {
|
||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
||||
}
|
||||
} else {
|
||||
p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
|
||||
RequestId: 123,
|
||||
GetBlockHeadersPacket: tt.query,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
|
||||
RequestId: 123,
|
||||
BlockHeadersPacket: headers,
|
||||
}); err != nil {
|
||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
||||
}
|
||||
p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
|
||||
RequestId: 123,
|
||||
GetBlockHeadersPacket: tt.query,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
|
||||
RequestId: 123,
|
||||
BlockHeadersPacket: headers,
|
||||
}); err != nil {
|
||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
||||
}
|
||||
// If the test used number origins, repeat with hashes as the too
|
||||
if tt.query.Origin.Hash == (common.Hash{}) {
|
||||
if origin := backend.chain.GetBlockByNumber(tt.query.Origin.Number); origin != nil {
|
||||
tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0
|
||||
|
||||
if protocol <= ETH65 {
|
||||
p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
|
||||
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil {
|
||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
||||
}
|
||||
} else {
|
||||
p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
|
||||
RequestId: 456,
|
||||
GetBlockHeadersPacket: tt.query,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
|
||||
RequestId: 456,
|
||||
BlockHeadersPacket: headers,
|
||||
}); err != nil {
|
||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
||||
}
|
||||
p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
|
||||
RequestId: 456,
|
||||
GetBlockHeadersPacket: tt.query,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
|
||||
RequestId: 456,
|
||||
BlockHeadersPacket: headers,
|
||||
}); err != nil {
|
||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,7 +284,6 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
|
||||
}
|
||||
|
||||
// Tests that block contents can be retrieved from a remote chain based on their hashes.
|
||||
func TestGetBlockBodies65(t *testing.T) { testGetBlockBodies(t, ETH65) }
|
||||
func TestGetBlockBodies66(t *testing.T) { testGetBlockBodies(t, ETH66) }
|
||||
|
||||
func testGetBlockBodies(t *testing.T, protocol uint) {
|
||||
@@ -369,28 +353,20 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
|
||||
}
|
||||
}
|
||||
// Send the hash request and verify the response
|
||||
if protocol <= ETH65 {
|
||||
p2p.Send(peer.app, GetBlockBodiesMsg, hashes)
|
||||
if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, bodies); err != nil {
|
||||
t.Errorf("test %d: bodies mismatch: %v", i, err)
|
||||
}
|
||||
} else {
|
||||
p2p.Send(peer.app, GetBlockBodiesMsg, GetBlockBodiesPacket66{
|
||||
RequestId: 123,
|
||||
GetBlockBodiesPacket: hashes,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, BlockBodiesPacket66{
|
||||
RequestId: 123,
|
||||
BlockBodiesPacket: bodies,
|
||||
}); err != nil {
|
||||
t.Errorf("test %d: bodies mismatch: %v", i, err)
|
||||
}
|
||||
p2p.Send(peer.app, GetBlockBodiesMsg, GetBlockBodiesPacket66{
|
||||
RequestId: 123,
|
||||
GetBlockBodiesPacket: hashes,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, BlockBodiesPacket66{
|
||||
RequestId: 123,
|
||||
BlockBodiesPacket: bodies,
|
||||
}); err != nil {
|
||||
t.Errorf("test %d: bodies mismatch: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that the state trie nodes can be retrieved based on hashes.
|
||||
func TestGetNodeData65(t *testing.T) { testGetNodeData(t, ETH65) }
|
||||
func TestGetNodeData66(t *testing.T) { testGetNodeData(t, ETH66) }
|
||||
|
||||
func testGetNodeData(t *testing.T, protocol uint) {
|
||||
@@ -449,14 +425,10 @@ func testGetNodeData(t *testing.T, protocol uint) {
|
||||
}
|
||||
it.Release()
|
||||
|
||||
if protocol <= ETH65 {
|
||||
p2p.Send(peer.app, GetNodeDataMsg, hashes)
|
||||
} else {
|
||||
p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{
|
||||
RequestId: 123,
|
||||
GetNodeDataPacket: hashes,
|
||||
})
|
||||
}
|
||||
p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{
|
||||
RequestId: 123,
|
||||
GetNodeDataPacket: hashes,
|
||||
})
|
||||
msg, err := peer.app.ReadMsg()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read node data response: %v", err)
|
||||
@@ -464,18 +436,14 @@ func testGetNodeData(t *testing.T, protocol uint) {
|
||||
if msg.Code != NodeDataMsg {
|
||||
t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg)
|
||||
}
|
||||
var data [][]byte
|
||||
if protocol <= ETH65 {
|
||||
if err := msg.Decode(&data); err != nil {
|
||||
t.Fatalf("failed to decode response node data: %v", err)
|
||||
}
|
||||
} else {
|
||||
var res NodeDataPacket66
|
||||
if err := msg.Decode(&res); err != nil {
|
||||
t.Fatalf("failed to decode response node data: %v", err)
|
||||
}
|
||||
data = res.NodeDataPacket
|
||||
var (
|
||||
data [][]byte
|
||||
res NodeDataPacket66
|
||||
)
|
||||
if err := msg.Decode(&res); err != nil {
|
||||
t.Fatalf("failed to decode response node data: %v", err)
|
||||
}
|
||||
data = res.NodeDataPacket
|
||||
// Verify that all hashes correspond to the requested data, and reconstruct a state tree
|
||||
for i, want := range hashes {
|
||||
if hash := crypto.Keccak256Hash(data[i]); hash != want {
|
||||
@@ -506,7 +474,6 @@ func testGetNodeData(t *testing.T, protocol uint) {
|
||||
}
|
||||
|
||||
// Tests that the transaction receipts can be retrieved based on hashes.
|
||||
func TestGetBlockReceipts65(t *testing.T) { testGetBlockReceipts(t, ETH65) }
|
||||
func TestGetBlockReceipts66(t *testing.T) { testGetBlockReceipts(t, ETH66) }
|
||||
|
||||
func testGetBlockReceipts(t *testing.T, protocol uint) {
|
||||
@@ -566,21 +533,14 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
|
||||
receipts = append(receipts, backend.chain.GetReceiptsByHash(block.Hash()))
|
||||
}
|
||||
// Send the hash request and verify the response
|
||||
if protocol <= ETH65 {
|
||||
p2p.Send(peer.app, GetReceiptsMsg, hashes)
|
||||
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, receipts); err != nil {
|
||||
t.Errorf("receipts mismatch: %v", err)
|
||||
}
|
||||
} else {
|
||||
p2p.Send(peer.app, GetReceiptsMsg, GetReceiptsPacket66{
|
||||
RequestId: 123,
|
||||
GetReceiptsPacket: hashes,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, ReceiptsPacket66{
|
||||
RequestId: 123,
|
||||
ReceiptsPacket: receipts,
|
||||
}); err != nil {
|
||||
t.Errorf("receipts mismatch: %v", err)
|
||||
}
|
||||
p2p.Send(peer.app, GetReceiptsMsg, GetReceiptsPacket66{
|
||||
RequestId: 123,
|
||||
GetReceiptsPacket: hashes,
|
||||
})
|
||||
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, ReceiptsPacket66{
|
||||
RequestId: 123,
|
||||
ReceiptsPacket: receipts,
|
||||
}); err != nil {
|
||||
t.Errorf("receipts mismatch: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,17 +27,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
// handleGetBlockHeaders handles Block header query, collect the requested headers and reply
|
||||
func handleGetBlockHeaders(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the complex header query
|
||||
var query GetBlockHeadersPacket
|
||||
if err := msg.Decode(&query); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
response := answerGetBlockHeadersQuery(backend, &query, peer)
|
||||
return peer.SendBlockHeaders(response)
|
||||
}
|
||||
|
||||
// handleGetBlockHeaders66 is the eth/66 version of handleGetBlockHeaders
|
||||
func handleGetBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the complex header query
|
||||
@@ -135,16 +124,6 @@ func answerGetBlockHeadersQuery(backend Backend, query *GetBlockHeadersPacket, p
|
||||
return headers
|
||||
}
|
||||
|
||||
func handleGetBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the block body retrieval message
|
||||
var query GetBlockBodiesPacket
|
||||
if err := msg.Decode(&query); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
response := answerGetBlockBodiesQuery(backend, query, peer)
|
||||
return peer.SendBlockBodiesRLP(response)
|
||||
}
|
||||
|
||||
func handleGetBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the block body retrieval message
|
||||
var query GetBlockBodiesPacket66
|
||||
@@ -174,16 +153,6 @@ func answerGetBlockBodiesQuery(backend Backend, query GetBlockBodiesPacket, peer
|
||||
return bodies
|
||||
}
|
||||
|
||||
func handleGetNodeData(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the trie node data retrieval message
|
||||
var query GetNodeDataPacket
|
||||
if err := msg.Decode(&query); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
response := answerGetNodeDataQuery(backend, query, peer)
|
||||
return peer.SendNodeData(response)
|
||||
}
|
||||
|
||||
func handleGetNodeData66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the trie node data retrieval message
|
||||
var query GetNodeDataPacket66
|
||||
@@ -223,16 +192,6 @@ func answerGetNodeDataQuery(backend Backend, query GetNodeDataPacket, peer *Peer
|
||||
return nodes
|
||||
}
|
||||
|
||||
func handleGetReceipts(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the block receipts retrieval message
|
||||
var query GetReceiptsPacket
|
||||
if err := msg.Decode(&query); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
response := answerGetReceiptsQuery(backend, query, peer)
|
||||
return peer.SendReceiptsRLP(response)
|
||||
}
|
||||
|
||||
func handleGetReceipts66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the block receipts retrieval message
|
||||
var query GetReceiptsPacket66
|
||||
@@ -312,15 +271,6 @@ func handleNewBlock(backend Backend, msg Decoder, peer *Peer) error {
|
||||
return backend.Handle(peer, ann)
|
||||
}
|
||||
|
||||
func handleBlockHeaders(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of headers arrived to one of our previous requests
|
||||
res := new(BlockHeadersPacket)
|
||||
if err := msg.Decode(res); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
return backend.Handle(peer, res)
|
||||
}
|
||||
|
||||
func handleBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of headers arrived to one of our previous requests
|
||||
res := new(BlockHeadersPacket66)
|
||||
@@ -332,15 +282,6 @@ func handleBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
return backend.Handle(peer, &res.BlockHeadersPacket)
|
||||
}
|
||||
|
||||
func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of block bodies arrived to one of our previous requests
|
||||
res := new(BlockBodiesPacket)
|
||||
if err := msg.Decode(res); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
return backend.Handle(peer, res)
|
||||
}
|
||||
|
||||
func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of block bodies arrived to one of our previous requests
|
||||
res := new(BlockBodiesPacket66)
|
||||
@@ -352,15 +293,6 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
return backend.Handle(peer, &res.BlockBodiesPacket)
|
||||
}
|
||||
|
||||
func handleNodeData(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of node state data arrived to one of our previous requests
|
||||
res := new(NodeDataPacket)
|
||||
if err := msg.Decode(res); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
return backend.Handle(peer, res)
|
||||
}
|
||||
|
||||
func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of node state data arrived to one of our previous requests
|
||||
res := new(NodeDataPacket66)
|
||||
@@ -372,15 +304,6 @@ func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
return backend.Handle(peer, &res.NodeDataPacket)
|
||||
}
|
||||
|
||||
func handleReceipts(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of receipts arrived to one of our previous requests
|
||||
res := new(ReceiptsPacket)
|
||||
if err := msg.Decode(res); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
return backend.Handle(peer, res)
|
||||
}
|
||||
|
||||
func handleReceipts66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// A batch of receipts arrived to one of our previous requests
|
||||
res := new(ReceiptsPacket66)
|
||||
@@ -409,16 +332,6 @@ func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer)
|
||||
return backend.Handle(peer, ann)
|
||||
}
|
||||
|
||||
func handleGetPooledTransactions(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the pooled transactions retrieval message
|
||||
var query GetPooledTransactionsPacket
|
||||
if err := msg.Decode(&query); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
hashes, txs := answerGetPooledTransactions(backend, query, peer)
|
||||
return peer.SendPooledTransactionsRLP(hashes, txs)
|
||||
}
|
||||
|
||||
func handleGetPooledTransactions66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Decode the pooled transactions retrieval message
|
||||
var query GetPooledTransactionsPacket66
|
||||
@@ -477,26 +390,6 @@ func handleTransactions(backend Backend, msg Decoder, peer *Peer) error {
|
||||
return backend.Handle(peer, &txs)
|
||||
}
|
||||
|
||||
func handlePooledTransactions(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Transactions arrived, make sure we have a valid and fresh chain to handle them
|
||||
if !backend.AcceptTxs() {
|
||||
return nil
|
||||
}
|
||||
// Transactions can be processed, parse all of them and deliver to the pool
|
||||
var txs PooledTransactionsPacket
|
||||
if err := msg.Decode(&txs); err != nil {
|
||||
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
|
||||
}
|
||||
for i, tx := range txs {
|
||||
// Validate and mark the remote transaction
|
||||
if tx == nil {
|
||||
return fmt.Errorf("%w: transaction %d is nil", errDecode, i)
|
||||
}
|
||||
peer.markTransaction(tx.Hash())
|
||||
}
|
||||
return backend.Handle(peer, &txs)
|
||||
}
|
||||
|
||||
func handlePooledTransactions66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
// Transactions arrived, make sure we have a valid and fresh chain to handle them
|
||||
if !backend.AcceptTxs() {
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
)
|
||||
|
||||
// Tests that handshake failures are detected and reported correctly.
|
||||
func TestHandshake65(t *testing.T) { testHandshake(t, ETH65) }
|
||||
func TestHandshake66(t *testing.T) { testHandshake(t, ETH66) }
|
||||
|
||||
func testHandshake(t *testing.T, protocol uint) {
|
||||
|
||||
+103
-186
@@ -75,12 +75,12 @@ type Peer struct {
|
||||
head common.Hash // Latest advertised head block hash
|
||||
td *big.Int // Latest advertised head block total difficulty
|
||||
|
||||
knownBlocks mapset.Set // Set of block hashes known to be known by this peer
|
||||
knownBlocks *knownCache // Set of block hashes known to be known by this peer
|
||||
queuedBlocks chan *blockPropagation // Queue of blocks to broadcast to the peer
|
||||
queuedBlockAnns chan *types.Block // Queue of blocks to announce to the peer
|
||||
|
||||
txpool TxPool // Transaction pool used by the broadcasters for liveness checks
|
||||
knownTxs mapset.Set // Set of transaction hashes known to be known by this peer
|
||||
knownTxs *knownCache // Set of transaction hashes known to be known by this peer
|
||||
txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests
|
||||
txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests
|
||||
|
||||
@@ -96,8 +96,8 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe
|
||||
Peer: p,
|
||||
rw: rw,
|
||||
version: version,
|
||||
knownTxs: mapset.NewSet(),
|
||||
knownBlocks: mapset.NewSet(),
|
||||
knownTxs: newKnownCache(maxKnownTxs),
|
||||
knownBlocks: newKnownCache(maxKnownBlocks),
|
||||
queuedBlocks: make(chan *blockPropagation, maxQueuedBlocks),
|
||||
queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns),
|
||||
txBroadcast: make(chan []common.Hash),
|
||||
@@ -108,9 +108,8 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe
|
||||
// Start up all the broadcasters
|
||||
go peer.broadcastBlocks()
|
||||
go peer.broadcastTransactions()
|
||||
if version >= ETH65 {
|
||||
go peer.announceTransactions()
|
||||
}
|
||||
go peer.announceTransactions()
|
||||
|
||||
return peer
|
||||
}
|
||||
|
||||
@@ -163,9 +162,6 @@ func (p *Peer) KnownTransaction(hash common.Hash) bool {
|
||||
// never be propagated to this particular peer.
|
||||
func (p *Peer) markBlock(hash common.Hash) {
|
||||
// If we reached the memory allowance, drop a previously known block hash
|
||||
for p.knownBlocks.Cardinality() >= maxKnownBlocks {
|
||||
p.knownBlocks.Pop()
|
||||
}
|
||||
p.knownBlocks.Add(hash)
|
||||
}
|
||||
|
||||
@@ -173,9 +169,6 @@ func (p *Peer) markBlock(hash common.Hash) {
|
||||
// will never be propagated to this particular peer.
|
||||
func (p *Peer) markTransaction(hash common.Hash) {
|
||||
// If we reached the memory allowance, drop a previously known transaction hash
|
||||
for p.knownTxs.Cardinality() >= maxKnownTxs {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
p.knownTxs.Add(hash)
|
||||
}
|
||||
|
||||
@@ -190,9 +183,6 @@ func (p *Peer) markTransaction(hash common.Hash) {
|
||||
// tests that directly send messages without having to do the asyn queueing.
|
||||
func (p *Peer) SendTransactions(txs types.Transactions) error {
|
||||
// Mark all the transactions as known, but ensure we don't overflow our limits
|
||||
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(txs)) {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
for _, tx := range txs {
|
||||
p.knownTxs.Add(tx.Hash())
|
||||
}
|
||||
@@ -206,12 +196,7 @@ func (p *Peer) AsyncSendTransactions(hashes []common.Hash) {
|
||||
select {
|
||||
case p.txBroadcast <- hashes:
|
||||
// Mark all the transactions as known, but ensure we don't overflow our limits
|
||||
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
p.knownTxs.Add(hash)
|
||||
}
|
||||
p.knownTxs.Add(hashes...)
|
||||
case <-p.term:
|
||||
p.Log().Debug("Dropping transaction propagation", "count", len(hashes))
|
||||
}
|
||||
@@ -225,12 +210,7 @@ func (p *Peer) AsyncSendTransactions(hashes []common.Hash) {
|
||||
// not be managed directly.
|
||||
func (p *Peer) sendPooledTransactionHashes(hashes []common.Hash) error {
|
||||
// Mark all the transactions as known, but ensure we don't overflow our limits
|
||||
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
p.knownTxs.Add(hash)
|
||||
}
|
||||
p.knownTxs.Add(hashes...)
|
||||
return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket(hashes))
|
||||
}
|
||||
|
||||
@@ -241,42 +221,17 @@ func (p *Peer) AsyncSendPooledTransactionHashes(hashes []common.Hash) {
|
||||
select {
|
||||
case p.txAnnounce <- hashes:
|
||||
// Mark all the transactions as known, but ensure we don't overflow our limits
|
||||
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
p.knownTxs.Add(hash)
|
||||
}
|
||||
p.knownTxs.Add(hashes...)
|
||||
case <-p.term:
|
||||
p.Log().Debug("Dropping transaction announcement", "count", len(hashes))
|
||||
}
|
||||
}
|
||||
|
||||
// SendPooledTransactionsRLP sends requested transactions to the peer and adds the
|
||||
// hashes in its transaction hash set for future reference.
|
||||
//
|
||||
// Note, the method assumes the hashes are correct and correspond to the list of
|
||||
// transactions being sent.
|
||||
func (p *Peer) SendPooledTransactionsRLP(hashes []common.Hash, txs []rlp.RawValue) error {
|
||||
// Mark all the transactions as known, but ensure we don't overflow our limits
|
||||
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
p.knownTxs.Add(hash)
|
||||
}
|
||||
return p2p.Send(p.rw, PooledTransactionsMsg, txs) // Not packed into PooledTransactionsPacket to avoid RLP decoding
|
||||
}
|
||||
|
||||
// ReplyPooledTransactionsRLP is the eth/66 version of SendPooledTransactionsRLP.
|
||||
func (p *Peer) ReplyPooledTransactionsRLP(id uint64, hashes []common.Hash, txs []rlp.RawValue) error {
|
||||
// Mark all the transactions as known, but ensure we don't overflow our limits
|
||||
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
|
||||
p.knownTxs.Pop()
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
p.knownTxs.Add(hash)
|
||||
}
|
||||
p.knownTxs.Add(hashes...)
|
||||
|
||||
// Not packed into PooledTransactionsPacket to avoid RLP decoding
|
||||
return p2p.Send(p.rw, PooledTransactionsMsg, PooledTransactionsRLPPacket66{
|
||||
RequestId: id,
|
||||
@@ -288,12 +243,8 @@ func (p *Peer) ReplyPooledTransactionsRLP(id uint64, hashes []common.Hash, txs [
|
||||
// a hash notification.
|
||||
func (p *Peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error {
|
||||
// Mark all the block hashes as known, but ensure we don't overflow our limits
|
||||
for p.knownBlocks.Cardinality() > max(0, maxKnownBlocks-len(hashes)) {
|
||||
p.knownBlocks.Pop()
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
p.knownBlocks.Add(hash)
|
||||
}
|
||||
p.knownBlocks.Add(hashes...)
|
||||
|
||||
request := make(NewBlockHashesPacket, len(hashes))
|
||||
for i := 0; i < len(hashes); i++ {
|
||||
request[i].Hash = hashes[i]
|
||||
@@ -309,9 +260,6 @@ func (p *Peer) AsyncSendNewBlockHash(block *types.Block) {
|
||||
select {
|
||||
case p.queuedBlockAnns <- block:
|
||||
// Mark all the block hash as known, but ensure we don't overflow our limits
|
||||
for p.knownBlocks.Cardinality() >= maxKnownBlocks {
|
||||
p.knownBlocks.Pop()
|
||||
}
|
||||
p.knownBlocks.Add(block.Hash())
|
||||
default:
|
||||
p.Log().Debug("Dropping block announcement", "number", block.NumberU64(), "hash", block.Hash())
|
||||
@@ -321,9 +269,6 @@ func (p *Peer) AsyncSendNewBlockHash(block *types.Block) {
|
||||
// SendNewBlock propagates an entire block to a remote peer.
|
||||
func (p *Peer) SendNewBlock(block *types.Block, td *big.Int) error {
|
||||
// Mark all the block hash as known, but ensure we don't overflow our limits
|
||||
for p.knownBlocks.Cardinality() >= maxKnownBlocks {
|
||||
p.knownBlocks.Pop()
|
||||
}
|
||||
p.knownBlocks.Add(block.Hash())
|
||||
return p2p.Send(p.rw, NewBlockMsg, &NewBlockPacket{
|
||||
Block: block,
|
||||
@@ -337,20 +282,12 @@ func (p *Peer) AsyncSendNewBlock(block *types.Block, td *big.Int) {
|
||||
select {
|
||||
case p.queuedBlocks <- &blockPropagation{block: block, td: td}:
|
||||
// Mark all the block hash as known, but ensure we don't overflow our limits
|
||||
for p.knownBlocks.Cardinality() >= maxKnownBlocks {
|
||||
p.knownBlocks.Pop()
|
||||
}
|
||||
p.knownBlocks.Add(block.Hash())
|
||||
default:
|
||||
p.Log().Debug("Dropping block propagation", "number", block.NumberU64(), "hash", block.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
// SendBlockHeaders sends a batch of block headers to the remote peer.
|
||||
func (p *Peer) SendBlockHeaders(headers []*types.Header) error {
|
||||
return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket(headers))
|
||||
}
|
||||
|
||||
// ReplyBlockHeaders is the eth/66 version of SendBlockHeaders.
|
||||
func (p *Peer) ReplyBlockHeaders(id uint64, headers []*types.Header) error {
|
||||
return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket66{
|
||||
@@ -359,12 +296,6 @@ func (p *Peer) ReplyBlockHeaders(id uint64, headers []*types.Header) error {
|
||||
})
|
||||
}
|
||||
|
||||
// SendBlockBodiesRLP sends a batch of block contents to the remote peer from
|
||||
// an already RLP encoded format.
|
||||
func (p *Peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error {
|
||||
return p2p.Send(p.rw, BlockBodiesMsg, bodies) // Not packed into BlockBodiesPacket to avoid RLP decoding
|
||||
}
|
||||
|
||||
// ReplyBlockBodiesRLP is the eth/66 version of SendBlockBodiesRLP.
|
||||
func (p *Peer) ReplyBlockBodiesRLP(id uint64, bodies []rlp.RawValue) error {
|
||||
// Not packed into BlockBodiesPacket to avoid RLP decoding
|
||||
@@ -374,12 +305,6 @@ func (p *Peer) ReplyBlockBodiesRLP(id uint64, bodies []rlp.RawValue) error {
|
||||
})
|
||||
}
|
||||
|
||||
// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the
|
||||
// hashes requested.
|
||||
func (p *Peer) SendNodeData(data [][]byte) error {
|
||||
return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket(data))
|
||||
}
|
||||
|
||||
// ReplyNodeData is the eth/66 response to GetNodeData.
|
||||
func (p *Peer) ReplyNodeData(id uint64, data [][]byte) error {
|
||||
return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket66{
|
||||
@@ -388,12 +313,6 @@ func (p *Peer) ReplyNodeData(id uint64, data [][]byte) error {
|
||||
})
|
||||
}
|
||||
|
||||
// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the
|
||||
// ones requested from an already RLP encoded format.
|
||||
func (p *Peer) SendReceiptsRLP(receipts []rlp.RawValue) error {
|
||||
return p2p.Send(p.rw, ReceiptsMsg, receipts) // Not packed into ReceiptsPacket to avoid RLP decoding
|
||||
}
|
||||
|
||||
// ReplyReceiptsRLP is the eth/66 response to GetReceipts.
|
||||
func (p *Peer) ReplyReceiptsRLP(id uint64, receipts []rlp.RawValue) error {
|
||||
return p2p.Send(p.rw, ReceiptsMsg, ReceiptsRLPPacket66{
|
||||
@@ -406,138 +325,136 @@ func (p *Peer) ReplyReceiptsRLP(id uint64, receipts []rlp.RawValue) error {
|
||||
// single header. It is used solely by the fetcher.
|
||||
func (p *Peer) RequestOneHeader(hash common.Hash) error {
|
||||
p.Log().Debug("Fetching single header", "hash", hash)
|
||||
query := GetBlockHeadersPacket{
|
||||
Origin: HashOrNumber{Hash: hash},
|
||||
Amount: uint64(1),
|
||||
Skip: uint64(0),
|
||||
Reverse: false,
|
||||
}
|
||||
if p.Version() >= ETH66 {
|
||||
id := rand.Uint64()
|
||||
id := rand.Uint64()
|
||||
|
||||
requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
GetBlockHeadersPacket: &query,
|
||||
})
|
||||
}
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &query)
|
||||
requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
GetBlockHeadersPacket: &GetBlockHeadersPacket{
|
||||
Origin: HashOrNumber{Hash: hash},
|
||||
Amount: uint64(1),
|
||||
Skip: uint64(0),
|
||||
Reverse: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
|
||||
// specified header query, based on the hash of an origin block.
|
||||
func (p *Peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error {
|
||||
p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse)
|
||||
query := GetBlockHeadersPacket{
|
||||
Origin: HashOrNumber{Hash: origin},
|
||||
Amount: uint64(amount),
|
||||
Skip: uint64(skip),
|
||||
Reverse: reverse,
|
||||
}
|
||||
if p.Version() >= ETH66 {
|
||||
id := rand.Uint64()
|
||||
id := rand.Uint64()
|
||||
|
||||
requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
GetBlockHeadersPacket: &query,
|
||||
})
|
||||
}
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &query)
|
||||
requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
GetBlockHeadersPacket: &GetBlockHeadersPacket{
|
||||
Origin: HashOrNumber{Hash: origin},
|
||||
Amount: uint64(amount),
|
||||
Skip: uint64(skip),
|
||||
Reverse: reverse,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the
|
||||
// specified header query, based on the number of an origin block.
|
||||
func (p *Peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
|
||||
p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse)
|
||||
query := GetBlockHeadersPacket{
|
||||
Origin: HashOrNumber{Number: origin},
|
||||
Amount: uint64(amount),
|
||||
Skip: uint64(skip),
|
||||
Reverse: reverse,
|
||||
}
|
||||
if p.Version() >= ETH66 {
|
||||
id := rand.Uint64()
|
||||
id := rand.Uint64()
|
||||
|
||||
requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
GetBlockHeadersPacket: &query,
|
||||
})
|
||||
}
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &query)
|
||||
}
|
||||
|
||||
// ExpectRequestHeadersByNumber is a testing method to mirror the recipient side
|
||||
// of the RequestHeadersByNumber operation.
|
||||
func (p *Peer) ExpectRequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
|
||||
req := &GetBlockHeadersPacket{
|
||||
Origin: HashOrNumber{Number: origin},
|
||||
Amount: uint64(amount),
|
||||
Skip: uint64(skip),
|
||||
Reverse: reverse,
|
||||
}
|
||||
return p2p.ExpectMsg(p.rw, GetBlockHeadersMsg, req)
|
||||
requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
GetBlockHeadersPacket: &GetBlockHeadersPacket{
|
||||
Origin: HashOrNumber{Number: origin},
|
||||
Amount: uint64(amount),
|
||||
Skip: uint64(skip),
|
||||
Reverse: reverse,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes
|
||||
// specified.
|
||||
func (p *Peer) RequestBodies(hashes []common.Hash) error {
|
||||
p.Log().Debug("Fetching batch of block bodies", "count", len(hashes))
|
||||
if p.Version() >= ETH66 {
|
||||
id := rand.Uint64()
|
||||
id := rand.Uint64()
|
||||
|
||||
requestTracker.Track(p.id, p.version, GetBlockBodiesMsg, BlockBodiesMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
|
||||
RequestId: id,
|
||||
GetBlockBodiesPacket: hashes,
|
||||
})
|
||||
}
|
||||
return p2p.Send(p.rw, GetBlockBodiesMsg, GetBlockBodiesPacket(hashes))
|
||||
requestTracker.Track(p.id, p.version, GetBlockBodiesMsg, BlockBodiesMsg, id)
|
||||
return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
|
||||
RequestId: id,
|
||||
GetBlockBodiesPacket: hashes,
|
||||
})
|
||||
}
|
||||
|
||||
// RequestNodeData fetches a batch of arbitrary data from a node's known state
|
||||
// data, corresponding to the specified hashes.
|
||||
func (p *Peer) RequestNodeData(hashes []common.Hash) error {
|
||||
p.Log().Debug("Fetching batch of state data", "count", len(hashes))
|
||||
if p.Version() >= ETH66 {
|
||||
id := rand.Uint64()
|
||||
id := rand.Uint64()
|
||||
|
||||
requestTracker.Track(p.id, p.version, GetNodeDataMsg, NodeDataMsg, id)
|
||||
return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket66{
|
||||
RequestId: id,
|
||||
GetNodeDataPacket: hashes,
|
||||
})
|
||||
}
|
||||
return p2p.Send(p.rw, GetNodeDataMsg, GetNodeDataPacket(hashes))
|
||||
requestTracker.Track(p.id, p.version, GetNodeDataMsg, NodeDataMsg, id)
|
||||
return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket66{
|
||||
RequestId: id,
|
||||
GetNodeDataPacket: hashes,
|
||||
})
|
||||
}
|
||||
|
||||
// RequestReceipts fetches a batch of transaction receipts from a remote node.
|
||||
func (p *Peer) RequestReceipts(hashes []common.Hash) error {
|
||||
p.Log().Debug("Fetching batch of receipts", "count", len(hashes))
|
||||
if p.Version() >= ETH66 {
|
||||
id := rand.Uint64()
|
||||
id := rand.Uint64()
|
||||
|
||||
requestTracker.Track(p.id, p.version, GetReceiptsMsg, ReceiptsMsg, id)
|
||||
return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket66{
|
||||
RequestId: id,
|
||||
GetReceiptsPacket: hashes,
|
||||
})
|
||||
}
|
||||
return p2p.Send(p.rw, GetReceiptsMsg, GetReceiptsPacket(hashes))
|
||||
requestTracker.Track(p.id, p.version, GetReceiptsMsg, ReceiptsMsg, id)
|
||||
return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket66{
|
||||
RequestId: id,
|
||||
GetReceiptsPacket: hashes,
|
||||
})
|
||||
}
|
||||
|
||||
// RequestTxs fetches a batch of transactions from a remote node.
|
||||
func (p *Peer) RequestTxs(hashes []common.Hash) error {
|
||||
p.Log().Debug("Fetching batch of transactions", "count", len(hashes))
|
||||
if p.Version() >= ETH66 {
|
||||
id := rand.Uint64()
|
||||
id := rand.Uint64()
|
||||
|
||||
requestTracker.Track(p.id, p.version, GetPooledTransactionsMsg, PooledTransactionsMsg, id)
|
||||
return p2p.Send(p.rw, GetPooledTransactionsMsg, &GetPooledTransactionsPacket66{
|
||||
RequestId: id,
|
||||
GetPooledTransactionsPacket: hashes,
|
||||
})
|
||||
requestTracker.Track(p.id, p.version, GetPooledTransactionsMsg, PooledTransactionsMsg, id)
|
||||
return p2p.Send(p.rw, GetPooledTransactionsMsg, &GetPooledTransactionsPacket66{
|
||||
RequestId: id,
|
||||
GetPooledTransactionsPacket: hashes,
|
||||
})
|
||||
}
|
||||
|
||||
// knownCache is a cache for known hashes.
|
||||
type knownCache struct {
|
||||
hashes mapset.Set
|
||||
max int
|
||||
}
|
||||
|
||||
// newKnownCache creates a new knownCache with a max capacity.
|
||||
func newKnownCache(max int) *knownCache {
|
||||
return &knownCache{
|
||||
max: max,
|
||||
hashes: mapset.NewSet(),
|
||||
}
|
||||
return p2p.Send(p.rw, GetPooledTransactionsMsg, GetPooledTransactionsPacket(hashes))
|
||||
}
|
||||
|
||||
// Add adds a list of elements to the set.
|
||||
func (k *knownCache) Add(hashes ...common.Hash) {
|
||||
for k.hashes.Cardinality() > max(0, k.max-len(hashes)) {
|
||||
k.hashes.Pop()
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
k.hashes.Add(hash)
|
||||
}
|
||||
}
|
||||
|
||||
// Contains returns whether the given item is in the set.
|
||||
func (k *knownCache) Contains(hash common.Hash) bool {
|
||||
return k.hashes.Contains(hash)
|
||||
}
|
||||
|
||||
// Cardinality returns the number of elements in the set.
|
||||
func (k *knownCache) Cardinality() int {
|
||||
return k.hashes.Cardinality()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ package eth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
@@ -59,3 +61,28 @@ func (p *testPeer) close() {
|
||||
p.Peer.Close()
|
||||
p.app.Close()
|
||||
}
|
||||
|
||||
func TestPeerSet(t *testing.T) {
|
||||
size := 5
|
||||
s := newKnownCache(size)
|
||||
|
||||
// add 10 items
|
||||
for i := 0; i < size*2; i++ {
|
||||
s.Add(common.Hash{byte(i)})
|
||||
}
|
||||
|
||||
if s.Cardinality() != size {
|
||||
t.Fatalf("wrong size, expected %d but found %d", size, s.Cardinality())
|
||||
}
|
||||
|
||||
vals := []common.Hash{}
|
||||
for i := 10; i < 20; i++ {
|
||||
vals = append(vals, common.Hash{byte(i)})
|
||||
}
|
||||
|
||||
// add item in batch
|
||||
s.Add(vals...)
|
||||
if s.Cardinality() < size {
|
||||
t.Fatalf("bad size")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
|
||||
// Constants to match up protocol versions and messages
|
||||
const (
|
||||
ETH65 = 65
|
||||
ETH66 = 66
|
||||
)
|
||||
|
||||
@@ -40,31 +39,28 @@ const ProtocolName = "eth"
|
||||
|
||||
// ProtocolVersions are the supported versions of the `eth` protocol (first
|
||||
// is primary).
|
||||
var ProtocolVersions = []uint{ETH66, ETH65}
|
||||
var ProtocolVersions = []uint{ETH66}
|
||||
|
||||
// protocolLengths are the number of implemented message corresponding to
|
||||
// different protocol versions.
|
||||
var protocolLengths = map[uint]uint64{ETH66: 17, ETH65: 17}
|
||||
var protocolLengths = map[uint]uint64{ETH66: 17}
|
||||
|
||||
// maxMessageSize is the maximum cap on the size of a protocol message.
|
||||
const maxMessageSize = 10 * 1024 * 1024
|
||||
|
||||
const (
|
||||
// Protocol messages in eth/64
|
||||
StatusMsg = 0x00
|
||||
NewBlockHashesMsg = 0x01
|
||||
TransactionsMsg = 0x02
|
||||
GetBlockHeadersMsg = 0x03
|
||||
BlockHeadersMsg = 0x04
|
||||
GetBlockBodiesMsg = 0x05
|
||||
BlockBodiesMsg = 0x06
|
||||
NewBlockMsg = 0x07
|
||||
GetNodeDataMsg = 0x0d
|
||||
NodeDataMsg = 0x0e
|
||||
GetReceiptsMsg = 0x0f
|
||||
ReceiptsMsg = 0x10
|
||||
|
||||
// Protocol messages overloaded in eth/65
|
||||
StatusMsg = 0x00
|
||||
NewBlockHashesMsg = 0x01
|
||||
TransactionsMsg = 0x02
|
||||
GetBlockHeadersMsg = 0x03
|
||||
BlockHeadersMsg = 0x04
|
||||
GetBlockBodiesMsg = 0x05
|
||||
BlockBodiesMsg = 0x06
|
||||
NewBlockMsg = 0x07
|
||||
GetNodeDataMsg = 0x0d
|
||||
NodeDataMsg = 0x0e
|
||||
GetReceiptsMsg = 0x0f
|
||||
ReceiptsMsg = 0x10
|
||||
NewPooledTransactionHashesMsg = 0x08
|
||||
GetPooledTransactionsMsg = 0x09
|
||||
PooledTransactionsMsg = 0x0a
|
||||
@@ -128,7 +124,7 @@ type GetBlockHeadersPacket struct {
|
||||
Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis)
|
||||
}
|
||||
|
||||
// GetBlockHeadersPacket represents a block header query over eth/66
|
||||
// GetBlockHeadersPacket66 represents a block header query over eth/66
|
||||
type GetBlockHeadersPacket66 struct {
|
||||
RequestId uint64
|
||||
*GetBlockHeadersPacket
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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/light"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
@@ -319,7 +319,7 @@ func handleMessage(backend Backend, peer *Peer) error {
|
||||
if err != nil {
|
||||
return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID})
|
||||
}
|
||||
var acc state.Account
|
||||
var acc types.StateAccount
|
||||
if err := rlp.DecodeBytes(accTrie.Get(account[:]), &acc); err != nil {
|
||||
return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID})
|
||||
}
|
||||
@@ -469,7 +469,7 @@ func handleMessage(backend Backend, peer *Peer) error {
|
||||
// Storage slots requested, open the storage trie and retrieve from there
|
||||
account, err := snap.Account(common.BytesToHash(pathset[0]))
|
||||
loads++ // always account database reads, even for failures
|
||||
if err != nil {
|
||||
if err != nil || account == nil {
|
||||
break
|
||||
}
|
||||
stTrie, err := trie.NewSecure(common.BytesToHash(account.Root), triedb)
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
@@ -125,8 +126,8 @@ type accountRequest struct {
|
||||
type accountResponse struct {
|
||||
task *accountTask // Task which this request is filling
|
||||
|
||||
hashes []common.Hash // Account hashes in the returned range
|
||||
accounts []*state.Account // Expanded accounts in the returned range
|
||||
hashes []common.Hash // Account hashes in the returned range
|
||||
accounts []*types.StateAccount // Expanded accounts in the returned range
|
||||
|
||||
cont bool // Whether the account range has a continuation
|
||||
}
|
||||
@@ -2274,9 +2275,9 @@ func (s *Syncer) OnAccounts(peer SyncPeer, id uint64, hashes []common.Hash, acco
|
||||
s.scheduleRevertAccountRequest(req)
|
||||
return err
|
||||
}
|
||||
accs := make([]*state.Account, len(accounts))
|
||||
accs := make([]*types.StateAccount, len(accounts))
|
||||
for i, account := range accounts {
|
||||
acc := new(state.Account)
|
||||
acc := new(types.StateAccount)
|
||||
if err := rlp.DecodeBytes(account, acc); err != nil {
|
||||
panic(err) // We created these blobs, we must be able to decode them
|
||||
}
|
||||
@@ -2740,7 +2741,7 @@ func (s *Syncer) onHealByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) e
|
||||
// Note it's not concurrent safe, please handle the concurrent issue outside.
|
||||
func (s *Syncer) onHealState(paths [][]byte, value []byte) error {
|
||||
if len(paths) == 1 {
|
||||
var account state.Account
|
||||
var account types.StateAccount
|
||||
if err := rlp.DecodeBytes(value, &account); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
@@ -1349,7 +1349,7 @@ func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) {
|
||||
accTrie, _ := trie.New(common.Hash{}, db)
|
||||
var entries entrySlice
|
||||
for i := uint64(1); i <= uint64(n); i++ {
|
||||
value, _ := rlp.EncodeToBytes(state.Account{
|
||||
value, _ := rlp.EncodeToBytes(types.StateAccount{
|
||||
Nonce: i,
|
||||
Balance: big.NewInt(int64(i)),
|
||||
Root: emptyRoot,
|
||||
@@ -1394,7 +1394,7 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
|
||||
}
|
||||
// Fill boundary accounts
|
||||
for i := 0; i < len(boundaries); i++ {
|
||||
value, _ := rlp.EncodeToBytes(state.Account{
|
||||
value, _ := rlp.EncodeToBytes(types.StateAccount{
|
||||
Nonce: uint64(0),
|
||||
Balance: big.NewInt(int64(i)),
|
||||
Root: emptyRoot,
|
||||
@@ -1406,7 +1406,7 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
|
||||
}
|
||||
// Fill other accounts if required
|
||||
for i := uint64(1); i <= uint64(n); i++ {
|
||||
value, _ := rlp.EncodeToBytes(state.Account{
|
||||
value, _ := rlp.EncodeToBytes(types.StateAccount{
|
||||
Nonce: i,
|
||||
Balance: big.NewInt(int64(i)),
|
||||
Root: emptyRoot,
|
||||
@@ -1442,7 +1442,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
|
||||
stTrie, stEntries := makeStorageTrieWithSeed(uint64(slots), i, db)
|
||||
stRoot := stTrie.Hash()
|
||||
stTrie.Commit(nil)
|
||||
value, _ := rlp.EncodeToBytes(state.Account{
|
||||
value, _ := rlp.EncodeToBytes(types.StateAccount{
|
||||
Nonce: i,
|
||||
Balance: big.NewInt(int64(i)),
|
||||
Root: stRoot,
|
||||
@@ -1489,7 +1489,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie
|
||||
if code {
|
||||
codehash = getCodeHash(i)
|
||||
}
|
||||
value, _ := rlp.EncodeToBytes(state.Account{
|
||||
value, _ := rlp.EncodeToBytes(types.StateAccount{
|
||||
Nonce: i,
|
||||
Balance: big.NewInt(int64(i)),
|
||||
Root: stRoot,
|
||||
|
||||
@@ -119,7 +119,8 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w",
|
||||
current.NumberU64(), current.Root().Hex(), err)
|
||||
}
|
||||
statedb, err = state.New(root, database, nil)
|
||||
if err != nil {
|
||||
|
||||
+4
-98
@@ -18,7 +18,6 @@ package eth
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -28,23 +27,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
const (
|
||||
forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available
|
||||
defaultMinSyncPeers = 5 // Amount of peers desired to start syncing
|
||||
|
||||
// This is the target size for the packs of transactions sent by txsyncLoop64.
|
||||
// A pack can get larger than this if a single transactions exceeds this size.
|
||||
txsyncPackSize = 100 * 1024
|
||||
)
|
||||
|
||||
type txsync struct {
|
||||
p *eth.Peer
|
||||
txs []*types.Transaction
|
||||
}
|
||||
|
||||
// syncTransactions starts sending all currently pending transactions to the given peer.
|
||||
func (h *handler) syncTransactions(p *eth.Peer) {
|
||||
// Assemble the set of transaction to broadcast or announce to the remote
|
||||
@@ -64,94 +53,11 @@ func (h *handler) syncTransactions(p *eth.Peer) {
|
||||
// The eth/65 protocol introduces proper transaction announcements, so instead
|
||||
// of dripping transactions across multiple peers, just send the entire list as
|
||||
// an announcement and let the remote side decide what they need (likely nothing).
|
||||
if p.Version() >= eth.ETH65 {
|
||||
hashes := make([]common.Hash, len(txs))
|
||||
for i, tx := range txs {
|
||||
hashes[i] = tx.Hash()
|
||||
}
|
||||
p.AsyncSendPooledTransactionHashes(hashes)
|
||||
return
|
||||
}
|
||||
// Out of luck, peer is running legacy protocols, drop the txs over
|
||||
select {
|
||||
case h.txsyncCh <- &txsync{p: p, txs: txs}:
|
||||
case <-h.quitSync:
|
||||
}
|
||||
}
|
||||
|
||||
// txsyncLoop64 takes care of the initial transaction sync for each new
|
||||
// connection. When a new peer appears, we relay all currently pending
|
||||
// transactions. In order to minimise egress bandwidth usage, we send
|
||||
// the transactions in small packs to one peer at a time.
|
||||
func (h *handler) txsyncLoop64() {
|
||||
defer h.wg.Done()
|
||||
|
||||
var (
|
||||
pending = make(map[enode.ID]*txsync)
|
||||
sending = false // whether a send is active
|
||||
pack = new(txsync) // the pack that is being sent
|
||||
done = make(chan error, 1) // result of the send
|
||||
)
|
||||
|
||||
// send starts a sending a pack of transactions from the sync.
|
||||
send := func(s *txsync) {
|
||||
if s.p.Version() >= eth.ETH65 {
|
||||
panic("initial transaction syncer running on eth/65+")
|
||||
}
|
||||
// Fill pack with transactions up to the target size.
|
||||
size := common.StorageSize(0)
|
||||
pack.p = s.p
|
||||
pack.txs = pack.txs[:0]
|
||||
for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ {
|
||||
pack.txs = append(pack.txs, s.txs[i])
|
||||
size += s.txs[i].Size()
|
||||
}
|
||||
// Remove the transactions that will be sent.
|
||||
s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])]
|
||||
if len(s.txs) == 0 {
|
||||
delete(pending, s.p.Peer.ID())
|
||||
}
|
||||
// Send the pack in the background.
|
||||
s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size)
|
||||
sending = true
|
||||
go func() { done <- pack.p.SendTransactions(pack.txs) }()
|
||||
}
|
||||
// pick chooses the next pending sync.
|
||||
pick := func() *txsync {
|
||||
if len(pending) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := rand.Intn(len(pending)) + 1
|
||||
for _, s := range pending {
|
||||
if n--; n == 0 {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case s := <-h.txsyncCh:
|
||||
pending[s.p.Peer.ID()] = s
|
||||
if !sending {
|
||||
send(s)
|
||||
}
|
||||
case err := <-done:
|
||||
sending = false
|
||||
// Stop tracking peers that cause send failures.
|
||||
if err != nil {
|
||||
pack.p.Log().Debug("Transaction send failed", "err", err)
|
||||
delete(pending, pack.p.Peer.ID())
|
||||
}
|
||||
// Schedule the next send.
|
||||
if s := pick(); s != nil {
|
||||
send(s)
|
||||
}
|
||||
case <-h.quitSync:
|
||||
return
|
||||
}
|
||||
hashes := make([]common.Hash, len(txs))
|
||||
for i, tx := range txs {
|
||||
hashes[i] = tx.Hash()
|
||||
}
|
||||
p.AsyncSendPooledTransactionHashes(hashes)
|
||||
}
|
||||
|
||||
// chainSyncer coordinates blockchain sync components.
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
)
|
||||
|
||||
// Tests that fast sync is disabled after a successful sync cycle.
|
||||
func TestFastSyncDisabling65(t *testing.T) { testFastSyncDisabling(t, eth.ETH65) }
|
||||
func TestFastSyncDisabling66(t *testing.T) { testFastSyncDisabling(t, eth.ETH66) }
|
||||
|
||||
// Tests that fast sync gets disabled as soon as a real block is successfully
|
||||
|
||||
+82
-16
@@ -291,7 +291,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
|
||||
}()
|
||||
}
|
||||
// Start a goroutine to feed all the blocks into the tracers
|
||||
begin := time.Now()
|
||||
var (
|
||||
begin = time.Now()
|
||||
derefTodo []common.Hash // list of hashes to dereference from the db
|
||||
derefsMu sync.Mutex // mutex for the derefs
|
||||
)
|
||||
|
||||
go func() {
|
||||
var (
|
||||
@@ -325,6 +329,14 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
|
||||
return
|
||||
default:
|
||||
}
|
||||
// clean out any derefs
|
||||
derefsMu.Lock()
|
||||
for _, h := range derefTodo {
|
||||
statedb.Database().TrieDB().Dereference(h)
|
||||
}
|
||||
derefTodo = derefTodo[:0]
|
||||
derefsMu.Unlock()
|
||||
|
||||
// Print progress logs if long enough time elapsed
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
logged = time.Now()
|
||||
@@ -383,12 +395,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
|
||||
Hash: res.block.Hash(),
|
||||
Traces: res.results,
|
||||
}
|
||||
// Schedule any parent tries held in memory by this task for dereferencing
|
||||
done[uint64(result.Block)] = result
|
||||
|
||||
// Dereference any parent tries held in memory by this task
|
||||
if res.statedb.Database().TrieDB() != nil {
|
||||
res.statedb.Database().TrieDB().Dereference(res.rootref)
|
||||
}
|
||||
derefsMu.Lock()
|
||||
derefTodo = append(derefTodo, res.rootref)
|
||||
derefsMu.Unlock()
|
||||
// Stream completed traces to the user, aborting on the first error
|
||||
for result, ok := done[next]; ok; result, ok = done[next] {
|
||||
if len(result.Traces) > 0 || next == end.NumberU64() {
|
||||
@@ -446,12 +457,11 @@ func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *Tra
|
||||
// EVM against a block pulled from the pool of bad ones and returns them as a JSON
|
||||
// object.
|
||||
func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
|
||||
for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) {
|
||||
if block.Hash() == hash {
|
||||
return api.traceBlock(ctx, block, config)
|
||||
}
|
||||
block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("bad block %#x not found", hash)
|
||||
}
|
||||
return nil, fmt.Errorf("bad block %#x not found", hash)
|
||||
return api.traceBlock(ctx, block, config)
|
||||
}
|
||||
|
||||
// StandardTraceBlockToFile dumps the structured logs created during the
|
||||
@@ -465,16 +475,72 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash,
|
||||
return api.standardTraceBlockToFile(ctx, block, config)
|
||||
}
|
||||
|
||||
// IntermediateRoots executes a block (bad- or canon- or side-), and returns a list
|
||||
// of intermediate roots: the stateroot after each transaction.
|
||||
func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) {
|
||||
block, _ := api.blockByHash(ctx, hash)
|
||||
if block == nil {
|
||||
// Check in the bad blocks
|
||||
block = rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
|
||||
}
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("block %#x not found", hash)
|
||||
}
|
||||
if block.NumberU64() == 0 {
|
||||
return nil, errors.New("genesis is not traceable")
|
||||
}
|
||||
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reexec := defaultTraceReexec
|
||||
if config != nil && config.Reexec != nil {
|
||||
reexec = *config.Reexec
|
||||
}
|
||||
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
roots []common.Hash
|
||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
||||
chainConfig = api.backend.ChainConfig()
|
||||
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
|
||||
)
|
||||
for i, tx := range block.Transactions() {
|
||||
var (
|
||||
msg, _ = tx.AsMessage(signer, block.BaseFee())
|
||||
txContext = core.NewEVMTxContext(msg)
|
||||
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
|
||||
)
|
||||
statedb.Prepare(tx.Hash(), i)
|
||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
|
||||
// We intentionally don't return the error here: if we do, then the RPC server will not
|
||||
// return the roots. Most likely, the caller already knows that a certain transaction fails to
|
||||
// be included, but still want the intermediate roots that led to that point.
|
||||
// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
|
||||
// executable.
|
||||
// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
|
||||
return roots, nil
|
||||
}
|
||||
// calling IntermediateRoot will internally call Finalize on the state
|
||||
// so any modifications are written to the trie
|
||||
roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects))
|
||||
}
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
// StandardTraceBadBlockToFile dumps the structured logs created during the
|
||||
// execution of EVM against a block pulled from the pool of bad ones to the
|
||||
// local file system and returns a list of files to the caller.
|
||||
func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
|
||||
for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) {
|
||||
if block.Hash() == hash {
|
||||
return api.standardTraceBlockToFile(ctx, block, config)
|
||||
}
|
||||
block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("bad block %#x not found", hash)
|
||||
}
|
||||
return nil, fmt.Errorf("bad block %#x not found", hash)
|
||||
return api.standardTraceBlockToFile(ctx, block, config)
|
||||
}
|
||||
|
||||
// traceBlock configures a new tracer according to the provided configuration, and
|
||||
|
||||
@@ -31,48 +31,27 @@
|
||||
// ids aggregates the 4byte ids found.
|
||||
ids : {},
|
||||
|
||||
// callType returns 'false' for non-calls, or the peek-index for the first param
|
||||
// after 'value', i.e. meminstart.
|
||||
callType: function(opstr){
|
||||
switch(opstr){
|
||||
case "CALL": case "CALLCODE":
|
||||
// gas, addr, val, memin, meminsz, memout, memoutsz
|
||||
return 3; // stack ptr to memin
|
||||
|
||||
case "DELEGATECALL": case "STATICCALL":
|
||||
// gas, addr, memin, meminsz, memout, memoutsz
|
||||
return 2; // stack ptr to memin
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// store save the given indentifier and datasize.
|
||||
store: function(id, size){
|
||||
var key = "" + toHex(id) + "-" + size;
|
||||
this.ids[key] = this.ids[key] + 1 || 1;
|
||||
},
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// Skip any opcodes that are not internal calls
|
||||
var ct = this.callType(log.op.toString());
|
||||
if (!ct) {
|
||||
return;
|
||||
}
|
||||
enter: function(frame) {
|
||||
// Skip any pre-compile invocations, those are just fancy opcodes
|
||||
if (isPrecompiled(toAddress(log.stack.peek(1).toString(16)))) {
|
||||
if (isPrecompiled(frame.getTo())) {
|
||||
return;
|
||||
}
|
||||
// Gather internal call details
|
||||
var inSz = log.stack.peek(ct + 1).valueOf();
|
||||
if (inSz >= 4) {
|
||||
var inOff = log.stack.peek(ct).valueOf();
|
||||
this.store(log.memory.slice(inOff, inOff + 4), inSz-4);
|
||||
var input = frame.getInput()
|
||||
if (input.length >= 4) {
|
||||
this.store(slice(input, 0, 4), input.length - 4);
|
||||
}
|
||||
},
|
||||
|
||||
exit: function(frameResult) {},
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) { },
|
||||
fault: function(log, db) {},
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// 4byteTracer searches for 4byte-identifiers, and collects them for post-processing.
|
||||
// It collects the methods identifiers along with the size of the supplied data, so
|
||||
// a reversed signature can be matched against the size of the data.
|
||||
//
|
||||
// Example:
|
||||
// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
|
||||
// {
|
||||
// 0x27dc297e-128: 1,
|
||||
// 0x38cc4831-0: 2,
|
||||
// 0x524f3889-96: 1,
|
||||
// 0xadf59f99-288: 1,
|
||||
// 0xc281d19e-0: 1
|
||||
// }
|
||||
{
|
||||
// ids aggregates the 4byte ids found.
|
||||
ids : {},
|
||||
|
||||
// callType returns 'false' for non-calls, or the peek-index for the first param
|
||||
// after 'value', i.e. meminstart.
|
||||
callType: function(opstr){
|
||||
switch(opstr){
|
||||
case "CALL": case "CALLCODE":
|
||||
// gas, addr, val, memin, meminsz, memout, memoutsz
|
||||
return 3; // stack ptr to memin
|
||||
|
||||
case "DELEGATECALL": case "STATICCALL":
|
||||
// gas, addr, memin, meminsz, memout, memoutsz
|
||||
return 2; // stack ptr to memin
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// store save the given indentifier and datasize.
|
||||
store: function(id, size){
|
||||
var key = "" + toHex(id) + "-" + size;
|
||||
this.ids[key] = this.ids[key] + 1 || 1;
|
||||
},
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// Skip any opcodes that are not internal calls
|
||||
var ct = this.callType(log.op.toString());
|
||||
if (!ct) {
|
||||
return;
|
||||
}
|
||||
// Skip any pre-compile invocations, those are just fancy opcodes
|
||||
if (isPrecompiled(toAddress(log.stack.peek(1).toString(16)))) {
|
||||
return;
|
||||
}
|
||||
// Gather internal call details
|
||||
var inSz = log.stack.peek(ct + 1).valueOf();
|
||||
if (inSz >= 4) {
|
||||
var inOff = log.stack.peek(ct).valueOf();
|
||||
this.store(log.memory.slice(inOff, inOff + 4), inSz-4);
|
||||
}
|
||||
},
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) { },
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx) {
|
||||
// Save the outer calldata also
|
||||
if (ctx.input.length >= 4) {
|
||||
this.store(slice(ctx.input, 0, 4), ctx.input.length-4)
|
||||
}
|
||||
return this.ids;
|
||||
},
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
// Copyright 2017 The go-ethereum Authors
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
@@ -14,212 +14,81 @@
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// callTracer is a full blown transaction tracer that extracts and reports all
|
||||
// the internal calls made by a transaction, along with any useful information.
|
||||
|
||||
// callFrameTracer uses the new call frame tracing methods to report useful information
|
||||
// about internal messages of a transaction.
|
||||
{
|
||||
// callstack is the current recursive call stack of the EVM execution.
|
||||
callstack: [{}],
|
||||
|
||||
// descended tracks whether we've just descended from an outer transaction into
|
||||
// an inner call.
|
||||
descended: false,
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// Capture any errors immediately
|
||||
var error = log.getError();
|
||||
if (error !== undefined) {
|
||||
this.fault(log, db);
|
||||
return;
|
||||
}
|
||||
// We only care about system opcodes, faster if we pre-check once
|
||||
var syscall = (log.op.toNumber() & 0xf0) == 0xf0;
|
||||
if (syscall) {
|
||||
var op = log.op.toString();
|
||||
}
|
||||
// If a new contract is being created, add to the call stack
|
||||
if (syscall && (op == 'CREATE' || op == "CREATE2")) {
|
||||
var inOff = log.stack.peek(1).valueOf();
|
||||
var inEnd = inOff + log.stack.peek(2).valueOf();
|
||||
|
||||
// Assemble the internal call report and store for completion
|
||||
var call = {
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
input: toHex(log.memory.slice(inOff, inEnd)),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
value: '0x' + log.stack.peek(0).toString(16)
|
||||
};
|
||||
this.callstack.push(call);
|
||||
this.descended = true
|
||||
return;
|
||||
}
|
||||
// If a contract is being self destructed, gather that as a subcall too
|
||||
if (syscall && op == 'SELFDESTRUCT') {
|
||||
var left = this.callstack.length;
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push({
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
to: toHex(toAddress(log.stack.peek(0).toString(16))),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
value: '0x' + db.getBalance(log.contract.getAddress()).toString(16)
|
||||
});
|
||||
return
|
||||
}
|
||||
// If a new method invocation is being done, add to the call stack
|
||||
if (syscall && (op == 'CALL' || op == 'CALLCODE' || op == 'DELEGATECALL' || op == 'STATICCALL')) {
|
||||
// Skip any pre-compile invocations, those are just fancy opcodes
|
||||
var to = toAddress(log.stack.peek(1).toString(16));
|
||||
if (isPrecompiled(to)) {
|
||||
return
|
||||
}
|
||||
var off = (op == 'DELEGATECALL' || op == 'STATICCALL' ? 0 : 1);
|
||||
|
||||
var inOff = log.stack.peek(2 + off).valueOf();
|
||||
var inEnd = inOff + log.stack.peek(3 + off).valueOf();
|
||||
|
||||
// Assemble the internal call report and store for completion
|
||||
var call = {
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
to: toHex(to),
|
||||
input: toHex(log.memory.slice(inOff, inEnd)),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
outOff: log.stack.peek(4 + off).valueOf(),
|
||||
outLen: log.stack.peek(5 + off).valueOf()
|
||||
};
|
||||
if (op != 'DELEGATECALL' && op != 'STATICCALL') {
|
||||
call.value = '0x' + log.stack.peek(2).toString(16);
|
||||
}
|
||||
this.callstack.push(call);
|
||||
this.descended = true
|
||||
return;
|
||||
}
|
||||
// If we've just descended into an inner call, retrieve it's true allowance. We
|
||||
// need to extract if from within the call as there may be funky gas dynamics
|
||||
// with regard to requested and actually given gas (2300 stipend, 63/64 rule).
|
||||
if (this.descended) {
|
||||
if (log.getDepth() >= this.callstack.length) {
|
||||
this.callstack[this.callstack.length - 1].gas = log.getGas();
|
||||
} else {
|
||||
// TODO(karalabe): The call was made to a plain account. We currently don't
|
||||
// have access to the true gas amount inside the call and so any amount will
|
||||
// mostly be wrong since it depends on a lot of input args. Skip gas for now.
|
||||
}
|
||||
this.descended = false;
|
||||
}
|
||||
// If an existing call is returning, pop off the call stack
|
||||
if (syscall && op == 'REVERT') {
|
||||
this.callstack[this.callstack.length - 1].error = "execution reverted";
|
||||
return;
|
||||
}
|
||||
if (log.getDepth() == this.callstack.length - 1) {
|
||||
// Pop off the last call and get the execution results
|
||||
var call = this.callstack.pop();
|
||||
|
||||
if (call.type == 'CREATE' || call.type == "CREATE2") {
|
||||
// If the call was a CREATE, retrieve the contract address and output code
|
||||
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost - log.getGas()).toString(16);
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
|
||||
var ret = log.stack.peek(0);
|
||||
if (!ret.equals(0)) {
|
||||
call.to = toHex(toAddress(ret.toString(16)));
|
||||
call.output = toHex(db.getCode(toAddress(ret.toString(16))));
|
||||
} else if (call.error === undefined) {
|
||||
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
|
||||
}
|
||||
} else {
|
||||
// If the call was a contract call, retrieve the gas usage and output
|
||||
if (call.gas !== undefined) {
|
||||
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost + call.gas - log.getGas()).toString(16);
|
||||
}
|
||||
var ret = log.stack.peek(0);
|
||||
if (!ret.equals(0)) {
|
||||
call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen));
|
||||
} else if (call.error === undefined) {
|
||||
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
|
||||
}
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
delete call.outOff; delete call.outLen;
|
||||
}
|
||||
if (call.gas !== undefined) {
|
||||
call.gas = '0x' + bigInt(call.gas).toString(16);
|
||||
}
|
||||
// Inject the call into the previous one
|
||||
var left = this.callstack.length;
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push(call);
|
||||
}
|
||||
},
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) {
|
||||
// If the topmost call already reverted, don't handle the additional fault again
|
||||
if (this.callstack[this.callstack.length - 1].error !== undefined) {
|
||||
return;
|
||||
}
|
||||
// Pop off the just failed call
|
||||
var call = this.callstack.pop();
|
||||
call.error = log.getError();
|
||||
|
||||
// Consume all available gas and clean any leftovers
|
||||
if (call.gas !== undefined) {
|
||||
call.gas = '0x' + bigInt(call.gas).toString(16);
|
||||
call.gasUsed = call.gas
|
||||
}
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
delete call.outOff; delete call.outLen;
|
||||
|
||||
// Flatten the failed call into its parent
|
||||
var left = this.callstack.length;
|
||||
if (left > 0) {
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push(call);
|
||||
return;
|
||||
}
|
||||
// Last call failed too, leave it in the stack
|
||||
this.callstack.push(call);
|
||||
},
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx, db) {
|
||||
var result = {
|
||||
type: ctx.type,
|
||||
from: toHex(ctx.from),
|
||||
to: toHex(ctx.to),
|
||||
value: '0x' + ctx.value.toString(16),
|
||||
gas: '0x' + bigInt(ctx.gas).toString(16),
|
||||
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
|
||||
input: toHex(ctx.input),
|
||||
output: toHex(ctx.output),
|
||||
time: ctx.time,
|
||||
};
|
||||
if (this.callstack[0].calls !== undefined) {
|
||||
result.calls = this.callstack[0].calls;
|
||||
}
|
||||
if (this.callstack[0].error !== undefined) {
|
||||
result.error = this.callstack[0].error;
|
||||
} else if (ctx.error !== undefined) {
|
||||
result.error = ctx.error;
|
||||
}
|
||||
if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
|
||||
delete result.output;
|
||||
}
|
||||
return this.finalize(result);
|
||||
},
|
||||
callstack: [{}],
|
||||
fault: function(log, db) {
|
||||
var len = this.callstack.length
|
||||
if (len > 1) {
|
||||
var call = this.callstack.pop()
|
||||
if (this.callstack[len-1].calls === undefined) {
|
||||
this.callstack[len-1].calls = []
|
||||
}
|
||||
this.callstack[len-1].calls.push(call)
|
||||
}
|
||||
},
|
||||
result: function(ctx, db) {
|
||||
// Prepare outer message info
|
||||
var result = {
|
||||
type: ctx.type,
|
||||
from: toHex(ctx.from),
|
||||
to: toHex(ctx.to),
|
||||
value: '0x' + ctx.value.toString(16),
|
||||
gas: '0x' + bigInt(ctx.gas).toString(16),
|
||||
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
|
||||
input: toHex(ctx.input),
|
||||
output: toHex(ctx.output),
|
||||
}
|
||||
if (this.callstack[0].calls !== undefined) {
|
||||
result.calls = this.callstack[0].calls
|
||||
}
|
||||
if (this.callstack[0].error !== undefined) {
|
||||
result.error = this.callstack[0].error
|
||||
} else if (ctx.error !== undefined) {
|
||||
result.error = ctx.error
|
||||
}
|
||||
if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
|
||||
delete result.output
|
||||
}
|
||||
|
||||
return this.finalize(result)
|
||||
},
|
||||
enter: function(frame) {
|
||||
var call = {
|
||||
type: frame.getType(),
|
||||
from: toHex(frame.getFrom()),
|
||||
to: toHex(frame.getTo()),
|
||||
input: toHex(frame.getInput()),
|
||||
gas: '0x' + bigInt(frame.getGas()).toString('16'),
|
||||
}
|
||||
if (frame.getValue() !== undefined){
|
||||
call.value='0x' + bigInt(frame.getValue()).toString(16)
|
||||
}
|
||||
this.callstack.push(call)
|
||||
},
|
||||
exit: function(frameResult) {
|
||||
var len = this.callstack.length
|
||||
if (len > 1) {
|
||||
var call = this.callstack.pop()
|
||||
call.gasUsed = '0x' + bigInt(frameResult.getGasUsed()).toString('16')
|
||||
var error = frameResult.getError()
|
||||
if (error === undefined) {
|
||||
call.output = toHex(frameResult.getOutput())
|
||||
} else {
|
||||
call.error = error
|
||||
if (call.type === 'CREATE' || call.type === 'CREATE2') {
|
||||
delete call.to
|
||||
}
|
||||
}
|
||||
len -= 1
|
||||
if (this.callstack[len-1].calls === undefined) {
|
||||
this.callstack[len-1].calls = []
|
||||
}
|
||||
this.callstack[len-1].calls.push(call)
|
||||
}
|
||||
},
|
||||
// finalize recreates a call object using the final desired field oder for json
|
||||
// serialization. This is a nicety feature to pass meaningfully ordered results
|
||||
// to users who don't interpret it, just display it.
|
||||
@@ -239,14 +108,14 @@
|
||||
}
|
||||
for (var key in sorted) {
|
||||
if (sorted[key] === undefined) {
|
||||
delete sorted[key];
|
||||
delete sorted[key]
|
||||
}
|
||||
}
|
||||
if (sorted.calls !== undefined) {
|
||||
for (var i=0; i<sorted.calls.length; i++) {
|
||||
sorted.calls[i] = this.finalize(sorted.calls[i]);
|
||||
sorted.calls[i] = this.finalize(sorted.calls[i])
|
||||
}
|
||||
}
|
||||
return sorted;
|
||||
return sorted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// callTracer is a full blown transaction tracer that extracts and reports all
|
||||
// the internal calls made by a transaction, along with any useful information.
|
||||
{
|
||||
// callstack is the current recursive call stack of the EVM execution.
|
||||
callstack: [{}],
|
||||
|
||||
// descended tracks whether we've just descended from an outer transaction into
|
||||
// an inner call.
|
||||
descended: false,
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// Capture any errors immediately
|
||||
var error = log.getError();
|
||||
if (error !== undefined) {
|
||||
this.fault(log, db);
|
||||
return;
|
||||
}
|
||||
// We only care about system opcodes, faster if we pre-check once
|
||||
var syscall = (log.op.toNumber() & 0xf0) == 0xf0;
|
||||
if (syscall) {
|
||||
var op = log.op.toString();
|
||||
}
|
||||
// If a new contract is being created, add to the call stack
|
||||
if (syscall && (op == 'CREATE' || op == "CREATE2")) {
|
||||
var inOff = log.stack.peek(1).valueOf();
|
||||
var inEnd = inOff + log.stack.peek(2).valueOf();
|
||||
|
||||
// Assemble the internal call report and store for completion
|
||||
var call = {
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
input: toHex(log.memory.slice(inOff, inEnd)),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
value: '0x' + log.stack.peek(0).toString(16)
|
||||
};
|
||||
this.callstack.push(call);
|
||||
this.descended = true
|
||||
return;
|
||||
}
|
||||
// If a contract is being self destructed, gather that as a subcall too
|
||||
if (syscall && op == 'SELFDESTRUCT') {
|
||||
var left = this.callstack.length;
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push({
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
to: toHex(toAddress(log.stack.peek(0).toString(16))),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
value: '0x' + db.getBalance(log.contract.getAddress()).toString(16)
|
||||
});
|
||||
return
|
||||
}
|
||||
// If a new method invocation is being done, add to the call stack
|
||||
if (syscall && (op == 'CALL' || op == 'CALLCODE' || op == 'DELEGATECALL' || op == 'STATICCALL')) {
|
||||
// Skip any pre-compile invocations, those are just fancy opcodes
|
||||
var to = toAddress(log.stack.peek(1).toString(16));
|
||||
if (isPrecompiled(to)) {
|
||||
return
|
||||
}
|
||||
var off = (op == 'DELEGATECALL' || op == 'STATICCALL' ? 0 : 1);
|
||||
|
||||
var inOff = log.stack.peek(2 + off).valueOf();
|
||||
var inEnd = inOff + log.stack.peek(3 + off).valueOf();
|
||||
|
||||
// Assemble the internal call report and store for completion
|
||||
var call = {
|
||||
type: op,
|
||||
from: toHex(log.contract.getAddress()),
|
||||
to: toHex(to),
|
||||
input: toHex(log.memory.slice(inOff, inEnd)),
|
||||
gasIn: log.getGas(),
|
||||
gasCost: log.getCost(),
|
||||
outOff: log.stack.peek(4 + off).valueOf(),
|
||||
outLen: log.stack.peek(5 + off).valueOf()
|
||||
};
|
||||
if (op != 'DELEGATECALL' && op != 'STATICCALL') {
|
||||
call.value = '0x' + log.stack.peek(2).toString(16);
|
||||
}
|
||||
this.callstack.push(call);
|
||||
this.descended = true
|
||||
return;
|
||||
}
|
||||
// If we've just descended into an inner call, retrieve it's true allowance. We
|
||||
// need to extract if from within the call as there may be funky gas dynamics
|
||||
// with regard to requested and actually given gas (2300 stipend, 63/64 rule).
|
||||
if (this.descended) {
|
||||
if (log.getDepth() >= this.callstack.length) {
|
||||
this.callstack[this.callstack.length - 1].gas = log.getGas();
|
||||
} else {
|
||||
// TODO(karalabe): The call was made to a plain account. We currently don't
|
||||
// have access to the true gas amount inside the call and so any amount will
|
||||
// mostly be wrong since it depends on a lot of input args. Skip gas for now.
|
||||
}
|
||||
this.descended = false;
|
||||
}
|
||||
// If an existing call is returning, pop off the call stack
|
||||
if (syscall && op == 'REVERT') {
|
||||
this.callstack[this.callstack.length - 1].error = "execution reverted";
|
||||
return;
|
||||
}
|
||||
if (log.getDepth() == this.callstack.length - 1) {
|
||||
// Pop off the last call and get the execution results
|
||||
var call = this.callstack.pop();
|
||||
|
||||
if (call.type == 'CREATE' || call.type == "CREATE2") {
|
||||
// If the call was a CREATE, retrieve the contract address and output code
|
||||
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost - log.getGas()).toString(16);
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
|
||||
var ret = log.stack.peek(0);
|
||||
if (!ret.equals(0)) {
|
||||
call.to = toHex(toAddress(ret.toString(16)));
|
||||
call.output = toHex(db.getCode(toAddress(ret.toString(16))));
|
||||
} else if (call.error === undefined) {
|
||||
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
|
||||
}
|
||||
} else {
|
||||
// If the call was a contract call, retrieve the gas usage and output
|
||||
if (call.gas !== undefined) {
|
||||
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost + call.gas - log.getGas()).toString(16);
|
||||
}
|
||||
var ret = log.stack.peek(0);
|
||||
if (!ret.equals(0)) {
|
||||
call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen));
|
||||
} else if (call.error === undefined) {
|
||||
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
|
||||
}
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
delete call.outOff; delete call.outLen;
|
||||
}
|
||||
if (call.gas !== undefined) {
|
||||
call.gas = '0x' + bigInt(call.gas).toString(16);
|
||||
}
|
||||
// Inject the call into the previous one
|
||||
var left = this.callstack.length;
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push(call);
|
||||
}
|
||||
},
|
||||
|
||||
// fault is invoked when the actual execution of an opcode fails.
|
||||
fault: function(log, db) {
|
||||
// If the topmost call already reverted, don't handle the additional fault again
|
||||
if (this.callstack[this.callstack.length - 1].error !== undefined) {
|
||||
return;
|
||||
}
|
||||
// Pop off the just failed call
|
||||
var call = this.callstack.pop();
|
||||
call.error = log.getError();
|
||||
|
||||
// Consume all available gas and clean any leftovers
|
||||
if (call.gas !== undefined) {
|
||||
call.gas = '0x' + bigInt(call.gas).toString(16);
|
||||
call.gasUsed = call.gas
|
||||
}
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
delete call.outOff; delete call.outLen;
|
||||
|
||||
// Flatten the failed call into its parent
|
||||
var left = this.callstack.length;
|
||||
if (left > 0) {
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push(call);
|
||||
return;
|
||||
}
|
||||
// Last call failed too, leave it in the stack
|
||||
this.callstack.push(call);
|
||||
},
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx, db) {
|
||||
var result = {
|
||||
type: ctx.type,
|
||||
from: toHex(ctx.from),
|
||||
to: toHex(ctx.to),
|
||||
value: '0x' + ctx.value.toString(16),
|
||||
gas: '0x' + bigInt(ctx.gas).toString(16),
|
||||
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
|
||||
input: toHex(ctx.input),
|
||||
output: toHex(ctx.output),
|
||||
time: ctx.time,
|
||||
};
|
||||
if (this.callstack[0].calls !== undefined) {
|
||||
result.calls = this.callstack[0].calls;
|
||||
}
|
||||
if (this.callstack[0].error !== undefined) {
|
||||
result.error = this.callstack[0].error;
|
||||
} else if (ctx.error !== undefined) {
|
||||
result.error = ctx.error;
|
||||
}
|
||||
if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
|
||||
delete result.output;
|
||||
}
|
||||
return this.finalize(result);
|
||||
},
|
||||
|
||||
// finalize recreates a call object using the final desired field oder for json
|
||||
// serialization. This is a nicety feature to pass meaningfully ordered results
|
||||
// to users who don't interpret it, just display it.
|
||||
finalize: function(call) {
|
||||
var sorted = {
|
||||
type: call.type,
|
||||
from: call.from,
|
||||
to: call.to,
|
||||
value: call.value,
|
||||
gas: call.gas,
|
||||
gasUsed: call.gasUsed,
|
||||
input: call.input,
|
||||
output: call.output,
|
||||
error: call.error,
|
||||
time: call.time,
|
||||
calls: call.calls,
|
||||
}
|
||||
for (var key in sorted) {
|
||||
if (sorted[key] === undefined) {
|
||||
delete sorted[key];
|
||||
}
|
||||
}
|
||||
if (sorted.calls !== undefined) {
|
||||
for (var i=0; i<sorted.calls.length; i++) {
|
||||
sorted.calls[i] = this.finalize(sorted.calls[i]);
|
||||
}
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
}
|
||||
Vendored
Vendored
File diff suppressed because one or more lines are too long
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"genesis": {
|
||||
"difficulty": "117067574",
|
||||
"extraData": "0xd783010502846765746887676f312e372e33856c696e7578",
|
||||
"gasLimit": "4712380",
|
||||
"hash": "0xe05db05eeb3f288041ecb10a787df121c0ed69499355716e17c307de313a4486",
|
||||
"miner": "0x0c062b329265c965deef1eede55183b3acb8f611",
|
||||
"mixHash": "0xb669ae39118a53d2c65fd3b1e1d3850dd3f8c6842030698ed846a2762d68b61d",
|
||||
"nonce": "0x2b469722b8e28c45",
|
||||
"number": "24973",
|
||||
"stateRoot": "0x532a5c3f75453a696428db078e32ae283c85cb97e4d8560dbdf022adac6df369",
|
||||
"timestamp": "1479891145",
|
||||
"totalDifficulty": "1892250259406",
|
||||
"alloc": {
|
||||
"0x6c06b16512b332e6cd8293a2974872674716ce18": {
|
||||
"balance": "0x0",
|
||||
"nonce": "1",
|
||||
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900480632e1a7d4d146036575b6000565b34600057604e60048080359060200190919050506050565b005b3373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f19350505050505b5056",
|
||||
"storage": {}
|
||||
},
|
||||
"0x66fdfd05e46126a07465ad24e40cc0597bc1ef31": {
|
||||
"balance": "0x229ebbb36c3e0f20",
|
||||
"nonce": "3",
|
||||
"code": "0x",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"chainId": 3,
|
||||
"homesteadBlock": 0,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"byzantiumBlock": 1700000,
|
||||
"constantinopleBlock": 4230000,
|
||||
"petersburgBlock": 4939394,
|
||||
"istanbulBlock": 6485846,
|
||||
"muirGlacierBlock": 7117117,
|
||||
"ethash": {}
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"number": "24974",
|
||||
"difficulty": "117067574",
|
||||
"timestamp": "1479891162",
|
||||
"gasLimit": "4712388",
|
||||
"miner": "0xc822ef32e6d26e170b70cf761e204c1806265914"
|
||||
},
|
||||
"input": "0xf889038504a81557008301f97e946c06b16512b332e6cd8293a2974872674716ce1880a42e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b1600002aa0e2a6558040c5d72bc59f2fb62a38993a314c849cd22fb393018d2c5af3112095a01bdb6d7ba32263ccc2ecc880d38c49d9f0c5a72d8b7908e3122b31356d349745",
|
||||
"result": {
|
||||
"type": "CALL",
|
||||
"from": "0x66fdfd05e46126a07465ad24e40cc0597bc1ef31",
|
||||
"to": "0x6c06b16512b332e6cd8293a2974872674716ce18",
|
||||
"value": "0x0",
|
||||
"gas": "0x1a466",
|
||||
"gasUsed": "0x1dc6",
|
||||
"input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000",
|
||||
"output": "0x",
|
||||
"calls": []
|
||||
}
|
||||
}
|
||||
Vendored
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3502894804",
|
||||
"gasLimit": "4722976",
|
||||
"miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
|
||||
"number": "2289806",
|
||||
"timestamp": "1513601314"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
||||
"balance": "0x0",
|
||||
"code": "0x",
|
||||
"nonce": "22",
|
||||
"storage": {}
|
||||
},
|
||||
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||
"balance": "0x4d87094125a369d9bd5",
|
||||
"code": "0x61deadff",
|
||||
"nonce": "1",
|
||||
"storage": {}
|
||||
},
|
||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||
"balance": "0x1780d77678137ac1b775",
|
||||
"code": "0x",
|
||||
"nonce": "29072",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3509749784",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "4727564",
|
||||
"hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
|
||||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"gas": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"input": "0x",
|
||||
"to": "0x000000000000000000000000000000000000dEaD",
|
||||
"type": "SELFDESTRUCT",
|
||||
"value": "0x4d87094125a369d9bd5"
|
||||
}
|
||||
],
|
||||
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||
"gas": "0x10738",
|
||||
"gasUsed": "0x7533",
|
||||
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||
"output": "0x",
|
||||
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3502894804",
|
||||
"gasLimit": "4722976",
|
||||
"miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
|
||||
"number": "2289806",
|
||||
"timestamp": "1513601314"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
||||
"balance": "0x0",
|
||||
"code": "0x",
|
||||
"nonce": "22",
|
||||
"storage": {}
|
||||
},
|
||||
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||
"balance": "0x4d87094125a369d9bd5",
|
||||
"code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
|
||||
"nonce": "1",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
|
||||
}
|
||||
},
|
||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||
"balance": "0x1780d77678137ac1b775",
|
||||
"code": "0x",
|
||||
"nonce": "29072",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3509749784",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "4727564",
|
||||
"hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
|
||||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"gas": "0x6d05",
|
||||
"gasUsed": "0x0",
|
||||
"input": "0x",
|
||||
"to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||
"type": "CALL",
|
||||
"value": "0x6f05b59d3b20000"
|
||||
}
|
||||
],
|
||||
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||
"gas": "0x10738",
|
||||
"gasUsed": "0x3ef9",
|
||||
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3755480783",
|
||||
"gasLimit": "5401723",
|
||||
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||
"number": "2294702",
|
||||
"timestamp": "1513676146"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x13e4acefe6a6700604929946e70e6443e4e73447": {
|
||||
"balance": "0xcf3e0938579f000",
|
||||
"code": "0x",
|
||||
"nonce": "9",
|
||||
"storage": {}
|
||||
},
|
||||
"0x7dc9c9730689ff0b0fd506c67db815f12d90a448": {
|
||||
"balance": "0x0",
|
||||
"code": "0x",
|
||||
"nonce": "0",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3757315409",
|
||||
"extraData": "0x566961425443",
|
||||
"gasLimit": "5406414",
|
||||
"hash": "0xae107f592eebdd9ff8d6ba00363676096e6afb0e1007a7d3d0af88173077378d",
|
||||
"miner": "0xd049bfd667cb46aa3ef5df0da3e57db3be39e511",
|
||||
"mixHash": "0xc927aa05a38bc3de864e95c33b3ae559d3f39c4ccd51cef6f113f9c50ba0caf1",
|
||||
"nonce": "0x93363bbd2c95f410",
|
||||
"number": "2294701",
|
||||
"stateRoot": "0x6b6737d5bde8058990483e915866bd1578014baeff57bd5e4ed228a2bfad635c",
|
||||
"timestamp": "1513676127",
|
||||
"totalDifficulty": "7160808139332585"
|
||||
},
|
||||
"input": "0xf907ef098504e3b29200830897be8080b9079c606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a1129a01060f46676a5dff6f407f0f51eb6f37f5c8c54e238c70221e18e65fc29d3ea65a0557b01c50ff4ffaac8ed6e5d31237a4ecbac843ab1bfe8bb0165a0060df7c54f",
|
||||
"result": {
|
||||
"from": "0x13e4acefe6a6700604929946e70e6443e4e73447",
|
||||
"gas": "0x5e106",
|
||||
"gasUsed": "0x5e106",
|
||||
"input": "0x606060405260405160208061077c83398101604052808051906020019091905050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415151561007d57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600460006101000a81548160ff02191690831515021790555050610653806101296000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029000000000000000000000000c65e620a3a55451316168d57e268f5702ef56a11",
|
||||
"output": "0x606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806305e4382a146100855780631c02708d146100ae5780632e1a7d4d146100c35780635114cb52146100e6578063a37dda2c146100fe578063ae200e7914610153578063b5769f70146101a8575b005b341561009057600080fd5b6100986101d1565b6040518082815260200191505060405180910390f35b34156100b957600080fd5b6100c16101d7565b005b34156100ce57600080fd5b6100e460048080359060200190919050506102eb565b005b6100fc6004808035906020019091905050610513565b005b341561010957600080fd5b6101116105d6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015e57600080fd5b6101666105fc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101b357600080fd5b6101bb610621565b6040518082815260200191505060405180910390f35b60025481565b60011515600460009054906101000a900460ff1615151415156101f957600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806102a15750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b15156102ac57600080fd5b6000600460006101000a81548160ff0219169083151502179055506003543073ffffffffffffffffffffffffffffffffffffffff163103600281905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806103935750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16145b151561039e57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561048357600060025411801561040757506002548111155b151561041257600080fd5b80600254036002819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561047e57600080fd5b610510565b600060035411801561049757506003548111155b15156104a257600080fd5b8060035403600381905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050151561050f57600080fd5b5b50565b60011515600460009054906101000a900460ff16151514151561053557600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614801561059657506003548160035401115b80156105bd575080600354013073ffffffffffffffffffffffffffffffffffffffff163110155b15156105c857600080fd5b806003540160038190555050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600354815600a165627a7a72305820c3b849e8440987ce43eae3097b77672a69234d516351368b03fe5b7de03807910029",
|
||||
"to": "0x7dc9c9730689ff0b0fd506c67db815f12d90a448",
|
||||
"type": "CREATE",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3665057456",
|
||||
"gasLimit": "5232723",
|
||||
"miner": "0xf4d8e706cfb25c0decbbdd4d2e2cc10c66376a3f",
|
||||
"number": "2294501",
|
||||
"timestamp": "1513673601"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9": {
|
||||
"balance": "0x2a3fc32bcc019283",
|
||||
"code": "0x",
|
||||
"nonce": "10",
|
||||
"storage": {}
|
||||
},
|
||||
"0xabbcd5b340c80b5f1c0545c04c987b87310296ae": {
|
||||
"balance": "0x0",
|
||||
"code": "0x606060405236156100755763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632d0335ab811461007a578063548db174146100ab5780637f649783146100fc578063b092145e1461014d578063c3f44c0a14610186578063c47cf5de14610203575b600080fd5b341561008557600080fd5b610099600160a060020a0360043516610270565b60405190815260200160405180910390f35b34156100b657600080fd5b6100fa600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061028f95505050505050565b005b341561010757600080fd5b6100fa600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061029e95505050505050565b005b341561015857600080fd5b610172600160a060020a03600435811690602435166102ad565b604051901515815260200160405180910390f35b341561019157600080fd5b6100fa6004803560ff1690602480359160443591606435600160a060020a0316919060a49060843590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965050509235600160a060020a031692506102cd915050565b005b341561020e57600080fd5b61025460046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061056a95505050505050565b604051600160a060020a03909116815260200160405180910390f35b600160a060020a0381166000908152602081905260409020545b919050565b61029a816000610594565b5b50565b61029a816001610594565b5b50565b600160209081526000928352604080842090915290825290205460ff1681565b60008080600160a060020a038416158061030d5750600160a060020a038085166000908152600160209081526040808320339094168352929052205460ff165b151561031857600080fd5b6103218561056a565b600160a060020a038116600090815260208190526040808220549295507f19000000000000000000000000000000000000000000000000000000000000009230918891908b908b90517fff000000000000000000000000000000000000000000000000000000000000008089168252871660018201526c01000000000000000000000000600160a060020a038088168202600284015286811682026016840152602a8301869052841602604a820152605e810182805190602001908083835b6020831061040057805182525b601f1990920191602091820191016103e0565b6001836020036101000a0380198251168184511617909252505050919091019850604097505050505050505051809103902091506001828a8a8a6040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f1151561049957600080fd5b5050602060405103519050600160a060020a03838116908216146104bc57600080fd5b600160a060020a0380841660009081526020819052604090819020805460010190559087169086905180828051906020019080838360005b8381101561050d5780820151818401525b6020016104f4565b50505050905090810190601f16801561053a5780820380516001836020036101000a031916815260200191505b5091505060006040518083038160008661646e5a03f1915050151561055e57600080fd5b5b505050505050505050565b600060248251101561057e5750600061028a565b600160a060020a0360248301511690505b919050565b60005b825181101561060157600160a060020a033316600090815260016020526040812083918584815181106105c657fe5b90602001906020020151600160a060020a031681526020810191909152604001600020805460ff19169115159190911790555b600101610597565b5b5050505600a165627a7a723058200027e8b695e9d2dea9f3629519022a69f3a1d23055ce86406e686ea54f31ee9c0029",
|
||||
"nonce": "1",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3672229776",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "5227619",
|
||||
"hash": "0xa07b3d6c6bf63f5f981016db9f2d1d93033833f2c17e8bf7209e85f1faf08076",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0x806e151ce2817be922e93e8d5921fa0f0d0fd213d6b2b9a3fa17458e74a163d0",
|
||||
"nonce": "0xbc5d43adc2c30c7d",
|
||||
"number": "2294500",
|
||||
"stateRoot": "0xca645b335888352ef9d8b1ef083e9019648180b259026572e3139717270de97d",
|
||||
"timestamp": "1513673552",
|
||||
"totalDifficulty": "7160066586979149"
|
||||
},
|
||||
"input": "0xf9018b0a8505d21dba00832dc6c094abbcd5b340c80b5f1c0545c04c987b87310296ae80b9012473b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988000000000000000000000000000000000000000000000000000000000000000000000000000000001ba0fd659d76a4edbd2a823e324c93f78ad6803b30ff4a9c8bce71ba82798975c70ca06571eecc0b765688ec6c78942c5ee8b585e00988c0141b518287e9be919bc48a",
|
||||
"result": {
|
||||
"error": "execution reverted",
|
||||
"from": "0x0f6cef2b7fbb504782e35aa82a2207e816a2b7a9",
|
||||
"gas": "0x2d55e8",
|
||||
"gasUsed": "0xc3",
|
||||
"input": "0x73b40a5c000000000000000000000000400de2e016bda6577407dfc379faba9899bc73ef0000000000000000000000002cc31912b2b0f3075a87b3640923d45a26cef3ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000064d79d8e6c7265636f76657279416464726573730000000000000000000000000000000000000000000000000000000000383e3ec32dc0f66d8fe60dbdc2f6815bdf73a988383e3ec32dc0f66d8fe60dbdc2f6815bdf73a98800000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"to": "0xabbcd5b340c80b5f1c0545c04c987b87310296ae",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"context": {
|
||||
"difficulty": "3502894804",
|
||||
"gasLimit": "4722976",
|
||||
"miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
|
||||
"number": "2289806",
|
||||
"timestamp": "1513601314"
|
||||
},
|
||||
"genesis": {
|
||||
"alloc": {
|
||||
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
||||
"balance": "0x0",
|
||||
"code": "0x",
|
||||
"nonce": "22",
|
||||
"storage": {}
|
||||
},
|
||||
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||
"balance": "0x4d87094125a369d9bd5",
|
||||
"code": "0x61deadff",
|
||||
"nonce": "1",
|
||||
"storage": {}
|
||||
},
|
||||
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||
"balance": "0x1780d77678137ac1b775",
|
||||
"code": "0x",
|
||||
"nonce": "29072",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"byzantiumBlock": 1700000,
|
||||
"chainId": 3,
|
||||
"daoForkSupport": true,
|
||||
"eip150Block": 0,
|
||||
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||
"eip155Block": 10,
|
||||
"eip158Block": 10,
|
||||
"ethash": {},
|
||||
"homesteadBlock": 0
|
||||
},
|
||||
"difficulty": "3509749784",
|
||||
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||
"gasLimit": "4727564",
|
||||
"hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
|
||||
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||
"mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
|
||||
"nonce": "0x4eb12e19c16d43da",
|
||||
"number": "2289805",
|
||||
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||
"timestamp": "1513601261",
|
||||
"totalDifficulty": "7143276353481064"
|
||||
},
|
||||
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||
"result": {
|
||||
"calls": [
|
||||
{
|
||||
"from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"input": "0x",
|
||||
"to": "0x000000000000000000000000000000000000dEaD",
|
||||
"type": "SELFDESTRUCT",
|
||||
"value": "0x4d87094125a369d9bd5"
|
||||
}
|
||||
],
|
||||
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||
"gas": "0x10738",
|
||||
"gasUsed": "0x7533",
|
||||
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||
"output": "0x",
|
||||
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||
"type": "CALL",
|
||||
"value": "0x0"
|
||||
}
|
||||
}
|
||||
Vendored
File diff suppressed because one or more lines are too long
+204
-32
@@ -284,6 +284,85 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
|
||||
vm.PutPropString(obj, "getInput")
|
||||
}
|
||||
|
||||
type frame struct {
|
||||
typ *string
|
||||
from *common.Address
|
||||
to *common.Address
|
||||
input []byte
|
||||
gas *uint
|
||||
value *big.Int
|
||||
}
|
||||
|
||||
func newFrame() *frame {
|
||||
return &frame{
|
||||
typ: new(string),
|
||||
from: new(common.Address),
|
||||
to: new(common.Address),
|
||||
gas: new(uint),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frame) pushObject(vm *duktape.Context) {
|
||||
obj := vm.PushObject()
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 })
|
||||
vm.PutPropString(obj, "getType")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 })
|
||||
vm.PutPropString(obj, "getFrom")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 })
|
||||
vm.PutPropString(obj, "getTo")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 })
|
||||
vm.PutPropString(obj, "getInput")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 })
|
||||
vm.PutPropString(obj, "getGas")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
if f.value != nil {
|
||||
pushValue(ctx, f.value)
|
||||
} else {
|
||||
ctx.PushUndefined()
|
||||
}
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getValue")
|
||||
}
|
||||
|
||||
type frameResult struct {
|
||||
gasUsed *uint
|
||||
output []byte
|
||||
errorValue *string
|
||||
}
|
||||
|
||||
func newFrameResult() *frameResult {
|
||||
return &frameResult{
|
||||
gasUsed: new(uint),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *frameResult) pushObject(vm *duktape.Context) {
|
||||
obj := vm.PushObject()
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 })
|
||||
vm.PutPropString(obj, "getGasUsed")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 })
|
||||
vm.PutPropString(obj, "getOutput")
|
||||
|
||||
vm.PushGoFunction(func(ctx *duktape.Context) int {
|
||||
if r.errorValue != nil {
|
||||
pushValue(ctx, *r.errorValue)
|
||||
} else {
|
||||
ctx.PushUndefined()
|
||||
}
|
||||
return 1
|
||||
})
|
||||
vm.PutPropString(obj, "getError")
|
||||
}
|
||||
|
||||
// Tracer provides an implementation of Tracer that evaluates a Javascript
|
||||
// function for each VM execution step.
|
||||
type Tracer struct {
|
||||
@@ -305,6 +384,9 @@ type Tracer struct {
|
||||
errorValue *string // Swappable error value wrapped by a log accessor
|
||||
refundValue *uint // Swappable refund value wrapped by a log accessor
|
||||
|
||||
frame *frame // Represents entry into call frame. Fields are swappable
|
||||
frameResult *frameResult // Represents exit from a call frame. Fields are swappable
|
||||
|
||||
ctx map[string]interface{} // Transaction context gathered throughout execution
|
||||
err error // Error, if one has occurred
|
||||
|
||||
@@ -312,6 +394,8 @@ type Tracer struct {
|
||||
reason error // Textual reason for the interruption
|
||||
|
||||
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
|
||||
traceSteps bool // When true, will invoke step() on each opcode
|
||||
traceCallFrames bool // When true, will invoke enter() and exit() js funcs
|
||||
}
|
||||
|
||||
// Context contains some contextual infos for a transaction execution that is not
|
||||
@@ -343,6 +427,8 @@ func New(code string, ctx *Context) (*Tracer, error) {
|
||||
costValue: new(uint),
|
||||
depthValue: new(uint),
|
||||
refundValue: new(uint),
|
||||
frame: newFrame(),
|
||||
frameResult: newFrameResult(),
|
||||
}
|
||||
if ctx.BlockHash != (common.Hash{}) {
|
||||
tracer.ctx["blockHash"] = ctx.BlockHash
|
||||
@@ -450,9 +536,7 @@ func New(code string, ctx *Context) (*Tracer, error) {
|
||||
}
|
||||
tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
|
||||
|
||||
if !tracer.vm.GetPropString(tracer.tracerObject, "step") {
|
||||
return nil, fmt.Errorf("trace object must expose a function step()")
|
||||
}
|
||||
hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step")
|
||||
tracer.vm.Pop()
|
||||
|
||||
if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
|
||||
@@ -465,6 +549,23 @@ func New(code string, ctx *Context) (*Tracer, error) {
|
||||
}
|
||||
tracer.vm.Pop()
|
||||
|
||||
hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter")
|
||||
tracer.vm.Pop()
|
||||
hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit")
|
||||
tracer.vm.Pop()
|
||||
|
||||
if hasEnter != hasExit {
|
||||
return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()")
|
||||
}
|
||||
if !hasStep {
|
||||
// If there's no step function, the enter and exit must be present
|
||||
if !hasEnter {
|
||||
return nil, fmt.Errorf("trace object must expose either step() or both enter() and exit()")
|
||||
}
|
||||
}
|
||||
tracer.traceCallFrames = hasEnter
|
||||
tracer.traceSteps = hasStep
|
||||
|
||||
// Tracer is valid, inject the big int library to access large numbers
|
||||
tracer.vm.EvalString(bigIntegerJS)
|
||||
tracer.vm.PutGlobalString("bigInt")
|
||||
@@ -513,6 +614,12 @@ func New(code string, ctx *Context) (*Tracer, error) {
|
||||
|
||||
tracer.vm.PutPropString(tracer.stateObject, "log")
|
||||
|
||||
tracer.frame.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(tracer.stateObject, "frame")
|
||||
|
||||
tracer.frameResult.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(tracer.stateObject, "frameResult")
|
||||
|
||||
tracer.dbWrapper.pushObject(tracer.vm)
|
||||
tracer.vm.PutPropString(tracer.stateObject, "db")
|
||||
|
||||
@@ -594,12 +701,16 @@ func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
||||
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
if !jst.traceSteps {
|
||||
return
|
||||
}
|
||||
if jst.err != nil {
|
||||
return
|
||||
}
|
||||
// If tracing was interrupted, set the error and stop
|
||||
if atomic.LoadUint32(&jst.interrupt) > 0 {
|
||||
jst.err = jst.reason
|
||||
env.Cancel()
|
||||
return
|
||||
}
|
||||
jst.opWrapper.op = op
|
||||
@@ -649,41 +760,70 @@ func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, er
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (jst *Tracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
if !jst.traceCallFrames {
|
||||
return
|
||||
}
|
||||
if jst.err != nil {
|
||||
return
|
||||
}
|
||||
// If tracing was interrupted, set the error and stop
|
||||
if atomic.LoadUint32(&jst.interrupt) > 0 {
|
||||
jst.err = jst.reason
|
||||
return
|
||||
}
|
||||
|
||||
*jst.frame.typ = typ.String()
|
||||
*jst.frame.from = from
|
||||
*jst.frame.to = to
|
||||
jst.frame.input = common.CopyBytes(input)
|
||||
*jst.frame.gas = uint(gas)
|
||||
jst.frame.value = nil
|
||||
if value != nil {
|
||||
jst.frame.value = new(big.Int).SetBytes(value.Bytes())
|
||||
}
|
||||
|
||||
if _, err := jst.call(true, "enter", "frame"); err != nil {
|
||||
jst.err = wrapError("enter", err)
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (jst *Tracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
if !jst.traceCallFrames {
|
||||
return
|
||||
}
|
||||
if jst.err != nil {
|
||||
return
|
||||
}
|
||||
// If tracing was interrupted, set the error and stop
|
||||
if atomic.LoadUint32(&jst.interrupt) > 0 {
|
||||
jst.err = jst.reason
|
||||
return
|
||||
}
|
||||
|
||||
jst.frameResult.output = common.CopyBytes(output)
|
||||
*jst.frameResult.gasUsed = uint(gasUsed)
|
||||
jst.frameResult.errorValue = nil
|
||||
if err != nil {
|
||||
jst.frameResult.errorValue = new(string)
|
||||
*jst.frameResult.errorValue = err.Error()
|
||||
}
|
||||
|
||||
if _, err := jst.call(true, "exit", "frameResult"); err != nil {
|
||||
jst.err = wrapError("exit", err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
||||
func (jst *Tracer) GetResult() (json.RawMessage, error) {
|
||||
// Transform the context into a JavaScript object and inject into the state
|
||||
obj := jst.vm.PushObject()
|
||||
|
||||
for key, val := range jst.ctx {
|
||||
switch val := val.(type) {
|
||||
case uint64:
|
||||
jst.vm.PushUint(uint(val))
|
||||
|
||||
case string:
|
||||
jst.vm.PushString(val)
|
||||
|
||||
case []byte:
|
||||
ptr := jst.vm.PushFixedBuffer(len(val))
|
||||
copy(makeSlice(ptr, uint(len(val))), val)
|
||||
|
||||
case common.Address:
|
||||
ptr := jst.vm.PushFixedBuffer(20)
|
||||
copy(makeSlice(ptr, 20), val[:])
|
||||
|
||||
case *big.Int:
|
||||
pushBigInt(val, jst.vm)
|
||||
|
||||
case int:
|
||||
jst.vm.PushInt(val)
|
||||
|
||||
case common.Hash:
|
||||
ptr := jst.vm.PushFixedBuffer(32)
|
||||
copy(makeSlice(ptr, 32), val[:])
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported type: %T", val))
|
||||
}
|
||||
jst.vm.PutPropString(obj, key)
|
||||
jst.addToObj(obj, key, val)
|
||||
}
|
||||
jst.vm.PutPropString(jst.stateObject, "ctx")
|
||||
|
||||
@@ -698,3 +838,35 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) {
|
||||
|
||||
return result, jst.err
|
||||
}
|
||||
|
||||
// addToObj pushes a field to a JS object.
|
||||
func (jst *Tracer) addToObj(obj int, key string, val interface{}) {
|
||||
pushValue(jst.vm, val)
|
||||
jst.vm.PutPropString(obj, key)
|
||||
}
|
||||
|
||||
func pushValue(ctx *duktape.Context, val interface{}) {
|
||||
switch val := val.(type) {
|
||||
case uint64:
|
||||
ctx.PushUint(uint(val))
|
||||
case string:
|
||||
ctx.PushString(val)
|
||||
case []byte:
|
||||
ptr := ctx.PushFixedBuffer(len(val))
|
||||
copy(makeSlice(ptr, uint(len(val))), val)
|
||||
case common.Address:
|
||||
ptr := ctx.PushFixedBuffer(20)
|
||||
copy(makeSlice(ptr, 20), val[:])
|
||||
case *big.Int:
|
||||
pushBigInt(val, ctx)
|
||||
case int:
|
||||
ctx.PushInt(val)
|
||||
case uint:
|
||||
ctx.PushUint(val)
|
||||
case common.Hash:
|
||||
ptr := ctx.PushFixedBuffer(32)
|
||||
copy(makeSlice(ptr, 32), val[:])
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported type: %T", val))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,3 +236,35 @@ func TestIsPrecompile(t *testing.T) {
|
||||
t.Errorf("Tracer should consider blake2f as precompile in istanbul")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnterExit(t *testing.T) {
|
||||
// test that either both or none of enter() and exit() are defined
|
||||
if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(Context)); err == nil {
|
||||
t.Fatal("tracer creation should've failed without exit() definition")
|
||||
}
|
||||
if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(Context)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// test that the enter and exit method are correctly invoked and the values passed
|
||||
tracer, err := New("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(Context))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
scope := &vm.ScopeContext{
|
||||
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
||||
}
|
||||
|
||||
tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
|
||||
tracer.CaptureExit([]byte{}, 400, nil)
|
||||
|
||||
have, err := tracer.GetResult()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := `{"enters":1,"exits":1,"enterGas":1000,"gasUsed":400}`
|
||||
if string(have) != want {
|
||||
t.Errorf("Number of invocations of enter() and exit() is wrong. Have %s, want %s\n", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,21 +203,25 @@ func TestPrestateTracerCreate2(t *testing.T) {
|
||||
|
||||
// Iterates over all the input-output datasets in the tracer test harness and
|
||||
// runs the JavaScript tracers against them.
|
||||
func TestCallTracer(t *testing.T) {
|
||||
files, err := ioutil.ReadDir("testdata")
|
||||
func TestCallTracerLegacy(t *testing.T) {
|
||||
testCallTracer("callTracerLegacy", "call_tracer_legacy", t)
|
||||
}
|
||||
|
||||
func testCallTracer(tracer string, dirPath string, t *testing.T) {
|
||||
files, err := ioutil.ReadDir(filepath.Join("testdata", dirPath))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve tracer test suite: %v", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if !strings.HasPrefix(file.Name(), "call_tracer_") {
|
||||
if !strings.HasSuffix(file.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
file := file // capture range variable
|
||||
t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) {
|
||||
t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Call tracer test found, read if from disk
|
||||
blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name()))
|
||||
blob, err := ioutil.ReadFile(filepath.Join("testdata", dirPath, file.Name()))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read testcase: %v", err)
|
||||
}
|
||||
@@ -248,7 +252,7 @@ func TestCallTracer(t *testing.T) {
|
||||
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
|
||||
|
||||
// Create the tracer, the EVM environment and run it
|
||||
tracer, err := New("callTracer", new(Context))
|
||||
tracer, err := New(tracer, new(Context))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
@@ -283,6 +287,10 @@ func TestCallTracer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallTracer(t *testing.T) {
|
||||
testCallTracer("callTracer", "call_tracer", t)
|
||||
}
|
||||
|
||||
// jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to
|
||||
// comparison
|
||||
func jsonEqual(x, y interface{}) bool {
|
||||
@@ -353,8 +361,8 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||
tracer := vm.NewStructLogger(&vm.LogConfig{
|
||||
Debug: false,
|
||||
//DisableStorage: true,
|
||||
//DisableMemory: true,
|
||||
//DisableReturnData: true,
|
||||
//EnableMemory: false,
|
||||
//EnableReturnData: false,
|
||||
})
|
||||
evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
|
||||
msg, err := tx.AsMessage(signer, nil)
|
||||
@@ -378,3 +386,73 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||
tracer.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTracers(b *testing.B) {
|
||||
files, err := ioutil.ReadDir(filepath.Join("testdata", "call_tracer"))
|
||||
if err != nil {
|
||||
b.Fatalf("failed to retrieve tracer test suite: %v", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if !strings.HasSuffix(file.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
file := file // capture range variable
|
||||
b.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(b *testing.B) {
|
||||
blob, err := ioutil.ReadFile(filepath.Join("testdata", "call_tracer", file.Name()))
|
||||
if err != nil {
|
||||
b.Fatalf("failed to read testcase: %v", err)
|
||||
}
|
||||
test := new(callTracerTest)
|
||||
if err := json.Unmarshal(blob, test); err != nil {
|
||||
b.Fatalf("failed to parse testcase: %v", err)
|
||||
}
|
||||
benchTracer("callTracer", test, b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||
// Configure a blockchain with the given prestate
|
||||
tx := new(types.Transaction)
|
||||
if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
|
||||
b.Fatalf("failed to parse testcase input: %v", err)
|
||||
}
|
||||
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
|
||||
msg, err := tx.AsMessage(signer, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
origin, _ := signer.Sender(tx)
|
||||
txContext := vm.TxContext{
|
||||
Origin: origin,
|
||||
GasPrice: tx.GasPrice(),
|
||||
}
|
||||
context := vm.BlockContext{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
Coinbase: test.Context.Miner,
|
||||
BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
|
||||
Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
|
||||
Difficulty: (*big.Int)(test.Context.Difficulty),
|
||||
GasLimit: uint64(test.Context.GasLimit),
|
||||
}
|
||||
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
|
||||
|
||||
// Create the tracer, the EVM environment and run it
|
||||
tracer, err := New(tracerName, new(Context))
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
snap := statedb.Snapshot()
|
||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if _, err = st.TransitionDb(); err != nil {
|
||||
b.Fatalf("failed to execute transaction: %v", err)
|
||||
}
|
||||
statedb.RevertToSnapshot(snap)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user