diff --git a/core/vm/analysis.go b/core/vm/analysis.go index 4aa8cfe70f11..a22d81a50474 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -61,20 +61,21 @@ func (bits *bitvec) codeSegment(pos uint64) bool { } // codeBitmap collects data locations in code. -func codeBitmap(code []byte) bitvec { +func codeBitmap(c *Contract) bitvec { // The bitmap is 4 bytes longer than necessary, in case the code // ends with a PUSH32, the algorithm will push zeroes onto the // bitvector outside the bounds of the actual code. - bits := make(bitvec, len(code)/8+1+4) - return codeBitmapInternal(code, bits) + bits := make(bitvec, c.CodeSize()/8+1+4) + return codeBitmapInternal(c, bits) } // codeBitmapInternal is the internal implementation of codeBitmap. // It exists for the purpose of being able to run benchmark tests // without dynamic allocations affecting the results. -func codeBitmapInternal(code, bits bitvec) bitvec { - for pc := uint64(0); pc < uint64(len(code)); { - op := OpCode(code[pc]) +func codeBitmapInternal(c *Contract, bits bitvec) bitvec { + codeBegin := c.CodeBeginOffset() + for pc := uint64(0); pc < c.CodeSize(); { + op := OpCode(c.Code[codeBegin+pc]) pc++ if int8(op) < int8(PUSH1) { // If not PUSH (the int8(op) > int(PUSH32) is always false). continue diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go index 398861f8ae7d..547f5ca296e4 100644 --- a/core/vm/analysis_test.go +++ b/core/vm/analysis_test.go @@ -20,6 +20,7 @@ import ( "math/bits" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) @@ -52,7 +53,15 @@ func TestJumpDestAnalysis(t *testing.T) { {[]byte{byte(PUSH32)}, 0b0000_0001, 4}, } for i, test := range tests { - ret := codeBitmap(test.code) + var ( + contract Contract + addr common.Address + hash common.Hash + header EOF1Header + ) + contract.SetCallCode(&addr, hash, test.code, &header) + + ret := codeBitmap(&contract) if ret[test.which] != test.exp { t.Fatalf("test %d: expected %x, got %02x", i, test.exp, ret[test.which]) } @@ -64,10 +73,17 @@ const analysisCodeSize = 1200 * 1024 func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { // 1.4 ms code := make([]byte, analysisCodeSize) - bench.SetBytes(analysisCodeSize) + var ( + contract Contract + addr common.Address + hash common.Hash + header EOF1Header + ) + contract.SetCallCode(&addr, hash, code, &header) bench.ResetTimer() + bench.SetBytes(analysisCodeSize) for i := 0; i < bench.N; i++ { - codeBitmap(code) + codeBitmap(&contract) } bench.StopTimer() } @@ -83,7 +99,13 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) { } func BenchmarkJumpdestOpAnalysis(bench *testing.B) { - var op OpCode + var ( + contract Contract + addr common.Address + hash common.Hash + header EOF1Header + op OpCode + ) bencher := func(b *testing.B) { code := make([]byte, analysisCodeSize) b.SetBytes(analysisCodeSize) @@ -91,12 +113,13 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) { code[i] = byte(op) } bits := make(bitvec, len(code)/8+1+4) + contract.SetCallCode(&addr, hash, code, &header) b.ResetTimer() for i := 0; i < b.N; i++ { for j := range bits { bits[j] = 0 } - codeBitmapInternal(code, bits) + codeBitmapInternal(&contract, bits) } } for op = PUSH1; op <= PUSH32; op++ { diff --git a/core/vm/contract.go b/core/vm/contract.go index 5fd9885db88f..3aa5565d46d9 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -88,11 +88,11 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool { udest, overflow := dest.Uint64WithOverflow() // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. // Don't bother checking for JUMPDEST in that case. - if overflow || udest >= uint64(len(c.Code)) { + if overflow || udest >= c.CodeSize() { return false } // Only JUMPDESTs allowed for destinations - if OpCode(c.Code[udest]) != JUMPDEST { + if OpCode(c.Code[c.CodeBeginOffset()+udest]) != JUMPDEST { return false } return c.isCode(udest) @@ -114,7 +114,7 @@ func (c *Contract) isCode(udest uint64) bool { if !exist { // Do the analysis and save in parent context // We do not need to store it in c.analysis - analysis = codeBitmap(c.Code) + analysis = codeBitmap(c) c.jumpdests[c.CodeHash] = analysis } // Also stash it in current contract for faster access @@ -126,7 +126,7 @@ func (c *Contract) isCode(udest uint64) bool { // we don't have to recalculate it for every JUMP instruction in the execution // However, we don't save it within the parent context if c.analysis == nil { - c.analysis = codeBitmap(c.Code) + c.analysis = codeBitmap(c) } return c.analysis.codeSegment(udest) } @@ -201,6 +201,13 @@ func (c *Contract) CodeEndOffset() uint64 { return c.header.CodeEndOffset() } +func (c *Contract) CodeSize() uint64 { + if c.IsLegacy() { + return uint64(len(c.Code)) + } + return uint64(c.header.codeSize) +} + // SetCallCode sets the code of the contract and address of the backing data // object func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte, header *EOF1Header) {