|
|
@@ -23,8 +23,11 @@ import (
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
+ "github.com/ethereum/go-ethereum/consensus"
|
|
|
+ "github.com/ethereum/go-ethereum/core"
|
|
|
"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/core/vm"
|
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
)
|
|
|
@@ -203,3 +206,113 @@ func BenchmarkEVM_CREATE2_1200(bench *testing.B) {
|
|
|
// initcode size 1200K, repeatedly calls CREATE2 and then modifies the mem contents
|
|
|
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
|
|
|
}
|
|
|
+
|
|
|
+func fakeHeader(n uint64, parentHash common.Hash) *types.Header {
|
|
|
+ header := types.Header{
|
|
|
+ Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
|
|
|
+ Number: big.NewInt(int64(n)),
|
|
|
+ ParentHash: parentHash,
|
|
|
+ Time: 1000,
|
|
|
+ Nonce: types.BlockNonce{0x1},
|
|
|
+ Extra: []byte{},
|
|
|
+ Difficulty: big.NewInt(0),
|
|
|
+ GasLimit: 100000,
|
|
|
+ }
|
|
|
+ return &header
|
|
|
+}
|
|
|
+
|
|
|
+type dummyChain struct {
|
|
|
+ counter int
|
|
|
+}
|
|
|
+
|
|
|
+// Engine retrieves the chain's consensus engine.
|
|
|
+func (d *dummyChain) Engine() consensus.Engine {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// GetHeader returns the hash corresponding to their hash.
|
|
|
+func (d *dummyChain) GetHeader(h common.Hash, n uint64) *types.Header {
|
|
|
+ d.counter++
|
|
|
+ parentHash := common.Hash{}
|
|
|
+ s := common.LeftPadBytes(big.NewInt(int64(n-1)).Bytes(), 32)
|
|
|
+ copy(parentHash[:], s)
|
|
|
+
|
|
|
+ //parentHash := common.Hash{byte(n - 1)}
|
|
|
+ //fmt.Printf("GetHeader(%x, %d) => header with parent %x\n", h, n, parentHash)
|
|
|
+ return fakeHeader(n, parentHash)
|
|
|
+}
|
|
|
+
|
|
|
+// TestBlockhash tests the blockhash operation. It's a bit special, since it internally
|
|
|
+// requires access to a chain reader.
|
|
|
+func TestBlockhash(t *testing.T) {
|
|
|
+ // Current head
|
|
|
+ n := uint64(1000)
|
|
|
+ parentHash := common.Hash{}
|
|
|
+ s := common.LeftPadBytes(big.NewInt(int64(n-1)).Bytes(), 32)
|
|
|
+ copy(parentHash[:], s)
|
|
|
+ header := fakeHeader(n, parentHash)
|
|
|
+
|
|
|
+ // This is the contract we're using. It requests the blockhash for current num (should be all zeroes),
|
|
|
+ // then iteratively fetches all blockhashes back to n-260.
|
|
|
+ // It returns
|
|
|
+ // 1. the first (should be zero)
|
|
|
+ // 2. the second (should be the parent hash)
|
|
|
+ // 3. the last non-zero hash
|
|
|
+ // By making the chain reader return hashes which correlate to the number, we can
|
|
|
+ // verify that it obtained the right hashes where it should
|
|
|
+
|
|
|
+ /*
|
|
|
+
|
|
|
+ pragma solidity ^0.5.3;
|
|
|
+ contract Hasher{
|
|
|
+
|
|
|
+ function test() public view returns (bytes32, bytes32, bytes32){
|
|
|
+ uint256 x = block.number;
|
|
|
+ bytes32 first;
|
|
|
+ bytes32 last;
|
|
|
+ bytes32 zero;
|
|
|
+ zero = blockhash(x); // Should be zeroes
|
|
|
+ first = blockhash(x-1);
|
|
|
+ for(uint256 i = 2 ; i < 260; i++){
|
|
|
+ bytes32 hash = blockhash(x - i);
|
|
|
+ if (uint256(hash) != 0){
|
|
|
+ last = hash;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return (zero, first, last);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ */
|
|
|
+ // The contract above
|
|
|
+ data := common.Hex2Bytes("6080604052348015600f57600080fd5b50600436106045576000357c010000000000000000000000000000000000000000000000000000000090048063f8a8fd6d14604a575b600080fd5b60506074565b60405180848152602001838152602001828152602001935050505060405180910390f35b600080600080439050600080600083409050600184034092506000600290505b61010481101560c35760008186034090506000816001900414151560b6578093505b5080806001019150506094565b508083839650965096505050505090919256fea165627a7a72305820462d71b510c1725ff35946c20b415b0d50b468ea157c8c77dff9466c9cb85f560029")
|
|
|
+ // The method call to 'test()'
|
|
|
+ input := common.Hex2Bytes("f8a8fd6d")
|
|
|
+ chain := &dummyChain{}
|
|
|
+ ret, _, err := Execute(data, input, &Config{
|
|
|
+ GetHashFn: core.GetHashFn(header, chain),
|
|
|
+ BlockNumber: new(big.Int).Set(header.Number),
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("expected no error, got %v", err)
|
|
|
+ }
|
|
|
+ if len(ret) != 96 {
|
|
|
+ t.Fatalf("expected returndata to be 96 bytes, got %d", len(ret))
|
|
|
+ }
|
|
|
+
|
|
|
+ zero := new(big.Int).SetBytes(ret[0:32])
|
|
|
+ first := new(big.Int).SetBytes(ret[32:64])
|
|
|
+ last := new(big.Int).SetBytes(ret[64:96])
|
|
|
+ if zero.BitLen() != 0 {
|
|
|
+ t.Fatalf("expected zeroes, got %x", ret[0:32])
|
|
|
+ }
|
|
|
+ if first.Uint64() != 999 {
|
|
|
+ t.Fatalf("second block should be 999, got %d (%x)", first, ret[32:64])
|
|
|
+ }
|
|
|
+ if last.Uint64() != 744 {
|
|
|
+ t.Fatalf("last block should be 744, got %d (%x)", last, ret[64:96])
|
|
|
+ }
|
|
|
+ if exp, got := 255, chain.counter; exp != got {
|
|
|
+ t.Errorf("suboptimal; too much chain iteration, expected %d, got %d", exp, got)
|
|
|
+ }
|
|
|
+}
|