mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 07:37:20 +00:00
parent
919b238c82
commit
995fa79bf5
1 changed files with 347 additions and 0 deletions
|
|
@ -1373,3 +1373,350 @@ func TestStandardTraceBlockToFile(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceBadBlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
accounts = newAccounts(2)
|
||||
storageContract = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
|
||||
signer = types.HomesteadSigner{}
|
||||
txHashs = make([]common.Hash, 0, 2)
|
||||
genesis = &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
storageContract: {
|
||||
Nonce: 1,
|
||||
Balance: big.NewInt(0),
|
||||
Code: []byte{
|
||||
byte(vm.PUSH1), 0x2a, // push 42
|
||||
byte(vm.PUSH1), 0x00, // push slot 0
|
||||
byte(vm.SSTORE), // sstore(0, 42)
|
||||
byte(vm.STOP),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
|
||||
// tx 0: plain transfer
|
||||
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &accounts[1].addr,
|
||||
Value: big.NewInt(1000),
|
||||
Gas: params.TxGas,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil}),
|
||||
signer, accounts[0].key)
|
||||
b.AddTx(tx)
|
||||
txHashs = append(txHashs, tx.Hash())
|
||||
|
||||
// tx 1: call storage contract (executes PUSH1, PUSH1, SSTORE, STOP)
|
||||
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 1,
|
||||
To: &storageContract,
|
||||
Value: big.NewInt(0),
|
||||
Gas: 50000,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil}),
|
||||
signer, accounts[0].key)
|
||||
b.AddTx(tx)
|
||||
txHashs = append(txHashs, tx.Hash())
|
||||
})
|
||||
defer backend.teardown()
|
||||
|
||||
// Write the block as a bad block so parent state is available
|
||||
block := backend.chain.GetBlockByNumber(1)
|
||||
rawdb.WriteBadBlock(backend.chaindb, block)
|
||||
|
||||
api := NewAPI(backend)
|
||||
result, err := api.TraceBadBlock(context.Background(), block.Hash(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("want no error, have %v", err)
|
||||
}
|
||||
if len(result) != 2 {
|
||||
t.Fatalf("expected 2 tx traces, got %d", len(result))
|
||||
}
|
||||
|
||||
// First tx: plain transfer
|
||||
have, _ := json.Marshal(result)
|
||||
var traces []struct {
|
||||
TxHash common.Hash `json:"txHash"`
|
||||
Result struct {
|
||||
Gas uint64 `json:"gas"`
|
||||
Failed bool `json:"failed"`
|
||||
StructLogs []json.RawMessage `json:"structLogs"`
|
||||
} `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal(have, &traces); err != nil {
|
||||
t.Fatalf("failed to unmarshal traces: %v", err)
|
||||
}
|
||||
if traces[0].TxHash != txHashs[0] {
|
||||
t.Errorf("tx 0: hash mismatch, have %v, want %v", traces[0].TxHash, txHashs[0])
|
||||
}
|
||||
if traces[0].Result.Gas != params.TxGas {
|
||||
t.Errorf("tx 0: gas mismatch, have %d, want %d", traces[0].Result.Gas, params.TxGas)
|
||||
}
|
||||
if len(traces[0].Result.StructLogs) != 0 {
|
||||
t.Errorf("tx 0: expected empty structLogs for plain transfer, got %d entries", len(traces[0].Result.StructLogs))
|
||||
}
|
||||
|
||||
// Second tx: contract call
|
||||
if traces[1].TxHash != txHashs[1] {
|
||||
t.Errorf("tx 1: hash mismatch, have %v, want %v", traces[1].TxHash, txHashs[1])
|
||||
}
|
||||
if traces[1].Result.Failed {
|
||||
t.Error("tx 1: expected success, got failed")
|
||||
}
|
||||
// Contract has 4 opcodes: PUSH1, PUSH1, SSTORE, STOP
|
||||
if len(traces[1].Result.StructLogs) != 4 {
|
||||
t.Errorf("tx 1: expected 4 structLog entries for contract call, got %d", len(traces[1].Result.StructLogs))
|
||||
}
|
||||
|
||||
// Non-existent bad block
|
||||
_, err = api.TraceBadBlock(context.Background(), common.Hash{42}, nil)
|
||||
if err == nil {
|
||||
t.Fatal("want error for non-existent bad block, have none")
|
||||
}
|
||||
wantErr := fmt.Sprintf("bad block %#x not found", common.Hash{42})
|
||||
if err.Error() != wantErr {
|
||||
t.Errorf("error mismatch, want '%s', have '%v'", wantErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntermediateRoots(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Initialize test accounts and a contract that writes to storage.
|
||||
var (
|
||||
accounts = newAccounts(2)
|
||||
storageContract = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
|
||||
signer = types.HomesteadSigner{}
|
||||
genesis = &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
// Contract: SSTORE(CALLVALUE, CALLVALUE)
|
||||
storageContract: {
|
||||
Nonce: 1,
|
||||
Balance: big.NewInt(0),
|
||||
Code: []byte{
|
||||
byte(vm.CALLVALUE),
|
||||
byte(vm.CALLVALUE),
|
||||
byte(vm.SSTORE),
|
||||
byte(vm.STOP),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
|
||||
// tx 0: plain transfer
|
||||
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &accounts[1].addr,
|
||||
Value: big.NewInt(1000),
|
||||
Gas: params.TxGas,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil}),
|
||||
signer, accounts[0].key)
|
||||
b.AddTx(tx)
|
||||
|
||||
// tx 1: sstore(1, 1)
|
||||
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 1,
|
||||
To: &storageContract,
|
||||
Value: big.NewInt(1),
|
||||
Gas: 50000,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil}),
|
||||
signer, accounts[0].key)
|
||||
b.AddTx(tx)
|
||||
|
||||
// tx 2: sstore(2, 2)
|
||||
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 2,
|
||||
To: &storageContract,
|
||||
Value: big.NewInt(2),
|
||||
Gas: 50000,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil}),
|
||||
signer, accounts[0].key)
|
||||
b.AddTx(tx)
|
||||
})
|
||||
defer backend.teardown()
|
||||
|
||||
api := NewAPI(backend)
|
||||
block := backend.chain.GetBlockByNumber(1)
|
||||
|
||||
// Should return one root per tx
|
||||
roots, err := api.IntermediateRoots(context.Background(), block.Hash(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("want no error, have %v", err)
|
||||
}
|
||||
if len(roots) != 3 {
|
||||
t.Fatalf("root count mismatch, have %d, want 3", len(roots))
|
||||
}
|
||||
for i, root := range roots {
|
||||
if root == (common.Hash{}) {
|
||||
t.Errorf("root[%d] should not be zero", i)
|
||||
}
|
||||
}
|
||||
if roots[0] == roots[1] {
|
||||
t.Error("root[0] and root[1] should differ (transfer vs sstore)")
|
||||
}
|
||||
if roots[1] == roots[2] {
|
||||
t.Error("root[1] and root[2] should differ (sstore to different slots)")
|
||||
}
|
||||
|
||||
// Intermediate roots of a bad block
|
||||
rawdb.WriteBadBlock(backend.chaindb, block)
|
||||
badRoots, err := api.IntermediateRoots(context.Background(), block.Hash(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("want no error for bad block fallback, have %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(roots, badRoots) {
|
||||
t.Errorf("bad block roots mismatch, have %v, want %v", badRoots, roots)
|
||||
}
|
||||
|
||||
// Genesis block: should return error
|
||||
genesisBlock := backend.chain.GetBlockByNumber(0)
|
||||
_, err = api.IntermediateRoots(context.Background(), genesisBlock.Hash(), nil)
|
||||
if err == nil || err.Error() != "genesis is not traceable" {
|
||||
t.Fatalf("want 'genesis is not traceable' error, have %v", err)
|
||||
}
|
||||
|
||||
// Non-existent block: should return error
|
||||
_, err = api.IntermediateRoots(context.Background(), common.Hash{42}, nil)
|
||||
if err == nil {
|
||||
t.Fatal("want error for non-existent block, have none")
|
||||
}
|
||||
wantErr := fmt.Sprintf("block %#x not found", common.Hash{42})
|
||||
if err.Error() != wantErr {
|
||||
t.Errorf("error mismatch, want '%s', have '%v'", wantErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStandardTraceBadBlockToFile(t *testing.T) {
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000000000)
|
||||
|
||||
aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
||||
aaCode = []byte{byte(vm.PUSH1), 0x00, byte(vm.POP)}
|
||||
|
||||
bb = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f44")
|
||||
bbCode = []byte{byte(vm.PUSH2), 0x00, 0x01, byte(vm.POP)}
|
||||
)
|
||||
|
||||
genesis := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
aa: {
|
||||
Code: aaCode,
|
||||
Nonce: 1,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
bb: {
|
||||
Code: bbCode,
|
||||
Nonce: 1,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
txHashs := make([]common.Hash, 0, 2)
|
||||
backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &aa,
|
||||
Value: big.NewInt(0),
|
||||
Gas: 50000,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil,
|
||||
}), types.HomesteadSigner{}, key)
|
||||
b.AddTx(tx)
|
||||
txHashs = append(txHashs, tx.Hash())
|
||||
|
||||
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: 1,
|
||||
To: &bb,
|
||||
Value: big.NewInt(1),
|
||||
Gas: 100000,
|
||||
GasPrice: b.BaseFee(),
|
||||
Data: nil,
|
||||
}), types.HomesteadSigner{}, key)
|
||||
b.AddTx(tx)
|
||||
txHashs = append(txHashs, tx.Hash())
|
||||
})
|
||||
defer backend.teardown()
|
||||
|
||||
// Write the block as a bad block
|
||||
block := backend.chain.GetBlockByNumber(1)
|
||||
rawdb.WriteBadBlock(backend.chaindb, block)
|
||||
|
||||
var testSuite = []struct {
|
||||
config *StdTraceConfig
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
// All txs traced
|
||||
config: nil,
|
||||
want: []string{
|
||||
`{"pc":0,"op":96,"gas":"0x7148","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||
{"pc":2,"op":80,"gas":"0x7145","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"POP"}
|
||||
{"pc":3,"op":0,"gas":"0x7143","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
|
||||
{"output":"","gasUsed":"0x5"}
|
||||
`,
|
||||
`{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"}
|
||||
{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"}
|
||||
{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
|
||||
{"output":"","gasUsed":"0x5"}
|
||||
`,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Specific tx traced
|
||||
config: &StdTraceConfig{TxHash: txHashs[1]},
|
||||
want: []string{
|
||||
`{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"}
|
||||
{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"}
|
||||
{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
|
||||
{"output":"","gasUsed":"0x5"}
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
api := NewAPI(backend)
|
||||
for i, tc := range testSuite {
|
||||
txTraces, err := api.StandardTraceBadBlockToFile(context.Background(), block.Hash(), tc.config)
|
||||
if err != nil {
|
||||
t.Fatalf("test %d: unexpected error %v", i, err)
|
||||
}
|
||||
if len(txTraces) != len(tc.want) {
|
||||
t.Fatalf("test %d: file count mismatch, have %d, want %d", i, len(txTraces), len(tc.want))
|
||||
}
|
||||
for j, traceFileName := range txTraces {
|
||||
defer os.Remove(traceFileName)
|
||||
traceReceived, err := os.ReadFile(traceFileName)
|
||||
if err != nil {
|
||||
t.Fatalf("test %d: could not read trace file: %v", i, err)
|
||||
}
|
||||
if tc.want[j] != string(traceReceived) {
|
||||
t.Fatalf("test %d, trace %d: result mismatch\nhave:\n%s\nwant:\n%s", i, j, string(traceReceived), tc.want[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Non-existent bad block
|
||||
_, err := api.StandardTraceBadBlockToFile(context.Background(), common.Hash{42}, nil)
|
||||
if err == nil {
|
||||
t.Fatal("want error for non-existent bad block, have none")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue