From 11ba4f7d97f592482f081805ec714929a1514c1e Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Nov 2025 11:45:01 +0100 Subject: [PATCH 1/7] Copy lfvm --- go/interpreter/sfvm/converter.go | 359 +++ go/interpreter/sfvm/converter_fuzz_test.go | 112 + go/interpreter/sfvm/converter_test.go | 554 ++++ go/interpreter/sfvm/ct.go | 214 ++ go/interpreter/sfvm/ct_test.go | 578 ++++ go/interpreter/sfvm/errors.go | 28 + go/interpreter/sfvm/example_code_test.go | 16 + go/interpreter/sfvm/gas.go | 298 ++ go/interpreter/sfvm/gas_test.go | 189 ++ go/interpreter/sfvm/hash_cache.go | 163 ++ go/interpreter/sfvm/hash_cache_test.go | 334 +++ go/interpreter/sfvm/instruction.go | 42 + go/interpreter/sfvm/instruction_logger.go | 50 + .../sfvm/instruction_logger_test.go | 146 + .../sfvm/instruction_statistcs_test.go | 177 ++ go/interpreter/sfvm/instruction_statistics.go | 184 ++ go/interpreter/sfvm/instruction_test.go | 47 + go/interpreter/sfvm/instructions.go | 1171 ++++++++ go/interpreter/sfvm/instructions_test.go | 2402 +++++++++++++++++ go/interpreter/sfvm/interpreter.go | 593 ++++ go/interpreter/sfvm/interpreter_mock.go | 64 + go/interpreter/sfvm/interpreter_test.go | 839 ++++++ go/interpreter/sfvm/keccak.go | 79 + go/interpreter/sfvm/keccak.h | 451 ++++ go/interpreter/sfvm/keccak_test.go | 127 + go/interpreter/sfvm/lfvm.go | 149 + go/interpreter/sfvm/lfvm_interface_test.go | 72 + go/interpreter/sfvm/lfvm_test.go | 73 + go/interpreter/sfvm/memory.go | 144 + go/interpreter/sfvm/memory_test.go | 477 ++++ go/interpreter/sfvm/opcode.go | 429 +++ go/interpreter/sfvm/opcode_test.go | 80 + go/interpreter/sfvm/stack.go | 146 + go/interpreter/sfvm/stack_test.go | 297 ++ go/interpreter/sfvm/stack_usage.go | 116 + go/interpreter/sfvm/stack_usage_test.go | 108 + go/interpreter/sfvm/super_instructions.go | 168 ++ .../sfvm/super_instructions_test.go | 59 + 38 files changed, 11535 insertions(+) create mode 100644 go/interpreter/sfvm/converter.go create mode 100644 go/interpreter/sfvm/converter_fuzz_test.go create mode 100644 go/interpreter/sfvm/converter_test.go create mode 100644 go/interpreter/sfvm/ct.go create mode 100644 go/interpreter/sfvm/ct_test.go create mode 100644 go/interpreter/sfvm/errors.go create mode 100644 go/interpreter/sfvm/example_code_test.go create mode 100644 go/interpreter/sfvm/gas.go create mode 100644 go/interpreter/sfvm/gas_test.go create mode 100644 go/interpreter/sfvm/hash_cache.go create mode 100644 go/interpreter/sfvm/hash_cache_test.go create mode 100644 go/interpreter/sfvm/instruction.go create mode 100644 go/interpreter/sfvm/instruction_logger.go create mode 100644 go/interpreter/sfvm/instruction_logger_test.go create mode 100644 go/interpreter/sfvm/instruction_statistcs_test.go create mode 100644 go/interpreter/sfvm/instruction_statistics.go create mode 100644 go/interpreter/sfvm/instruction_test.go create mode 100644 go/interpreter/sfvm/instructions.go create mode 100644 go/interpreter/sfvm/instructions_test.go create mode 100644 go/interpreter/sfvm/interpreter.go create mode 100644 go/interpreter/sfvm/interpreter_mock.go create mode 100644 go/interpreter/sfvm/interpreter_test.go create mode 100644 go/interpreter/sfvm/keccak.go create mode 100644 go/interpreter/sfvm/keccak.h create mode 100644 go/interpreter/sfvm/keccak_test.go create mode 100644 go/interpreter/sfvm/lfvm.go create mode 100644 go/interpreter/sfvm/lfvm_interface_test.go create mode 100644 go/interpreter/sfvm/lfvm_test.go create mode 100644 go/interpreter/sfvm/memory.go create mode 100644 go/interpreter/sfvm/memory_test.go create mode 100644 go/interpreter/sfvm/opcode.go create mode 100644 go/interpreter/sfvm/opcode_test.go create mode 100644 go/interpreter/sfvm/stack.go create mode 100644 go/interpreter/sfvm/stack_test.go create mode 100644 go/interpreter/sfvm/stack_usage.go create mode 100644 go/interpreter/sfvm/stack_usage_test.go create mode 100644 go/interpreter/sfvm/super_instructions.go create mode 100644 go/interpreter/sfvm/super_instructions_test.go diff --git a/go/interpreter/sfvm/converter.go b/go/interpreter/sfvm/converter.go new file mode 100644 index 00000000..d5d1bc17 --- /dev/null +++ b/go/interpreter/sfvm/converter.go @@ -0,0 +1,359 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "math" + "unsafe" + + "github.com/0xsoniclabs/tosca/go/ct/common" + "github.com/0xsoniclabs/tosca/go/tosca" + + "github.com/0xsoniclabs/tosca/go/tosca/vm" + lru "github.com/hashicorp/golang-lru/v2" +) + +// ConversionConfig contains a set of configuration options for the code conversion. +type ConversionConfig struct { + // CacheSize is the maximum size of the maintained code cache in bytes. + // If set to 0, a default size is used. If negative, no cache is used. + // Cache sizes are grown in increments of maxCachedCodeLength. + // Positive values larger than 0 but less than maxCachedCodeLength are + // reported as invalid cache sizes during initialization. + CacheSize int + // WithSuperInstructions enables the use of super instructions. + WithSuperInstructions bool +} + +// Converter converts EVM code to LFVM code. +type Converter struct { + config ConversionConfig + cache *lru.Cache[tosca.Hash, Code] +} + +// NewConverter creates a new code converter with the provided configuration. +func NewConverter(config ConversionConfig) (*Converter, error) { + if config.CacheSize == 0 { + config.CacheSize = (1 << 30) // = 1GiB + } + + var cache *lru.Cache[tosca.Hash, Code] + if config.CacheSize > 0 { + var err error + const instructionSize = int(unsafe.Sizeof(Instruction{})) + capacity := config.CacheSize / maxCachedCodeLength / instructionSize + cache, err = lru.New[tosca.Hash, Code](capacity) + if err != nil { + return nil, err + } + } + return &Converter{ + config: config, + cache: cache, + }, nil +} + +// Convert converts EVM code to LFVM code. If the provided code hash is not nil, +// it is assumed to be a valid hash of the code and is used to cache the +// conversion result. If the hash is nil, the conversion result is not cached. +func (c *Converter) Convert(code []byte, codeHash *tosca.Hash) (Code, error) { + if len(code) > math.MaxUint16 { + return Code{}, errCodeSizeExceeded + } + + if c.cache == nil || codeHash == nil { + return convert(code, c.config), nil + } + + res, exists := c.cache.Get(*codeHash) + if exists { + return res, nil + } + + res = convert(code, c.config) + if len(res) > maxCachedCodeLength { + return res, nil + } + + c.cache.Add(*codeHash, res) + return res, nil +} + +// maxCachedCodeLength is the maximum length of a code in bytes that are +// retained in the cache. To avoid excessive memory usage, longer codes are not +// cached. The defined limit is the current limit for codes stored on the chain. +// Only initialization codes can be longer. Since the Shanghai hard fork, the +// maximum size of initialization codes is 2 * 24_576 = 49_152 bytes (see +// https://eips.ethereum.org/EIPS/eip-3860). Such init codes are deliberately +// not cached due to the expected limited re-use and the missing code hash. +const maxCachedCodeLength = 1<<14 + 1<<13 // = 24_576 bytes + +// --- code builder --- + +type codeBuilder struct { + code []Instruction + nextPos int +} + +func newCodeBuilder(codelength int) codeBuilder { + return codeBuilder{make([]Instruction, codelength), 0} +} + +func (b *codeBuilder) length() int { + return b.nextPos +} + +func (b *codeBuilder) appendOp(opcode OpCode, arg uint16) *codeBuilder { + b.code[b.nextPos].opcode = opcode + b.code[b.nextPos].arg = arg + b.nextPos++ + return b +} + +func (b *codeBuilder) appendCode(opcode OpCode) *codeBuilder { + b.code[b.nextPos].opcode = opcode + b.nextPos++ + return b +} + +func (b *codeBuilder) appendData(data uint16) *codeBuilder { + return b.appendOp(DATA, data) +} + +func (b *codeBuilder) padNoOpsUntil(pos int) { + for i := b.nextPos; i < pos; i++ { + b.code[i].opcode = NOOP + } + b.nextPos = pos +} + +func (b *codeBuilder) toCode() Code { + return b.code[0:b.nextPos] +} + +func convert(code []byte, options ConversionConfig) Code { + return convertWithObserver(code, options, func(int, int) {}) +} + +// convertWithObserver converts EVM code to LFVM code and calls the observer +// with the code position of every pair of instructions converted. +func convertWithObserver( + code []byte, + options ConversionConfig, + observer func(evmPc int, lfvmPc int), +) Code { + res := newCodeBuilder(len(code)) + + // Convert each individual instruction. + for i := 0; i < len(code); { + // Handle jump destinations + if code[i] == byte(vm.JUMPDEST) { + // Jump to the next jump destination and fill space with noops + if res.length() < i { + res.appendOp(JUMP_TO, uint16(i)) + } + res.padNoOpsUntil(i) + res.appendCode(JUMPDEST) + observer(i, i) + i++ + continue + } + + // Convert instructions + observer(i, res.nextPos) + inc := appendInstructions(&res, i, code, options.WithSuperInstructions) + i += inc + 1 + } + return res.toCode() +} + +func appendInstructions(res *codeBuilder, pos int, code []byte, withSuperInstructions bool) int { + // Convert super instructions. + if withSuperInstructions { + if n := appendSuperInstructions(res, pos, code); n > 0 { + return n + } + } + + // Convert individual instructions. + toscaOpCode := vm.OpCode(code[pos]) + + if toscaOpCode == vm.PC { + if pos > math.MaxUint16 { + res.appendCode(INVALID) + return 1 + } + res.appendOp(PC, uint16(pos)) + return 0 + } + + if vm.PUSH1 <= toscaOpCode && toscaOpCode <= vm.PUSH32 { + // Determine the number of bytes to be pushed. + numBytes := int(toscaOpCode) - int(vm.PUSH1) + 1 + + var data []byte + // If there are not enough bytes left in the code, rest is filled with 0 + // zeros are padded right + if len(code) < pos+numBytes+2 { + extension := (pos + numBytes + 2 - len(code)) / 2 + if (pos+numBytes+2-len(code))%2 > 0 { + extension++ + } + if extension > 0 { + instruction := common.RightPadSlice(res.code[:], len(res.code)+extension) + res.code = instruction + } + data = common.RightPadSlice(code[pos+1:], numBytes+1) + } else { + data = code[pos+1 : pos+1+numBytes] + } + + // Fix the op-codes of the resulting instructions + if numBytes == 1 { + res.appendOp(PUSH1, uint16(data[0])<<8) + } else { + res.appendOp(PUSH1+OpCode(numBytes-1), uint16(data[0])<<8|uint16(data[1])) + } + + // Fix the arguments by packing them in pairs into the instructions. + for i := 2; i < numBytes-1; i += 2 { + res.appendData(uint16(data[i])<<8 | uint16(data[i+1])) + } + if numBytes > 1 && numBytes%2 == 1 { + res.appendData(uint16(data[numBytes-1]) << 8) + } + + return numBytes + } + + // All the rest converts to a single instruction. + res.appendCode(OpCode(toscaOpCode)) + return 0 +} + +func appendSuperInstructions(res *codeBuilder, pos int, code []byte) int { + if len(code) > pos+7 { + op0 := vm.OpCode(code[pos]) + op1 := vm.OpCode(code[pos+1]) + op2 := vm.OpCode(code[pos+2]) + op3 := vm.OpCode(code[pos+3]) + op4 := vm.OpCode(code[pos+4]) + op5 := vm.OpCode(code[pos+5]) + op6 := vm.OpCode(code[pos+6]) + op7 := vm.OpCode(code[pos+7]) + if op0 == vm.PUSH1 && op2 == vm.PUSH4 && op7 == vm.DUP3 { + res.appendOp(PUSH1_PUSH4_DUP3, uint16(op1)<<8) + res.appendData(uint16(op3)<<8 | uint16(op4)) + res.appendData(uint16(op5)<<8 | uint16(op6)) + return 7 + } + if op0 == vm.PUSH1 && op2 == vm.PUSH1 && op4 == vm.PUSH1 && op6 == vm.SHL && op7 == vm.SUB { + res.appendOp(PUSH1_PUSH1_PUSH1_SHL_SUB, uint16(op1)<<8|uint16(op3)) + res.appendData(uint16(op5)) + return 7 + } + } + if len(code) > pos+4 { + op0 := vm.OpCode(code[pos]) + op1 := vm.OpCode(code[pos+1]) + op2 := vm.OpCode(code[pos+2]) + op3 := vm.OpCode(code[pos+3]) + op4 := vm.OpCode(code[pos+4]) + if op0 == vm.AND && op1 == vm.SWAP1 && op2 == vm.POP && op3 == vm.SWAP2 && op4 == vm.SWAP1 { + res.appendCode(AND_SWAP1_POP_SWAP2_SWAP1) + return 4 + } + if op0 == vm.ISZERO && op1 == vm.PUSH2 && op4 == vm.JUMPI { + res.appendOp(ISZERO_PUSH2_JUMPI, uint16(op2)<<8|uint16(op3)) + return 4 + } + } + if len(code) > pos+3 { + op0 := vm.OpCode(code[pos]) + op1 := vm.OpCode(code[pos+1]) + op2 := vm.OpCode(code[pos+2]) + op3 := vm.OpCode(code[pos+3]) + if op0 == vm.SWAP2 && op1 == vm.SWAP1 && op2 == vm.POP && op3 == vm.JUMP { + res.appendCode(SWAP2_SWAP1_POP_JUMP) + return 3 + } + if op0 == vm.SWAP1 && op1 == vm.POP && op2 == vm.SWAP2 && op3 == vm.SWAP1 { + res.appendCode(SWAP1_POP_SWAP2_SWAP1) + return 3 + } + if op0 == vm.POP && op1 == vm.SWAP2 && op2 == vm.SWAP1 && op3 == vm.POP { + res.appendCode(POP_SWAP2_SWAP1_POP) + return 3 + } + if op0 == vm.PUSH2 && op3 == vm.JUMP { + res.appendOp(PUSH2_JUMP, uint16(op1)<<8|uint16(op2)) + return 3 + } + if op0 == vm.PUSH2 && op3 == vm.JUMPI { + res.appendOp(PUSH2_JUMPI, uint16(op1)<<8|uint16(op2)) + return 3 + } + if op0 == vm.PUSH1 && op2 == vm.PUSH1 { + res.appendOp(PUSH1_PUSH1, uint16(op1)<<8|uint16(op3)) + return 3 + } + } + if len(code) > pos+2 { + op0 := vm.OpCode(code[pos]) + op1 := vm.OpCode(code[pos+1]) + op2 := vm.OpCode(code[pos+2]) + if op0 == vm.PUSH1 && op2 == vm.ADD { + res.appendOp(PUSH1_ADD, uint16(op1)) + return 2 + } + if op0 == vm.PUSH1 && op2 == vm.SHL { + res.appendOp(PUSH1_SHL, uint16(op1)) + return 2 + } + if op0 == vm.PUSH1 && op2 == vm.DUP1 { + res.appendOp(PUSH1_DUP1, uint16(op1)) + return 2 + } + } + if len(code) > pos+1 { + op0 := vm.OpCode(code[pos]) + op1 := vm.OpCode(code[pos+1]) + if op0 == vm.SWAP1 && op1 == vm.POP { + res.appendCode(SWAP1_POP) + return 1 + } + if op0 == vm.POP && op1 == vm.JUMP { + res.appendCode(POP_JUMP) + return 1 + } + if op0 == vm.POP && op1 == vm.POP { + res.appendCode(POP_POP) + return 1 + } + if op0 == vm.SWAP2 && op1 == vm.SWAP1 { + res.appendCode(SWAP2_SWAP1) + return 1 + } + if op0 == vm.SWAP2 && op1 == vm.POP { + res.appendCode(SWAP2_POP) + return 1 + } + if op0 == vm.DUP2 && op1 == vm.MSTORE { + res.appendCode(DUP2_MSTORE) + return 1 + } + if op0 == vm.DUP2 && op1 == vm.LT { + res.appendCode(DUP2_LT) + return 1 + } + } + return 0 +} diff --git a/go/interpreter/sfvm/converter_fuzz_test.go b/go/interpreter/sfvm/converter_fuzz_test.go new file mode 100644 index 00000000..92f06e07 --- /dev/null +++ b/go/interpreter/sfvm/converter_fuzz_test.go @@ -0,0 +1,112 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "math" + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca/vm" +) + +// To run this fuzzer use the following command: +// go test ./interpreter/lfvm -run none -fuzz LfvmConverter --fuzztime 10m + +func FuzzLfvmConverter(f *testing.F) { + + // Add empty code + f.Add([]byte{}) + + f.Fuzz(func(t *testing.T, toscaCode []byte) { + + // EIP-170 stablish maximum code size to 0x6000 bytes, ~22 KB + // (see https://eips.ethereum.org/EIPS/eip-170) + // EIP-3860 stablish maximum init code size to 49_152 bytes + // (see https://eips.ethereum.org/EIPS/eip-3860) + // Before EIP-3860, any size was allowed, but in the Fantom + // network anything larger than 2^16 was observed, which is + // also the limit for the conversion code (due to a 16-bit PC + // counter representation in the PC instruction). Thus, we test + // the code up to this level. + maxCodeSize := math.MaxUint16 + if len(toscaCode) > maxCodeSize { + t.Skip() + } + + mapping := make([]int, len(toscaCode)) + lfvmCode := convertWithObserver(toscaCode, ConversionConfig{}, func(evm, lfvm int) { + mapping[evm] = lfvm + }) + + // Check that no super-instructions have been used. + for _, op := range lfvmCode { + if op.opcode.isSuperInstruction() { + t.Errorf("Super-instruction %v used", op.opcode) + } + } + + // Check that all operations are mapped to matching operations. + for i := 0; i < len(toscaCode); i++ { + originalPos := i + lfvmPos := mapping[originalPos] + + toscaOpCode := vm.OpCode(toscaCode[originalPos]) + lfvmOpCode := lfvmCode[lfvmPos].opcode + + if !lfvmOpCode.isBaseInstruction() { + t.Errorf("Expected base instructions only, got %v", lfvmOpCode) + } + + if vm.OpCode(lfvmOpCode) != toscaOpCode { + t.Errorf("Invalid conversion from %v to %v", toscaOpCode, lfvmOpCode) + } + + // Check that the position of JUMPDEST ops are preserved. + if toscaOpCode == vm.JUMPDEST { + if originalPos != lfvmPos { + t.Errorf("Expected JUMPDEST at %d, got %d", originalPos, lfvmPos) + } + } + + // Check that PC instructions point to the correct target. + if toscaOpCode == vm.PC { + target := int(lfvmCode[lfvmPos].arg) + if target != originalPos { + t.Errorf("Invalid PC target, wanted %d, got %d", originalPos, target) + } + } + + // Skip the data section of PUSH instructions. + if vm.PUSH1 <= toscaOpCode && toscaOpCode <= vm.PUSH32 { + i += int(toscaOpCode-vm.PUSH1) + 1 + } + } + + // Check that JUMP_TO instructions point to their immediately succeeding JUMPDEST. + for i := 0; i < len(lfvmCode); i++ { + if lfvmCode[i].opcode == JUMP_TO { + trg := int(lfvmCode[i].arg) + if trg < i { + t.Errorf("invalid JUMP_TO target from %d to %d", i, trg) + } + if trg >= len(lfvmCode) || lfvmCode[trg].opcode != JUMPDEST { + t.Fatalf("JUMP_TO target %d is not a JUMPDEST", trg) + } + for j := i + 1; j < trg; j++ { + cur := lfvmCode[j].opcode + if cur != NOOP { + t.Errorf("found %v between JUMP_TO and JUMPDEST at %d", cur, j) + } + } + } + } + }) +} diff --git a/go/interpreter/sfvm/converter_test.go b/go/interpreter/sfvm/converter_test.go new file mode 100644 index 00000000..de04207c --- /dev/null +++ b/go/interpreter/sfvm/converter_test.go @@ -0,0 +1,554 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "errors" + "math" + "math/rand" + "slices" + "testing" + "time" + "unsafe" + + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" + "golang.org/x/sync/errgroup" +) + +func TestNewConverter_UsesDefaultCapacity(t *testing.T) { + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + if want, got := (1 << 30), converter.config.CacheSize; got != want { + t.Errorf("Expected default cache capacity of %d, got %d", want, got) + } +} + +func TestNewConverter_CacheCanBeDisabled(t *testing.T) { + converter, err := NewConverter(ConversionConfig{ + CacheSize: -1, + }) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + if want, got := -1, converter.config.CacheSize; got != want { + t.Errorf("Expected default cache capacity of %d, got %d", want, got) + } + if converter.cache != nil { + t.Errorf("Expected cache to be disabled") + } + // Conversion should still work without a nil pointer dereference. + _, err = converter.Convert([]byte{0}, &tosca.Hash{0}) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + } +} + +func TestNewConverter_TooSmallCapacityLeadsToCreationIssues(t *testing.T) { + _, err := NewConverter(ConversionConfig{ + CacheSize: maxCachedCodeLength / 2, + }) + if err == nil { + t.Fatalf("expected error when creating converter with too small cache size") + } +} + +func TestConverter_LongExampleCode(t *testing.T) { + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + _, err = converter.Convert(longExampleCode, nil) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + } +} + +func TestConverter_CodeLargerThanMaxUint16ReturnsAnError(t *testing.T) { + tests := map[string]struct { + codeSize int + err error + }{ + "small code": { + codeSize: math.MaxUint16 - 1, + err: nil, + }, + "exact code": { + codeSize: math.MaxUint16, + err: nil, + }, + "large code": { + codeSize: math.MaxUint16 + 1, + err: errCodeSizeExceeded, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + config := config{ + ConversionConfig: ConversionConfig{}, + } + vm, err := newVm(config) + if err != nil { + t.Fatalf("unexpected error") + } + _, err = vm.Run(tosca.Parameters{Code: make([]byte, test.codeSize)}) + if !errors.Is(err, test.err) { + t.Fatalf("unexpected error: want %v, got %v", test.err, err) + } + }) + } +} + +func TestConverter_InputsAreCachedUsingHashAsKey(t *testing.T) { + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + code := []byte{byte(vm.STOP)} + hash := tosca.Hash{byte(1)} + want, err := converter.Convert(code, &hash) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + + } + got, err := converter.Convert(code, &hash) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + } + if &want[0] != &got[0] { // < it needs to be the same slice + t.Errorf("cached conversion result not returned") + } +} + +func TestConverter_CacheSizeLimitIsEnforced(t *testing.T) { + for _, limit := range []int{10, 100, 1000} { + const instructionSize = int(unsafe.Sizeof(Instruction{})) + converter, err := NewConverter(ConversionConfig{ + CacheSize: limit * maxCachedCodeLength * instructionSize, + }) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + for i := 0; i < limit*10; i++ { + hash := tosca.Hash{byte(i), byte(i >> 8), byte(i >> 16)} + _, err = converter.Convert([]byte{0}, &hash) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + } + } + if got := len(converter.cache.Keys()); got > limit { + t.Errorf("Conversion cache grew to %d entries", got) + } + } +} + +func TestConverter_ExceedinglyLongCodesAreNotCached(t *testing.T) { + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + if want, got := 0, len(converter.cache.Keys()); want != got { + t.Errorf("Expected %d entries in the cache, got %d", want, got) + } + _, err = converter.Convert([]byte{0}, &tosca.Hash{0}) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + } + if want, got := 1, len(converter.cache.Keys()); want != got { + t.Errorf("Expected %d entries in the cache, got %d", want, got) + } + // Codes with an excessive length should not be cached. + _, err = converter.Convert(make([]byte, maxCachedCodeLength+1), &tosca.Hash{1}) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + } + if want, got := 1, len(converter.cache.Keys()); want != got { + t.Errorf("Expected %d entries in the cache, got %d", want, got) + } +} + +func TestConverter_ResultsAreCached(t *testing.T) { + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + code := []byte{byte(vm.STOP)} + hash := tosca.Hash{byte(1)} + want, err := converter.Convert(code, &hash) + if err != nil { + t.Fatalf("failed to convert code: %v", err) + } + if got, found := converter.cache.Get(hash); !found || !slices.Equal(want, got) { + t.Errorf("converted code not added to cache") + } +} + +func TestConverter_ConverterIsThreadSafe(t *testing.T) { + // This test is to be run with --race to detect concurrency issues. + const ( + NumGoroutines = 100 + NumSteps = 1000 + ) + + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + t.Fatalf("failed to create converter: %v", err) + } + code := []byte{byte(vm.STOP)} + hash := tosca.Hash{byte(1)} + + errs, _ := errgroup.WithContext(t.Context()) + errs.SetLimit(NumGoroutines) + for i := range NumGoroutines { + errs.Go(func() error { + for j := 0; j < NumSteps; j++ { + // read a value every go routine is requesting + _, routineErr := converter.Convert(code, &hash) + if routineErr != nil { + return routineErr + } + // convert a value only this go routine is requesting + _, routineErr = converter.Convert(code, &tosca.Hash{byte(i), byte(j)}) + if routineErr != nil { + return routineErr + } + } + return nil + }) + } + err = errs.Wait() + if err != nil { + t.Fatalf("failed to run converter: %v", err) + } +} + +func TestConvertWithObserver_MapsEvmToLfvmPositions(t *testing.T) { + code := []byte{ + byte(vm.ADD), + byte(vm.PUSH1), 1, + byte(vm.PUSH3), 1, 2, 3, + byte(vm.SWAP1), + byte(vm.JUMPDEST), + } + + type pair struct { + evm, lfvm int + } + var pairs []pair + res := convertWithObserver(code, ConversionConfig{}, func(evm, lfvm int) { + pairs = append(pairs, pair{evm, lfvm}) + }) + + want := []pair{ + {0, 0}, + {1, 1}, + {3, 2}, + {7, 4}, + {8, 8}, + } + + if !slices.Equal(pairs, want) { + t.Errorf("Expected %v, got %v", want, pairs) + } + + for _, p := range pairs { + if want, got := OpCode(code[p.evm]), res[p.lfvm].opcode; want != got { + t.Errorf("Expected %v, got %v", want, got) + } + } +} + +func TestConvertWithObserver_PreservesJumpDestLocations(t *testing.T) { + r := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) + + for i := 0; i < 100; i++ { + code := make([]byte, 100) + r.Read(code) + + mapping := map[int]int{} + res := convertWithObserver(code, ConversionConfig{}, func(evm, lfvm int) { + if _, found := mapping[evm]; found { + t.Errorf("Duplicate mapping for EVM position %d", evm) + } + mapping[evm] = lfvm + }) + + // Check that all operations are mapped to matching operations. + for evm, lfvm := range mapping { + if want, got := OpCode(code[evm]), res[lfvm].opcode; want != got { + t.Errorf("Expected %v, got %v", want, got) + } + } + + // Check that the position of JUMPDESTs is preserved. + for evm, lfvm := range mapping { + if vm.OpCode(code[evm]) == vm.JUMPDEST { + if evm != lfvm { + t.Errorf("Expected JUMPDEST at %d, got %d", evm, lfvm) + } + } + } + + // Check that all JUMPDEST operations got mapped. + for i := 0; i < len(code); i++ { + cur := vm.OpCode(code[i]) + if cur == vm.JUMPDEST { + if _, found := mapping[i]; !found { + t.Errorf("JUMPDEST at %d not mapped", i) + } + } + if vm.PUSH1 <= cur && cur <= vm.PUSH32 { + i += int(cur - vm.PUSH1 + 1) + } + } + } +} + +func TestConvert_ProgramCounterBeyond16bitAreConvertedIntoInvalidInstructions(t *testing.T) { + max := math.MaxUint16 + positions := []int{0, 1, max / 2, max - 1, max, max + 1} + code := make([]byte, max+2) + for _, pos := range positions { + code[pos] = byte(vm.PC) + } + res := convert(code, ConversionConfig{}) + + for _, pos := range positions { + want := PC + if pos > max { + want = INVALID + } + if got := res[pos].opcode; want != got { + t.Errorf("Expected %v at position %d, got %v", want, pos, got) + } + } +} + +func TestConvert_BaseInstructionsAreConvertedToEquivalents(t *testing.T) { + config := ConversionConfig{ + WithSuperInstructions: false, + } + for _, op := range allOpCodesWhere(OpCode.isBaseInstruction) { + t.Run(op.String(), func(t *testing.T) { + code := []byte{byte(op)} + res := convert(code, config) + if want, got := op, res[0].opcode; want != got { + t.Errorf("Expected %v, got %v", want, got) + } + }) + } +} + +func TestConvert_PushOperationsUsePaddedImmediateData(t *testing.T) { + data := []byte{} + for i := 0; i < 32; i++ { + data = append(data, byte(i+1)) + } + for op := PUSH1; op <= PUSH32; op++ { + t.Run(op.String(), func(t *testing.T) { + // Test all possible truncated push data lengths. + length := int(op) - int(PUSH1) + 1 + for i := 0; i <= length; i++ { + code := append([]byte{byte(op)}, data[:i]...) + res := convert(code, ConversionConfig{}) + + // the push operation is correct + if want, got := op, res[0].opcode; want != got { + t.Errorf("Expected %v, got %v", want, got) + } + + // all the rest is data + for i, op := range res[1:] { + if op.opcode != DATA { + t.Errorf("Expected DATA at position %d, got %v", i, op.opcode) + } + } + + // there is enough data + if got, want := len(res), length/2+length%2; got != want { + t.Errorf("Expected %d instructions, got %d", want, got) + } + + // re-construct data + gotData := make([]byte, length+1) + for i, op := range res { + gotData[i*2] = byte(op.arg >> 8) + gotData[i*2+1] = byte(op.arg) + } + gotData = gotData[:length] + + // make sure prefix is correct + if want, got := data[:i], gotData[:i]; !bytes.Equal(want, got) { + t.Errorf("Expected %x, got %x", want, got) + } + + // make sure zero padding is correct + if want, got := make([]byte, length-i), gotData[i:]; !bytes.Equal(want, got) { + t.Errorf("Expected %x, got %x", want, got) + } + } + }) + } +} + +func TestConvert_AllJumpToOperationsPointToSubsequentJumpdest(t *testing.T) { + r := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) + + counter := 0 + for i := 0; i < 1000; i++ { + code := make([]byte, 100) + r.Read(code) + res := convert(code, ConversionConfig{}) + + for i, instruction := range res { + if instruction.opcode == JUMP_TO { + counter++ + trg := instruction.arg + if trg <= uint16(i) { + t.Errorf("JUMP_TO %d points to preceding position %d", trg, i) + } + if trg >= uint16(len(res)) { + t.Fatalf("JUMP_TO %d out of bounds", trg) + } + if res[trg].opcode != JUMPDEST { + t.Errorf("JUMP_TO %d does not point to JUMPDEST", trg) + } + + // Everything from the JUMP_TO to to the jump destination is a + // NOOP instruction. + for pos := i + 1; pos < int(trg); pos++ { + if res[pos].opcode != NOOP { + t.Errorf("Expected NOOP at position %d, got %v", pos, res[pos].opcode) + } + } + } + } + } + if counter == 0 { + t.Errorf("No JUMP_TO operations found") + } +} + +func TestConvert_SI_WhenEnabledSuperInstructionsAreUsed(t *testing.T) { + config := ConversionConfig{ + WithSuperInstructions: true, + } + for _, op := range allOpCodesWhere(OpCode.isSuperInstruction) { + t.Run(op.String(), func(t *testing.T) { + code := []byte{} + for _, op := range op.decompose() { + code = append(code, byte(op)) + if PUSH1 <= op && op <= PUSH32 { + code = append(code, make([]byte, int(op)-int(PUSH1)+1)...) + } + } + res := convert(code, config) + if want, got := op, res[0].opcode; want != got { + t.Errorf("Expected %v, got %v", want, got) + } + }) + } +} + +func TestConvert_SI_WhenDisabledNoSuperInstructionsAreUsed(t *testing.T) { + config := ConversionConfig{ + WithSuperInstructions: false, + } + for _, op := range allOpCodesWhere(OpCode.isSuperInstruction) { + t.Run(op.String(), func(t *testing.T) { + code := []byte{} + for _, op := range op.decompose() { + code = append(code, byte(op)) + } + + res := convert(code, config) + for i, instr := range res { + if instr.opcode.isSuperInstruction() { + t.Errorf("Super instruction %v used at position %d", instr.opcode, i) + } + } + }) + } +} + +func TestConverter_SI_FallsBackToLFVMInstructionsWhenNoSuperInstructionIsFit(t *testing.T) { + + config := ConversionConfig{ + WithSuperInstructions: true, + } + code := []byte{byte(PUSH2), 0x12, 0x34, byte(ADD), byte(PUSH1), 0x56, byte(SUB)} + convertedCode := convert(code, config) + if len(convertedCode) != 4 { + t.Fatalf("Expected 4 instructions, got %d", len(convertedCode)) + } + for _, inst := range convertedCode { + if inst.opcode.isSuperInstruction() { + t.Errorf("Super instruction %v used", inst.opcode) + } + } +} + +func benchmarkConvertCode(b *testing.B, code []byte, config ConversionConfig) { + converter, err := NewConverter(config) + if err != nil { + b.Fatalf("failed to create converter: %v", err) + } + for i := 0; i < b.N; i++ { + _, err = converter.Convert(code, nil) + if err != nil { + b.Fatalf("failed to convert code: %v", err) + } + } +} + +func BenchmarkConvertLongExampleCodeNoCache(b *testing.B) { + benchmarkConvertCode(b, longExampleCode, ConversionConfig{CacheSize: -1}) +} + +func BenchmarkConvertLongExampleCode(b *testing.B) { + benchmarkConvertCode(b, longExampleCode, ConversionConfig{}) +} + +func BenchmarkConversionCacheLookupSpeed(b *testing.B) { + // This benchmark measures the lookup speed of the conversion cache + // by converting the same code snippet multiple times. + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + b.Fatalf("failed to create converter: %v", err) + } + hash := &tosca.Hash{0} + for i := 0; i < b.N; i++ { + _, err = converter.Convert(nil, hash) + if err != nil { + b.Fatalf("failed to convert code: %v", err) + } + } +} + +func BenchmarkConversionCacheUpdateSpeed(b *testing.B) { + // This benchmark measures the update speed of the conversion cache + // by converting codes with different (reported) hashes. + converter, err := NewConverter(ConversionConfig{}) + if err != nil { + b.Fatalf("failed to create converter: %v", err) + } + for i := 0; i < b.N; i++ { + hash := tosca.Hash{byte(i), byte(i >> 8), byte(i >> 16), byte(i >> 24)} + _, err = converter.Convert(nil, &hash) + if err != nil { + b.Fatalf("failed to convert code: %v", err) + } + } +} diff --git a/go/interpreter/sfvm/ct.go b/go/interpreter/sfvm/ct.go new file mode 100644 index 00000000..479beb44 --- /dev/null +++ b/go/interpreter/sfvm/ct.go @@ -0,0 +1,214 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + + "github.com/0xsoniclabs/tosca/go/ct" + "github.com/0xsoniclabs/tosca/go/ct/common" + "github.com/0xsoniclabs/tosca/go/ct/st" + "github.com/0xsoniclabs/tosca/go/ct/utils" + "github.com/0xsoniclabs/tosca/go/tosca" + lru "github.com/hashicorp/golang-lru/v2" +) + +func NewConformanceTestingTarget() ct.Evm { + + // Can only fail for invalid configuration. Configuration is hardcoded. + sanctionedVm, _ := NewInterpreter(Config{}) + + // can only fail for non-positive size + cache, _ := lru.New[[32]byte, *pcMap](4096) + + return &ctAdapter{ + vm: sanctionedVm, + pcMapCache: cache, + } +} + +type ctAdapter struct { + vm *lfvm + pcMapCache *lru.Cache[[32]byte, *pcMap] +} + +func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { + params := utils.ToVmParameters(state) + if params.Revision > newestSupportedRevision { + return state, &tosca.ErrUnsupportedRevision{Revision: params.Revision} + } + + // No need to run anything that is not in a running state. + if state.Status != st.Running { + return state, nil + } + + converted, err := a.vm.converter.Convert( + params.Code, + params.CodeHash, + ) + if err != nil { + return &st.State{}, fmt.Errorf("failed to convert code: %w", err) + } + + pcMap := a.getPcMap(state.Code) + + memory := convertCtMemoryToLfvmMemory(state.Memory) + + // Set up execution context. + var ctxt = &context{ + pc: int32(pcMap.evmToLfvm[state.Pc]), + params: params, + context: params.Context, + gas: params.Gas, + refund: tosca.Gas(state.GasRefund), + stack: convertCtStackToLfvmStack(state.Stack), + memory: memory, + code: converted, + returnData: state.LastCallReturnData.ToBytes(), + withShaCache: a.vm.config.WithShaCache, + } + + defer func() { + ReturnStack(ctxt.stack) + }() + + // Run interpreter. + status := statusRunning + for i := 0; status == statusRunning && i < numSteps; i++ { + status = execute(ctxt, true) + } + + // Update the resulting state. + state.Status = convertLfvmStatusToCtStatus(status) + + if status == statusRunning { + state.Pc = pcMap.lfvmToEvm[ctxt.pc] + } + + state.Gas = ctxt.gas + state.GasRefund = ctxt.refund + state.Stack = convertLfvmStackToCtStack(ctxt.stack, state.Stack) + state.Memory = convertLfvmMemoryToCtMemory(ctxt.memory) + state.LastCallReturnData = common.NewBytes(ctxt.returnData) + if status == statusReturned || status == statusReverted { + state.ReturnData = common.NewBytes(ctxt.returnData) + } + + return state, nil +} + +func (a *ctAdapter) getPcMap(code *st.Code) *pcMap { + hash := code.Hash() + pcMap, found := a.pcMapCache.Get(hash) + if found { + return pcMap + } + byteCode := code.Copy() + pcMap = genPcMap(byteCode) + a.pcMapCache.Add(hash, pcMap) + return pcMap +} + +// pcMap is a bidirectional map to map program counters between evm <-> lfvm. +type pcMap struct { + evmToLfvm []uint16 + lfvmToEvm []uint16 +} + +// genPcMap creates a bidirectional program counter map for a given code, +// allowing mapping from a program counter in evm code to lfvm and vice versa. +func genPcMap(code []byte) *pcMap { + evmToLfvm := make([]uint16, len(code)+1) + lfvmToEvm := make([]uint16, len(code)+1) + + config := ConversionConfig{ + WithSuperInstructions: false, + } + res := convertWithObserver(code, config, func(evm, lfvm int) { + evmToLfvm[evm] = uint16(lfvm) + lfvmToEvm[lfvm] = uint16(evm) + }) + + // A program counter may correctly point to the position after the last + // instruction, which would lead to an implicit STOP. + evmToLfvm[len(code)] = uint16(len(res)) + + // The LFVM code could also be longer than the input code if extra padding + // of truncated PUSH instructions has been added. + if len(res)+1 > len(lfvmToEvm) { + lfvmToEvm = append(lfvmToEvm, make([]uint16, len(res)+1-len(lfvmToEvm))...) + } + lfvmToEvm[len(res)] = uint16(len(code)) + + // Locations pointing to JUMP_TO instructions in LFVM need to be updated to + // the position of the jump target. + for i := 0; i < len(res); i++ { + if res[i].opcode == JUMP_TO { + lfvmToEvm[i] = res[i].arg + } + } + + return &pcMap{ + evmToLfvm: evmToLfvm, + lfvmToEvm: lfvmToEvm, + } +} + +func convertLfvmStatusToCtStatus(status status) st.StatusCode { + switch status { + case statusRunning: + return st.Running + case statusReturned, statusStopped: + return st.Stopped + case statusReverted: + return st.Reverted + case statusSelfDestructed: + return st.Stopped + case statusFailed: + return st.Failed + } + return st.Failed +} + +func convertCtStackToLfvmStack(stack *st.Stack) *stack { + result := NewStack() + for i := stack.Size() - 1; i >= 0; i-- { + val := stack.Get(i).Uint256() + result.push(&val) + } + return result +} + +func convertLfvmStackToCtStack(stack *stack, result *st.Stack) *st.Stack { + len := stack.len() + result.Resize(len) + for i := 0; i < len; i++ { + result.Set(len-i-1, common.NewU256FromUint256(stack.get(i))) + } + return result +} + +func convertCtMemoryToLfvmMemory(memory *st.Memory) *Memory { + data := memory.Read(0, uint64(memory.Size())) + mem := NewMemory() + words := tosca.SizeInWords(uint64(len(data))) + mem.store = make([]byte, words*32) + copy(mem.store, data) + mem.currentMemoryCost = tosca.Gas((words*words)/512 + (3 * words)) + return mem +} + +func convertLfvmMemoryToCtMemory(memory *Memory) *st.Memory { + result := st.NewMemory() + result.Set(memory.store) + return result +} diff --git a/go/interpreter/sfvm/ct_test.go b/go/interpreter/sfvm/ct_test.go new file mode 100644 index 00000000..6b7950e3 --- /dev/null +++ b/go/interpreter/sfvm/ct_test.go @@ -0,0 +1,578 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "errors" + "math" + "reflect" + "testing" + + "github.com/0xsoniclabs/tosca/go/ct" + cc "github.com/0xsoniclabs/tosca/go/ct/common" + "github.com/0xsoniclabs/tosca/go/ct/st" + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" +) + +func TestCtAdapter_Add(t *testing.T) { + s := st.NewState(st.NewCode([]byte{ + byte(vm.PUSH1), 3, + byte(vm.PUSH1), 4, + byte(vm.ADD), + })) + s.Status = st.Running + s.Revision = tosca.R07_Istanbul + s.Pc = 0 + s.Gas = 100 + s.Stack = st.NewStack(cc.NewU256(1), cc.NewU256(2)) + defer s.Stack.Release() + s.Memory = st.NewMemory(1, 2, 3) + + c := NewConformanceTestingTarget() + + s, err := c.StepN(s, 4) + + if err != nil { + t.Fatalf("unexpected conversion error: %v", err) + } + + if want, got := st.Stopped, s.Status; want != got { + t.Fatalf("unexpected status: wanted %v, got %v", want, got) + } + + if want, got := cc.NewU256(3+4), s.Stack.Get(0); !want.Eq(got) { + t.Errorf("unexpected result: wanted %s, got %s", want, got) + } +} + +func TestCtAdapter_Interface(t *testing.T) { + // Compile time check that ctAdapter implements the st.Evm interface. + var _ ct.Evm = &ctAdapter{} +} + +func TestCTAdapter_DoesNotAddDuplicatedCodeToPCMap(t *testing.T) { + s := st.NewState(st.NewCode([]byte{ + byte(vm.STOP), + })) + c := NewConformanceTestingTarget() + + for i := 0; i < 3; i++ { + s.Status = st.Running + _, err := c.StepN(s, 1) + if err != nil { + t.Fatalf("unexpected conversion error: %v", err) + } + if want, got := 1, c.(*ctAdapter).pcMapCache.Len(); want != got { + t.Fatalf("unexpected pc map size, wanted %d, got %d", want, got) + } + } +} + +func TestCtAdapter_ReturnsErrorForUnsupportedRevisions(t *testing.T) { + unsupportedRevision := newestSupportedRevision + 1 + want := &tosca.ErrUnsupportedRevision{Revision: unsupportedRevision} + s := st.NewState(st.NewCode([]byte{ + byte(vm.STOP), + })) + s.Revision = unsupportedRevision + + c := NewConformanceTestingTarget() + _, err := c.StepN(s, 1) + + var e *tosca.ErrUnsupportedRevision + if !errors.As(err, &e) { + t.Errorf("unexpected error, wanted %v, got %v", want, err) + } +} + +func TestCtAdapter_ReturnsErrorIfCodeSizeExceeded(t *testing.T) { + s := st.NewState(st.NewCode(make([]byte, math.MaxUint16+1))) + s.Status = st.Running + s.Revision = tosca.R07_Istanbul + + c := NewConformanceTestingTarget() + _, err := c.StepN(s, 1) + + if !errors.Is(err, errCodeSizeExceeded) { + t.Errorf("unexpected error, wanted %v, got %v", errCodeSizeExceeded, err) + } +} + +func TestCtAdapter_DoesNotAffectNonRunningStates(t *testing.T) { + s := st.NewState(st.NewCode([]byte{ + byte(vm.STOP), + })) + s.Status = st.Stopped + + c := NewConformanceTestingTarget() + s2, err := c.StepN(s.Clone(), 1) + if err != nil { + t.Fatalf("unexpected conversion error: %v", err) + } + if !s.Eq(s2) { + t.Errorf("unexpected state, wanted %v, got %v", s, s2) + } +} + +func TestCtAdapter_SetsPcOnResultingState(t *testing.T) { + s := st.NewState(st.NewCode([]byte{ + byte(vm.PUSH1), + 0x01, + byte(vm.PUSH0), + })) + s.Gas = 100 + s.Stack = st.NewStack() + defer s.Stack.Release() + c := NewConformanceTestingTarget() + s2, err := c.StepN(s, 1) + if err != nil { + t.Fatalf("unexpected conversion error: %v", err) + } + if want, got := uint16(2), s2.Pc; want != got { + t.Errorf("unexpected pc, wanted %d, got %d", want, got) + } +} + +func TestCtAdapter_FillsReturnDataOnResultingState(t *testing.T) { + s := st.NewState(st.NewCode([]byte{ + byte(vm.PUSH1), byte(1), + byte(vm.PUSH1), byte(0), + byte(vm.RETURN), + })) + s.Gas = 100 + memory := []byte{0xFA} + s.Memory.Append(memory) + c := NewConformanceTestingTarget() + s2, err := c.StepN(s, 3) + if err != nil { + t.Fatalf("unexpected conversion error: %v", err) + } + if want, got := memory, s2.ReturnData.ToBytes(); !bytes.Equal(want, got) { + t.Errorf("unexpected return data, wanted %v, got %v", want, got) + } +} + +//////////////////////////////////////////////////////////// +// ct -> lfvm + +func TestConvertToLfvm_StatusCode(t *testing.T) { + + tests := map[status]st.StatusCode{ + statusRunning: st.Running, + statusReverted: st.Reverted, + statusReturned: st.Stopped, + statusStopped: st.Stopped, + statusSelfDestructed: st.Stopped, + statusFailed: st.Failed, + } + + for status, test := range tests { + got := convertLfvmStatusToCtStatus(status) + if want, got := test, got; want != got { + t.Errorf("unexpected conversion, wanted %v, got %v", want, got) + } + } +} + +func TestConvertToLfvm_StatusCodeFailsOnUnknownStatus(t *testing.T) { + status := convertLfvmStatusToCtStatus(statusFailed + 1) + if status != st.Failed { + t.Errorf("unexpected conversion, wanted %v, got %v", st.Failed, status) + } +} + +func TestConvertToLfvm_Pc(t *testing.T) { + tests := map[string][]struct { + evmCode []byte + evmPc uint16 + lfvmPc uint16 + }{ + "empty": {{}}, + "pos-0": {{[]byte{byte(vm.STOP)}, 0, 0}}, + "pos-1": {{[]byte{byte(vm.STOP), byte(vm.STOP), byte(vm.STOP)}, 1, 1}}, + "one-past-end": {{[]byte{byte(vm.STOP)}, 1, 1}}, + "shifted": {{[]byte{ + byte(vm.PUSH1), 0x01, + byte(vm.PUSH1), 0x02, + byte(vm.ADD)}, 2, 1}}, + "jumpdest": {{[]byte{ + byte(vm.PUSH3), 0x00, 0x00, 0x06, + byte(vm.JUMP), + byte(vm.INVALID), + byte(vm.JUMPDEST)}, + 6, 6}}, + "extra padding for truncated push": {{[]byte{ + byte(vm.PUSH14), 0x2e, 0x5a, 0x30, 0x10, 0x64, + }, 6, 7}}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + for _, cur := range test { + pcMap := genPcMap(cur.evmCode) + lfvmPc := pcMap.evmToLfvm[cur.evmPc] + if want, got := cur.lfvmPc, lfvmPc; want != got { + t.Errorf("invalid conversion, wanted %d, got %d", want, got) + } + } + }) + } +} + +func TestConvertToLfvm_Code(t *testing.T) { + tests := map[string][]struct { + evmCode []byte + lfvmCode Code + }{ + "empty": {{}}, + "stop": {{[]byte{byte(vm.STOP)}, Code{Instruction{STOP, 0x0000}}}}, + "add": {{[]byte{ + byte(vm.PUSH1), 0x01, + byte(vm.PUSH1), 0x02, + byte(vm.ADD)}, + Code{Instruction{PUSH1, 0x0100}, + Instruction{PUSH1, 0x0200}, + Instruction{ADD, 0x0000}}}}, + "jump": {{[]byte{ + byte(vm.PUSH1), 0x04, + byte(vm.JUMP), + byte(vm.INVALID), + byte(vm.JUMPDEST)}, + Code{Instruction{PUSH1, 0x0400}, + Instruction{JUMP, 0x0000}, + Instruction{INVALID, 0x0000}, + Instruction{JUMP_TO, 0x0004}, + Instruction{JUMPDEST, 0x0000}}}}, + "jumpdest": {{[]byte{ + byte(vm.PUSH3), 0x00, 0x00, 0x06, + byte(vm.JUMP), + byte(vm.INVALID), + byte(vm.JUMPDEST)}, + Code{Instruction{PUSH3, 0x0000}, + Instruction{DATA, 0x0600}, + Instruction{JUMP, 0x0000}, + Instruction{INVALID, 0x0000}, + Instruction{JUMP_TO, 0x0006}, + Instruction{NOOP, 0x0000}, + Instruction{JUMPDEST, 0x0000}}}}, + "push2": {{[]byte{byte(vm.PUSH2), 0xBA, 0xAD}, Code{Instruction{PUSH2, 0xBAAD}}}}, + "push3": {{[]byte{byte(vm.PUSH3), 0xBA, 0xAD, 0xC0}, Code{Instruction{PUSH3, 0xBAAD}, Instruction{DATA, 0xC000}}}}, + "push31": {{[]byte{ + byte(vm.PUSH31), + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}, + Code{Instruction{PUSH31, 0x0102}, + Instruction{DATA, 0x0304}, + Instruction{DATA, 0x0506}, + Instruction{DATA, 0x0708}, + Instruction{DATA, 0x090A}, + Instruction{DATA, 0x0B0C}, + Instruction{DATA, 0x0D0E}, + Instruction{DATA, 0x0F10}, + Instruction{DATA, 0x1112}, + Instruction{DATA, 0x1314}, + Instruction{DATA, 0x1516}, + Instruction{DATA, 0x1718}, + Instruction{DATA, 0x191A}, + Instruction{DATA, 0x1B1C}, + Instruction{DATA, 0x1D1E}, + Instruction{DATA, 0x1F00}}}}, + "push32": {{[]byte{ + byte(vm.PUSH32), + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF}, + Code{Instruction{PUSH32, 0x0102}, + Instruction{DATA, 0x0304}, + Instruction{DATA, 0x0506}, + Instruction{DATA, 0x0708}, + Instruction{DATA, 0x090A}, + Instruction{DATA, 0x0B0C}, + Instruction{DATA, 0x0D0E}, + Instruction{DATA, 0x0F10}, + Instruction{DATA, 0x1112}, + Instruction{DATA, 0x1314}, + Instruction{DATA, 0x1516}, + Instruction{DATA, 0x1718}, + Instruction{DATA, 0x191A}, + Instruction{DATA, 0x1B1C}, + Instruction{DATA, 0x1D1E}, + Instruction{DATA, 0x1FFF}}}}, + "invalid": {{[]byte{byte(vm.INVALID)}, Code{Instruction{INVALID, 0x0000}}}}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + for _, cur := range test { + got := convert(cur.evmCode, ConversionConfig{}) + + want := cur.lfvmCode + + if wantSize, gotSize := len(want), len(got); wantSize != gotSize { + t.Fatalf("unexpected code size, wanted %d, got %d", wantSize, gotSize) + } + + for i := 0; i < len(got); i++ { + if wantInst, gotInst := want[i], got[i]; wantInst != gotInst { + t.Errorf("unexpected instruction, wanted %v, got %v", wantInst, gotInst) + } + } + } + }) + } +} + +func TestConvertToLfvm_CodeWithSuperInstructions(t *testing.T) { + tests := map[string]struct { + evmCode []byte + want Code + }{ + "PUSH1PUSH4DUP3": { + []byte{byte(vm.PUSH1), 0x01, + byte(vm.PUSH4), 0x01, 0x02, 0x03, 0x04, + byte(vm.DUP3)}, + Code{Instruction{PUSH1_PUSH4_DUP3, 0x0100}, + Instruction{DATA, 0x0102}, + Instruction{DATA, 0x0304}, + }}, + "PUSH1_PUSH1_PUSH1_SHL_SUB": { + []byte{byte(vm.PUSH1), 0x01, + byte(vm.PUSH1), 0x01, + byte(vm.PUSH1), 0x01, + byte(vm.SHL), + byte(vm.SUB)}, + Code{Instruction{PUSH1_PUSH1_PUSH1_SHL_SUB, 0x0101}, + Instruction{DATA, 0x0001}, + }}, + "AND_SWAP1_POP_SWAP2_SWAP1": { + []byte{byte(vm.AND), byte(vm.SWAP1), byte(vm.POP), + byte(vm.SWAP2), byte(vm.SWAP1)}, + Code{Instruction{AND_SWAP1_POP_SWAP2_SWAP1, 0x0000}}}, + "ISZERO_PUSH2_JUMPI": { + []byte{byte(vm.ISZERO), + byte(vm.PUSH2), 0x01, 0x02, + byte(vm.JUMPI)}, + Code{Instruction{ISZERO_PUSH2_JUMPI, 0x0102}}}, + "SWAP2_SWAP1_POP_JUMP": { + []byte{byte(vm.SWAP2), byte(vm.SWAP1), byte(vm.POP), + byte(vm.JUMP)}, + Code{Instruction{SWAP2_SWAP1_POP_JUMP, 0x0000}}}, + "SWAP1_POP_SWAP2_SWAP1": { + []byte{byte(vm.SWAP1), byte(vm.POP), byte(vm.SWAP2), + byte(vm.SWAP1)}, + Code{Instruction{SWAP1_POP_SWAP2_SWAP1, 0x0000}}}, + "POP_SWAP2_SWAP1_POP": { + []byte{byte(vm.POP), byte(vm.SWAP2), byte(vm.SWAP1), + byte(vm.POP)}, + Code{Instruction{POP_SWAP2_SWAP1_POP, 0x0000}}}, + "PUSH2_JUMP": { + []byte{byte(vm.PUSH2), 0x01, 0x02, + byte(vm.JUMP)}, + Code{Instruction{PUSH2_JUMP, 0x0102}}}, + "PUSH2_JUMPI": { + []byte{byte(vm.PUSH2), 0x01, 0x02, + byte(vm.JUMPI)}, + Code{Instruction{PUSH2_JUMPI, 0x0102}}}, + "PUSH1_PUSH1": { + []byte{byte(vm.PUSH1), 0x01, + byte(vm.PUSH1), 0x01}, + Code{Instruction{PUSH1_PUSH1, 0x0101}}}, + "PUSH1_ADD": { + []byte{byte(vm.PUSH1), 0x01, + byte(vm.ADD)}, + Code{Instruction{PUSH1_ADD, 0x0001}}}, + "PUSH1_SHL": { + []byte{byte(vm.PUSH1), 0x01, + byte(vm.SHL)}, + Code{Instruction{PUSH1_SHL, 0x0001}}}, + "PUSH1_DUP1": { + []byte{byte(vm.PUSH1), 0x01, + byte(vm.DUP1)}, + Code{Instruction{PUSH1_DUP1, 0x0001}}}, + "SWAP1_POP": { + []byte{byte(vm.SWAP1), byte(vm.POP)}, + Code{Instruction{SWAP1_POP, 0x0000}}}, + "POP_JUMP": { + []byte{byte(vm.POP), byte(vm.JUMP)}, + Code{Instruction{POP_JUMP, 0x0000}}}, + "POP_POP": { + []byte{byte(vm.POP), byte(vm.POP)}, + Code{Instruction{POP_POP, 0x0000}}}, + "SWAP2_SWAP1": { + []byte{byte(vm.SWAP2), byte(vm.SWAP1)}, + Code{Instruction{SWAP2_SWAP1, 0x0000}}}, + "SWAP2_POP": { + []byte{byte(vm.SWAP2), byte(vm.POP)}, + Code{Instruction{SWAP2_POP, 0x0000}}}, + "DUP2_MSTORE": { + []byte{byte(vm.DUP2), byte(vm.MSTORE)}, + Code{Instruction{DUP2_MSTORE, 0x0000}}}, + "DUP2_LT": { + []byte{byte(vm.DUP2), byte(vm.LT)}, + Code{Instruction{DUP2_LT, 0x0000}}}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + options := ConversionConfig{WithSuperInstructions: true} + got := convert(test.evmCode, options) + if !reflect.DeepEqual(test.want, got) { + t.Fatalf("unexpected code, wanted %v, got %v", test.want, got) + } + }) + } +} + +func TestConvertToLfvm_Stack(t *testing.T) { + newLfvmStack := func(values ...cc.U256) *stack { + stack := NewStack() + for i := 0; i < len(values); i++ { + value := values[i].Uint256() + stack.push(&value) + } + return stack + } + + tests := map[string]struct { + ctStack *st.Stack + lfvmStack *stack + }{ + "empty": { + st.NewStack(), + newLfvmStack()}, + "one-element": { + st.NewStack(cc.NewU256(7)), + newLfvmStack(cc.NewU256(7))}, + "two-elements": { + st.NewStack(cc.NewU256(1), cc.NewU256(2)), + newLfvmStack(cc.NewU256(1), cc.NewU256(2))}, + "three-elements": { + st.NewStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3)), + newLfvmStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3))}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + stack := convertCtStackToLfvmStack(test.ctStack) + if want, got := test.lfvmStack.len(), stack.len(); want != got { + t.Fatalf("unexpected stack size, wanted %v, got %v", want, got) + } + for i := 0; i < stack.len(); i++ { + want := *test.lfvmStack.get(i) + got := *stack.get(i) + if want != got { + t.Errorf("unexpected stack value, wanted %v, got %v", want, got) + } + } + ReturnStack(test.lfvmStack) + ReturnStack(stack) + test.ctStack.Release() + }) + } +} + +//////////////////////////////////////////////////////////// +// lfvm -> ct + +func TestConvertToCt_Pc(t *testing.T) { + tests := map[string][]struct { + evmCode []byte + lfvmPc uint16 + evmPc uint16 + }{ + "empty": {{}}, + "pos-0": {{[]byte{byte(vm.STOP)}, 0, 0}}, + "pos-1": {{[]byte{byte(vm.STOP), byte(vm.STOP), byte(vm.STOP)}, 1, 1}}, + "one-past-end": {{[]byte{byte(vm.STOP)}, 1, 1}}, + "shifted": {{[]byte{ + byte(vm.PUSH1), 0x01, + byte(vm.PUSH1), 0x02, + byte(vm.ADD)}, 1, 2}}, + "jumpdest": {{[]byte{ + byte(vm.PUSH3), 0x00, 0x00, 0x06, + byte(vm.JUMP), + byte(vm.INVALID), + byte(vm.JUMPDEST)}, + 6, 6}}, + "extra padding for truncated push": {{[]byte{ + byte(vm.PUSH14), 0x2e, 0x5a, 0x30, 0x10, 0x64, + }, 7, 6}}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + for _, cur := range test { + pcMap := genPcMap(cur.evmCode) + evmPc := pcMap.lfvmToEvm[cur.lfvmPc] + if want, got := cur.evmPc, evmPc; want != got { + t.Errorf("invalid conversion, wanted %d, got %d", want, got) + } + } + }) + } +} + +func TestConvertToCt_Stack(t *testing.T) { + newLfvmStack := func(values ...cc.U256) *stack { + stack := NewStack() + for i := 0; i < len(values); i++ { + value := values[i].Uint256() + stack.push(&value) + } + return stack + } + + tests := map[string]struct { + lfvmStack *stack + ctStack *st.Stack + }{ + "empty": { + newLfvmStack(), + st.NewStack()}, + "one-element": { + newLfvmStack(cc.NewU256(7)), + st.NewStack(cc.NewU256(7))}, + "two-elements": { + newLfvmStack(cc.NewU256(1), cc.NewU256(2)), + st.NewStack(cc.NewU256(1), cc.NewU256(2))}, + "three-elements": { + newLfvmStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3)), + st.NewStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3))}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + want := test.ctStack + ctStack := st.NewStack() + got := convertLfvmStackToCtStack(test.lfvmStack, ctStack) + + diffs := got.Diff(want) + for _, diff := range diffs { + t.Errorf("%s", diff) + } + ReturnStack(test.lfvmStack) + test.ctStack.Release() + ctStack.Release() + }) + } +} + +func BenchmarkLfvmStackToCtStack(b *testing.B) { + stack := NewStack() + for i := 0; i < MAX_STACK_SIZE/2; i++ { + stack.pushUndefined().SetUint64(uint64(i)) + } + ctStack := st.NewStack() + for i := 0; i < b.N; i++ { + convertLfvmStackToCtStack(stack, ctStack) + } +} diff --git a/go/interpreter/sfvm/errors.go b/go/interpreter/sfvm/errors.go new file mode 100644 index 00000000..98c8eedc --- /dev/null +++ b/go/interpreter/sfvm/errors.go @@ -0,0 +1,28 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import "github.com/0xsoniclabs/tosca/go/tosca" + +const ( + errOverflow = tosca.ConstError("overflow") + errInvalidOpCode = tosca.ConstError("invalid op-code") + errInvalidRevision = tosca.ConstError("invalid revision") + errInvalidJump = tosca.ConstError("invalid jump destination") + errOutOfGas = tosca.ConstError("out of gas") + errStaticContextViolation = tosca.ConstError("static context violation") + errStackLimitsViolation = tosca.ConstError("stack limits violation") + errInitCodeTooLarge = tosca.ConstError("init code larger than allowed") + errMaxMemoryExpansionSize = tosca.ConstError("max memory expansion size exceeded") + errStackUnderflow = tosca.ConstError("stack underflow") + errStackOverflow = tosca.ConstError("stack overflow") + errCodeSizeExceeded = tosca.ConstError("max code size exceeded") +) diff --git a/go/interpreter/sfvm/example_code_test.go b/go/interpreter/sfvm/example_code_test.go new file mode 100644 index 00000000..6d8e3a90 --- /dev/null +++ b/go/interpreter/sfvm/example_code_test.go @@ -0,0 +1,16 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +// An example contract captured for testing and benchmarking. +var longExampleCode = []byte{ + 96, 4, 54, 16, 21, 97, 0, 13, 87, 97, 66, 157, 86, 91, 96, 0, 53, 96, 28, 82, 96, 0, 81, 52, 21, 97, 0, 33, 87, 96, 0, 128, 253, 91, 99, 49, 60, 229, 103, 129, 20, 21, 97, 0, 56, 87, 96, 18, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 169, 5, 156, 187, 129, 20, 21, 97, 0, 138, 87, 96, 4, 53, 96, 160, 28, 21, 97, 0, 84, 87, 96, 0, 128, 253, 91, 51, 97, 1, 64, 82, 96, 4, 53, 97, 1, 96, 82, 96, 36, 53, 97, 1, 128, 82, 97, 1, 128, 81, 97, 1, 96, 81, 97, 1, 64, 81, 96, 6, 88, 1, 97, 66, 163, 86, 91, 96, 0, 80, 96, 1, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 35, 184, 114, 221, 129, 20, 21, 97, 1, 115, 87, 96, 4, 53, 96, 160, 28, 21, 97, 0, 166, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 160, 28, 21, 97, 0, 182, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 1, 64, 82, 96, 36, 53, 97, 1, 96, 82, 96, 68, 53, 97, 1, 128, 82, 97, 1, 128, 81, 97, 1, 96, 81, 97, 1, 64, 81, 96, 6, 88, 1, 97, 66, 163, 86, 91, 96, 0, 80, 96, 22, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 84, 97, 1, 64, 82, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 1, 64, 81, 24, 21, 97, 1, 104, 87, 97, 1, 64, 81, 96, 68, 53, 128, 130, 16, 21, 97, 1, 67, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 22, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 85, 91, 96, 1, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 9, 94, 167, 179, 129, 20, 21, 97, 1, 236, 87, 96, 4, 53, 96, 160, 28, 21, 97, 1, 143, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 22, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 85, 96, 36, 53, 97, 1, 64, 82, 96, 4, 53, 51, 127, 140, 91, 225, 229, 235, 236, 125, 91, 209, 79, 113, 66, 125, 30, 132, 243, 221, 3, 20, 192, 247, 178, 41, 30, 91, 32, 10, 200, 199, 195, 185, 37, 96, 32, 97, 1, 64, 163, 96, 1, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 217, 108, 127, 206, 129, 20, 21, 97, 2, 33, 87, 96, 4, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 96, 64, 97, 1, 96, 243, 91, 99, 20, 240, 89, 121, 129, 20, 21, 97, 2, 86, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 96, 64, 97, 1, 96, 243, 91, 99, 15, 107, 168, 227, 129, 20, 21, 97, 3, 0, 87, 96, 64, 54, 97, 1, 64, 55, 97, 1, 128, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 68, 97, 1, 128, 81, 96, 2, 129, 16, 97, 2, 136, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 96, 4, 97, 1, 128, 81, 96, 2, 129, 16, 97, 2, 160, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 128, 130, 16, 21, 97, 2, 178, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 132, 53, 128, 128, 97, 2, 199, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 1, 64, 97, 1, 128, 81, 96, 2, 129, 16, 97, 2, 225, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 2, 117, 87, 91, 80, 80, 96, 64, 97, 1, 64, 243, 91, 99, 68, 105, 227, 14, 129, 20, 21, 97, 3, 53, 87, 96, 5, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 96, 64, 97, 1, 96, 243, 91, 99, 244, 70, 193, 208, 129, 20, 21, 97, 3, 100, 87, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 64, 82, 97, 1, 64, 81, 96, 100, 128, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 118, 162, 240, 240, 129, 20, 21, 97, 3, 138, 87, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 64, 82, 97, 1, 64, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 187, 123, 139, 128, 129, 20, 21, 97, 4, 255, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 64, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 96, 82, 80, 97, 1, 96, 81, 97, 1, 64, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 1, 192, 82, 97, 1, 224, 82, 97, 1, 192, 128, 81, 97, 2, 0, 82, 128, 96, 32, 1, 81, 97, 2, 32, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 64, 81, 97, 2, 96, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 0, 81, 97, 2, 128, 82, 97, 2, 32, 81, 97, 2, 160, 82, 97, 2, 96, 81, 97, 2, 192, 82, 97, 2, 192, 81, 97, 2, 160, 81, 97, 2, 128, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 3, 32, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 32, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 4, 219, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 23, 84, 128, 128, 97, 4, 240, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 94, 13, 68, 63, 129, 20, 21, 97, 5, 28, 87, 96, 0, 97, 1, 64, 82, 96, 0, 97, 1, 96, 82, 97, 5, 61, 86, 91, 99, 126, 66, 252, 12, 129, 20, 21, 97, 5, 56, 87, 96, 64, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 5, 61, 86, 91, 97, 6, 45, 86, 91, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 5, 77, 87, 25, 91, 96, 127, 28, 21, 97, 5, 90, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 5, 109, 87, 25, 91, 96, 127, 28, 21, 97, 5, 122, 87, 96, 0, 128, 253, 91, 144, 80, 80, 97, 1, 64, 81, 97, 1, 128, 82, 97, 1, 96, 81, 97, 1, 160, 82, 97, 1, 64, 81, 21, 21, 97, 5, 186, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 96, 4, 53, 97, 1, 192, 82, 96, 36, 53, 97, 1, 224, 82, 96, 68, 53, 97, 2, 0, 82, 97, 1, 128, 81, 97, 2, 32, 82, 97, 1, 160, 81, 97, 2, 64, 82, 97, 2, 64, 81, 97, 2, 32, 81, 97, 2, 0, 81, 97, 1, 224, 81, 97, 1, 192, 81, 96, 6, 88, 1, 97, 79, 140, 86, 91, 97, 2, 160, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 160, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 7, 33, 30, 247, 129, 20, 21, 97, 6, 74, 87, 96, 0, 97, 1, 64, 82, 96, 0, 97, 1, 96, 82, 97, 6, 107, 86, 91, 99, 227, 111, 213, 1, 129, 20, 21, 97, 6, 102, 87, 96, 64, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 6, 107, 86, 91, 97, 12, 244, 86, 91, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 6, 123, 87, 25, 91, 96, 127, 28, 21, 97, 6, 136, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 6, 155, 87, 25, 91, 96, 127, 28, 21, 97, 6, 168, 87, 96, 0, 128, 253, 91, 144, 80, 80, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 128, 82, 96, 32, 97, 2, 32, 96, 4, 99, 187, 123, 139, 128, 97, 1, 192, 82, 97, 1, 220, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 6, 240, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 6, 253, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 32, 81, 97, 1, 160, 82, 97, 1, 64, 81, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 64, 81, 21, 21, 97, 7, 69, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 192, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 224, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 1, 192, 81, 97, 2, 0, 82, 97, 1, 224, 81, 97, 2, 32, 82, 97, 2, 32, 81, 97, 2, 0, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 128, 82, 97, 2, 160, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 128, 128, 81, 97, 1, 192, 82, 128, 96, 32, 1, 81, 97, 1, 224, 82, 80, 96, 4, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 7, 198, 87, 25, 91, 96, 127, 28, 21, 97, 7, 211, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 0, 82, 96, 36, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 7, 242, 87, 25, 91, 96, 127, 28, 21, 97, 7, 255, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 32, 82, 96, 1, 97, 2, 64, 82, 96, 1, 97, 2, 96, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 8, 41, 87, 96, 4, 53, 97, 2, 64, 82, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 8, 61, 87, 96, 36, 53, 97, 2, 96, 82, 91, 96, 0, 97, 2, 128, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 8, 204, 87, 97, 1, 192, 96, 4, 53, 96, 2, 129, 16, 97, 8, 98, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 68, 53, 97, 1, 128, 96, 4, 53, 96, 2, 129, 16, 97, 8, 125, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 8, 150, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 8, 188, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 128, 82, 97, 10, 168, 86, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 10, 65, 87, 96, 64, 54, 97, 2, 160, 55, 96, 68, 53, 97, 2, 160, 97, 2, 0, 81, 96, 2, 129, 16, 97, 8, 246, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 96, 32, 97, 3, 160, 96, 100, 99, 237, 142, 132, 243, 97, 2, 224, 82, 97, 2, 160, 81, 97, 3, 0, 82, 97, 2, 192, 81, 97, 3, 32, 82, 96, 1, 97, 3, 64, 82, 97, 2, 252, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 9, 68, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 9, 81, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 160, 81, 97, 1, 160, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 9, 112, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 128, 82, 97, 2, 128, 128, 81, 97, 2, 128, 81, 96, 32, 97, 3, 64, 96, 4, 99, 221, 202, 63, 67, 97, 2, 224, 82, 97, 2, 252, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 9, 199, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 9, 212, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 64, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 9, 239, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 4, 168, 23, 200, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 10, 16, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 97, 2, 128, 128, 81, 97, 1, 224, 81, 129, 129, 131, 1, 16, 21, 97, 10, 50, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 10, 168, 86, 91, 96, 32, 97, 3, 96, 96, 100, 99, 94, 13, 68, 63, 97, 2, 160, 82, 97, 2, 0, 81, 97, 2, 192, 82, 97, 2, 32, 81, 97, 2, 224, 82, 96, 68, 53, 97, 3, 0, 82, 97, 2, 188, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 10, 139, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 10, 152, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 96, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 2, 64, 81, 97, 2, 192, 82, 97, 2, 96, 81, 97, 2, 224, 82, 97, 2, 128, 81, 97, 3, 0, 82, 97, 1, 192, 81, 97, 3, 32, 82, 97, 1, 224, 81, 97, 3, 64, 82, 97, 3, 64, 81, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 3, 160, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 160, 81, 97, 2, 160, 82, 97, 1, 192, 97, 2, 96, 81, 96, 2, 129, 16, 97, 11, 109, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 81, 128, 130, 16, 21, 97, 11, 131, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 11, 153, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 192, 82, 96, 2, 84, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 11, 191, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 97, 2, 192, 81, 97, 2, 224, 81, 128, 130, 16, 21, 97, 11, 236, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 12, 16, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 128, 97, 2, 96, 81, 96, 2, 129, 16, 97, 12, 43, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 12, 59, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 96, 0, 97, 2, 32, 81, 18, 21, 21, 97, 12, 231, 87, 96, 32, 97, 3, 160, 96, 68, 99, 204, 43, 39, 215, 97, 3, 0, 82, 97, 2, 192, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 12, 131, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 160, 81, 128, 128, 97, 12, 153, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 32, 82, 97, 2, 32, 81, 97, 3, 64, 82, 97, 3, 28, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 12, 206, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 12, 219, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 160, 81, 97, 2, 192, 82, 91, 97, 2, 192, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 237, 142, 132, 243, 129, 20, 21, 97, 13, 11, 87, 96, 0, 97, 1, 64, 82, 97, 13, 60, 86, 91, 99, 228, 126, 107, 158, 129, 20, 21, 97, 13, 55, 87, 96, 100, 53, 96, 1, 28, 21, 97, 13, 39, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 13, 60, 86, 91, 97, 15, 221, 86, 91, 96, 68, 53, 96, 1, 28, 21, 97, 13, 76, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 97, 1, 96, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 128, 81, 97, 1, 96, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 97, 1, 64, 81, 21, 97, 13, 191, 87, 96, 4, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 128, 81, 97, 1, 224, 82, 97, 1, 160, 81, 97, 2, 0, 82, 97, 1, 96, 81, 97, 2, 32, 82, 97, 2, 32, 81, 97, 2, 0, 81, 97, 1, 224, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 128, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 128, 81, 97, 1, 192, 82, 97, 1, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 68, 53, 21, 97, 14, 132, 87, 97, 1, 128, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 72, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 96, 4, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 97, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 129, 129, 131, 1, 16, 21, 97, 14, 117, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 14, 206, 86, 91, 97, 1, 128, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 152, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 96, 4, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 177, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 128, 130, 16, 21, 97, 14, 195, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 14, 44, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 1, 128, 81, 97, 2, 0, 82, 97, 1, 160, 81, 97, 2, 32, 82, 97, 1, 96, 81, 97, 2, 64, 82, 97, 2, 64, 81, 97, 2, 32, 81, 97, 2, 0, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 160, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 160, 81, 97, 1, 224, 82, 96, 0, 97, 2, 0, 82, 96, 68, 53, 21, 97, 15, 124, 87, 97, 1, 224, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 15, 108, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 0, 82, 97, 15, 157, 86, 91, 97, 1, 192, 81, 97, 1, 224, 81, 128, 130, 16, 21, 97, 15, 145, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 0, 82, 91, 97, 2, 0, 81, 96, 23, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 15, 184, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 192, 81, 128, 128, 97, 15, 206, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 11, 76, 126, 77, 129, 20, 21, 97, 15, 243, 87, 51, 97, 1, 64, 82, 97, 16, 36, 86, 91, 99, 12, 62, 75, 84, 129, 20, 21, 97, 16, 31, 87, 96, 100, 53, 96, 160, 28, 21, 97, 16, 15, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 16, 36, 86, 91, 97, 23, 175, 86, 91, 96, 24, 84, 21, 97, 16, 49, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 17, 84, 21, 97, 16, 67, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 97, 1, 64, 81, 97, 1, 96, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 128, 81, 97, 1, 96, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 128, 81, 97, 1, 224, 82, 97, 1, 160, 81, 97, 2, 0, 82, 97, 1, 96, 81, 97, 2, 32, 82, 97, 2, 32, 81, 97, 2, 0, 81, 97, 1, 224, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 128, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 128, 81, 97, 1, 192, 82, 96, 23, 84, 97, 1, 224, 82, 97, 1, 128, 81, 97, 2, 0, 82, 97, 1, 160, 81, 97, 2, 32, 82, 97, 2, 64, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 224, 81, 21, 21, 97, 17, 80, 87, 96, 0, 96, 4, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 65, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 17, 97, 17, 80, 87, 96, 0, 128, 253, 91, 97, 1, 128, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 100, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 4, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 124, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 129, 129, 131, 1, 16, 21, 97, 17, 144, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 0, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 171, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 17, 34, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 32, 81, 97, 2, 128, 82, 97, 1, 96, 81, 97, 2, 160, 82, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 96, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 3, 0, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 0, 81, 97, 2, 64, 82, 97, 1, 192, 81, 97, 2, 64, 81, 17, 97, 18, 86, 87, 96, 0, 128, 253, 91, 96, 64, 54, 97, 2, 96, 55, 97, 2, 64, 81, 97, 2, 160, 82, 96, 0, 97, 2, 192, 82, 96, 0, 97, 1, 224, 81, 17, 21, 97, 21, 189, 87, 96, 2, 84, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 18, 144, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 4, 128, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 96, 3, 84, 97, 3, 0, 82, 97, 3, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 2, 64, 81, 97, 1, 128, 97, 3, 32, 81, 96, 2, 129, 16, 97, 18, 207, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 18, 232, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 192, 81, 128, 128, 97, 18, 254, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 64, 82, 96, 0, 97, 3, 96, 82, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 34, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 64, 81, 17, 21, 97, 19, 107, 87, 97, 3, 64, 81, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 73, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 19, 91, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 96, 82, 97, 19, 161, 86, 91, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 127, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 64, 81, 128, 130, 16, 21, 97, 19, 149, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 96, 82, 91, 97, 2, 224, 81, 97, 3, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 19, 189, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 96, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 229, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 254, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 23, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 20, 52, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 20, 85, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 109, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 141, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 97, 2, 96, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 167, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 20, 185, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 18, 183, 87, 91, 80, 80, 97, 1, 64, 97, 3, 32, 82, 91, 97, 3, 32, 81, 81, 96, 32, 97, 3, 32, 81, 1, 97, 3, 32, 82, 97, 3, 32, 97, 3, 32, 81, 16, 21, 97, 21, 0, 87, 97, 20, 222, 86, 91, 97, 2, 0, 81, 97, 3, 64, 82, 97, 2, 32, 81, 97, 3, 96, 82, 97, 1, 96, 81, 97, 3, 128, 82, 97, 3, 128, 81, 97, 3, 96, 81, 97, 3, 64, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 3, 224, 82, 97, 3, 0, 97, 3, 32, 82, 91, 97, 3, 32, 81, 82, 96, 32, 97, 3, 32, 81, 3, 97, 3, 32, 82, 97, 1, 64, 97, 3, 32, 81, 16, 21, 21, 97, 21, 92, 87, 97, 21, 57, 86, 91, 97, 3, 224, 81, 97, 2, 160, 82, 97, 1, 224, 81, 97, 2, 160, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 21, 125, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 21, 152, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 192, 81, 128, 128, 97, 21, 174, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 97, 21, 224, 86, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 97, 2, 0, 81, 129, 85, 97, 2, 32, 81, 96, 1, 130, 1, 85, 80, 97, 2, 64, 81, 97, 2, 192, 82, 91, 96, 68, 53, 97, 2, 192, 81, 16, 21, 97, 21, 242, 87, 96, 0, 128, 253, 91, 97, 2, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 0, 96, 4, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 19, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 17, 21, 97, 22, 167, 87, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 47, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 22, 69, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 35, 184, 114, 221, 97, 3, 0, 82, 51, 97, 3, 32, 82, 48, 97, 3, 64, 82, 96, 4, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 113, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 97, 3, 96, 82, 97, 3, 28, 96, 0, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 144, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 22, 167, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 21, 254, 87, 91, 80, 80, 97, 1, 224, 128, 81, 97, 2, 192, 81, 129, 129, 131, 1, 16, 21, 97, 22, 209, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 96, 21, 97, 1, 64, 81, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 2, 192, 81, 129, 129, 131, 1, 16, 21, 97, 23, 1, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 85, 80, 97, 1, 224, 81, 96, 23, 85, 97, 2, 192, 81, 97, 2, 224, 82, 97, 1, 64, 81, 96, 0, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 2, 224, 163, 96, 4, 53, 97, 2, 224, 82, 96, 36, 53, 97, 3, 0, 82, 97, 2, 96, 81, 97, 3, 32, 82, 97, 2, 128, 81, 97, 3, 64, 82, 97, 2, 64, 81, 97, 3, 96, 82, 97, 1, 224, 81, 97, 3, 128, 82, 51, 127, 38, 245, 90, 133, 8, 29, 36, 151, 78, 133, 198, 192, 0, 69, 208, 240, 69, 57, 145, 233, 88, 115, 245, 43, 255, 13, 33, 175, 64, 121, 167, 104, 96, 192, 97, 2, 224, 162, 97, 2, 192, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 61, 240, 33, 36, 129, 20, 21, 97, 23, 197, 87, 51, 97, 1, 64, 82, 97, 23, 246, 86, 91, 99, 221, 193, 245, 157, 129, 20, 21, 97, 23, 241, 87, 96, 132, 53, 96, 160, 28, 21, 97, 23, 225, 87, 96, 0, 128, 253, 91, 96, 32, 96, 132, 97, 1, 64, 55, 96, 0, 80, 97, 23, 246, 86, 91, 97, 29, 218, 86, 91, 96, 24, 84, 21, 97, 24, 3, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 24, 24, 87, 25, 91, 96, 127, 28, 21, 97, 24, 37, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 24, 56, 87, 25, 91, 96, 127, 28, 21, 97, 24, 69, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 17, 84, 21, 97, 24, 85, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 96, 82, 96, 32, 97, 2, 0, 96, 4, 99, 187, 123, 139, 128, 97, 1, 160, 82, 97, 1, 188, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 24, 174, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 24, 187, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 0, 81, 97, 1, 128, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 160, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 192, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 1, 160, 81, 97, 2, 32, 82, 97, 1, 192, 81, 97, 2, 64, 82, 97, 2, 64, 81, 97, 2, 32, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 160, 82, 97, 2, 192, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 160, 128, 81, 97, 1, 224, 82, 128, 96, 32, 1, 81, 97, 2, 0, 82, 80, 97, 1, 224, 96, 4, 53, 96, 2, 129, 16, 97, 25, 111, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 68, 53, 97, 1, 96, 96, 4, 53, 96, 2, 129, 16, 97, 25, 138, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 25, 163, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 25, 201, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 32, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 96, 4, 53, 97, 2, 96, 82, 96, 36, 53, 97, 2, 128, 82, 97, 2, 32, 81, 97, 2, 160, 82, 97, 1, 224, 81, 97, 2, 192, 82, 97, 2, 0, 81, 97, 2, 224, 82, 97, 2, 224, 81, 97, 2, 192, 81, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 96, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 3, 64, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 64, 81, 97, 2, 64, 82, 97, 1, 224, 96, 36, 53, 96, 2, 129, 16, 97, 26, 126, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 64, 81, 128, 130, 16, 21, 97, 26, 148, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 26, 170, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 96, 82, 97, 2, 96, 81, 96, 2, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 26, 208, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 128, 82, 97, 2, 96, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 26, 253, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 27, 33, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 96, 36, 53, 96, 2, 129, 16, 97, 27, 59, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 27, 75, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 96, 100, 53, 97, 2, 96, 81, 16, 21, 97, 27, 103, 87, 96, 0, 128, 253, 91, 97, 2, 128, 81, 96, 3, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 27, 130, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 160, 82, 97, 2, 160, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 27, 187, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 96, 36, 53, 96, 2, 129, 16, 97, 27, 213, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 27, 229, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 160, 82, 97, 1, 160, 96, 4, 53, 96, 2, 129, 16, 97, 28, 2, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 68, 53, 129, 129, 131, 1, 16, 21, 97, 28, 25, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 4, 53, 96, 2, 129, 16, 97, 28, 48, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 1, 160, 96, 36, 53, 96, 2, 129, 16, 97, 28, 79, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 81, 128, 130, 16, 21, 97, 28, 101, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 160, 81, 128, 130, 16, 21, 97, 28, 125, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 36, 53, 96, 2, 129, 16, 97, 28, 148, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 96, 4, 53, 96, 2, 129, 16, 97, 28, 176, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 28, 198, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 35, 184, 114, 221, 97, 2, 192, 82, 51, 97, 2, 224, 82, 48, 97, 3, 0, 82, 96, 68, 53, 97, 3, 32, 82, 97, 2, 220, 96, 0, 96, 4, 53, 96, 2, 129, 16, 97, 28, 251, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 29, 18, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 2, 129, 16, 97, 29, 34, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 29, 56, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 2, 192, 82, 97, 1, 64, 81, 97, 2, 224, 82, 97, 2, 96, 81, 97, 3, 0, 82, 97, 2, 220, 96, 0, 96, 36, 53, 96, 2, 129, 16, 97, 29, 108, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 29, 131, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 2, 192, 82, 96, 68, 53, 97, 2, 224, 82, 96, 36, 53, 97, 3, 0, 82, 97, 2, 96, 81, 97, 3, 32, 82, 51, 127, 139, 62, 150, 242, 184, 137, 250, 119, 28, 83, 201, 129, 180, 13, 175, 0, 95, 99, 246, 55, 241, 134, 159, 112, 112, 82, 209, 90, 61, 217, 113, 64, 96, 128, 97, 2, 192, 162, 97, 2, 96, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 166, 65, 126, 214, 129, 20, 21, 97, 29, 240, 87, 51, 97, 1, 64, 82, 97, 30, 33, 86, 91, 99, 68, 238, 25, 134, 129, 20, 21, 97, 30, 28, 87, 96, 132, 53, 96, 160, 28, 21, 97, 30, 12, 87, 96, 0, 128, 253, 91, 96, 32, 96, 132, 97, 1, 64, 55, 96, 0, 80, 97, 30, 33, 86, 91, 97, 41, 39, 86, 91, 96, 24, 84, 21, 97, 30, 46, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 30, 67, 87, 25, 91, 96, 127, 28, 21, 97, 30, 80, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 30, 99, 87, 25, 91, 96, 127, 28, 21, 97, 30, 112, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 17, 84, 21, 97, 30, 128, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 96, 68, 53, 97, 1, 96, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 128, 82, 96, 32, 97, 2, 32, 96, 4, 99, 187, 123, 139, 128, 97, 1, 192, 82, 97, 1, 220, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 30, 224, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 30, 237, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 32, 81, 97, 1, 160, 82, 115, 141, 17, 236, 56, 163, 235, 94, 149, 107, 5, 47, 103, 218, 139, 220, 155, 239, 138, 191, 62, 97, 1, 192, 82, 115, 4, 6, 141, 166, 200, 58, 252, 250, 14, 19, 186, 21, 166, 105, 102, 98, 51, 93, 91, 117, 97, 1, 224, 82, 96, 4, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 31, 63, 87, 25, 91, 96, 127, 28, 21, 97, 31, 76, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 0, 82, 96, 36, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 31, 107, 87, 25, 91, 96, 127, 28, 21, 97, 31, 120, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 32, 82, 96, 1, 97, 2, 64, 82, 96, 1, 97, 2, 96, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 31, 162, 87, 96, 4, 53, 97, 2, 64, 82, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 31, 182, 87, 96, 36, 53, 97, 2, 96, 82, 91, 96, 96, 54, 97, 2, 128, 55, 96, 0, 97, 2, 0, 81, 18, 21, 97, 31, 238, 87, 96, 4, 53, 96, 2, 129, 16, 97, 31, 217, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 2, 160, 82, 97, 32, 12, 86, 91, 97, 1, 192, 97, 2, 0, 81, 96, 2, 129, 16, 97, 32, 2, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 82, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 32, 61, 87, 96, 36, 53, 96, 2, 129, 16, 97, 32, 40, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 2, 192, 82, 97, 32, 91, 86, 91, 97, 1, 192, 97, 2, 32, 81, 96, 2, 129, 16, 97, 32, 81, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 192, 82, 91, 97, 2, 160, 81, 59, 97, 32, 105, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 35, 184, 114, 221, 97, 2, 224, 82, 51, 97, 3, 0, 82, 48, 97, 3, 32, 82, 97, 1, 96, 81, 97, 3, 64, 82, 97, 2, 252, 96, 0, 97, 2, 160, 81, 90, 241, 97, 32, 158, 87, 96, 0, 128, 253, 91, 96, 0, 97, 2, 0, 81, 18, 21, 97, 32, 177, 87, 96, 1, 97, 32, 185, 86, 91, 96, 0, 97, 2, 32, 81, 18, 91, 21, 97, 39, 133, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 2, 224, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 3, 0, 82, 80, 97, 1, 64, 97, 3, 96, 82, 91, 97, 3, 96, 81, 81, 96, 32, 97, 3, 96, 81, 1, 97, 3, 96, 82, 97, 3, 96, 97, 3, 96, 81, 16, 21, 97, 33, 10, 87, 97, 32, 232, 86, 91, 97, 2, 224, 81, 97, 3, 128, 82, 97, 3, 0, 81, 97, 3, 160, 82, 97, 3, 160, 81, 97, 3, 128, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 4, 0, 82, 97, 4, 32, 82, 97, 3, 64, 97, 3, 96, 82, 91, 97, 3, 96, 81, 82, 96, 32, 97, 3, 96, 81, 3, 97, 3, 96, 82, 97, 1, 64, 97, 3, 96, 81, 16, 21, 21, 97, 33, 94, 87, 97, 33, 59, 86, 91, 97, 4, 0, 128, 81, 97, 3, 32, 82, 128, 96, 32, 1, 81, 97, 3, 64, 82, 80, 96, 0, 97, 3, 96, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 34, 1, 87, 97, 3, 32, 96, 4, 53, 96, 2, 129, 16, 97, 33, 150, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 96, 81, 97, 1, 128, 96, 4, 53, 96, 2, 129, 16, 97, 33, 178, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 33, 203, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 33, 241, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 3, 96, 82, 97, 35, 144, 86, 91, 96, 64, 54, 97, 3, 128, 55, 97, 1, 96, 81, 97, 3, 128, 97, 2, 0, 81, 96, 2, 129, 16, 97, 34, 32, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 96, 1, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 3, 192, 82, 96, 32, 97, 4, 96, 96, 36, 99, 112, 160, 130, 49, 97, 3, 224, 82, 48, 97, 4, 0, 82, 97, 3, 252, 97, 3, 192, 81, 90, 250, 97, 34, 94, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 34, 107, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 96, 81, 97, 3, 96, 82, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 59, 97, 34, 149, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 11, 76, 126, 77, 97, 3, 224, 82, 97, 3, 128, 81, 97, 4, 0, 82, 97, 3, 160, 81, 97, 4, 32, 82, 96, 0, 97, 4, 64, 82, 97, 3, 252, 96, 0, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 241, 97, 34, 223, 87, 96, 0, 128, 253, 91, 96, 32, 97, 4, 96, 96, 36, 99, 112, 160, 130, 49, 97, 3, 224, 82, 48, 97, 4, 0, 82, 97, 3, 252, 97, 3, 192, 81, 90, 250, 97, 35, 6, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 35, 19, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 96, 81, 97, 3, 96, 81, 128, 130, 16, 21, 97, 35, 43, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 96, 82, 97, 1, 96, 81, 97, 1, 160, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 35, 82, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 97, 3, 96, 82, 97, 3, 96, 128, 81, 97, 3, 64, 81, 129, 129, 131, 1, 16, 21, 97, 35, 133, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 91, 97, 1, 64, 97, 3, 160, 82, 91, 97, 3, 160, 81, 81, 96, 32, 97, 3, 160, 81, 1, 97, 3, 160, 82, 97, 3, 160, 97, 3, 160, 81, 16, 21, 97, 35, 186, 87, 97, 35, 152, 86, 91, 97, 2, 64, 81, 97, 3, 192, 82, 97, 2, 96, 81, 97, 3, 224, 82, 97, 3, 96, 81, 97, 4, 0, 82, 97, 3, 32, 81, 97, 4, 32, 82, 97, 3, 64, 81, 97, 4, 64, 82, 97, 4, 64, 81, 97, 4, 32, 81, 97, 4, 0, 81, 97, 3, 224, 81, 97, 3, 192, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 4, 160, 82, 97, 3, 128, 97, 3, 160, 82, 91, 97, 3, 160, 81, 82, 96, 32, 97, 3, 160, 81, 3, 97, 3, 160, 82, 97, 1, 64, 97, 3, 160, 81, 16, 21, 21, 97, 36, 46, 87, 97, 36, 11, 86, 91, 97, 4, 160, 81, 97, 3, 128, 82, 97, 3, 32, 97, 2, 96, 81, 96, 2, 129, 16, 97, 36, 74, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 128, 81, 128, 130, 16, 21, 97, 36, 96, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 36, 118, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 97, 2, 128, 81, 96, 2, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 36, 156, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 3, 160, 82, 97, 2, 128, 81, 97, 3, 160, 81, 128, 130, 16, 21, 97, 36, 201, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 36, 237, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 128, 97, 2, 96, 81, 96, 2, 129, 16, 97, 37, 8, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 37, 24, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 128, 82, 97, 3, 160, 81, 96, 3, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 37, 61, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 3, 192, 82, 97, 3, 192, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 37, 118, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 128, 97, 2, 96, 81, 96, 2, 129, 16, 97, 37, 145, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 37, 161, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 192, 82, 97, 2, 224, 97, 2, 64, 81, 96, 2, 129, 16, 97, 37, 191, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 96, 81, 129, 129, 131, 1, 16, 21, 97, 37, 215, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 64, 81, 96, 2, 129, 16, 97, 37, 239, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 2, 224, 97, 2, 96, 81, 96, 2, 129, 16, 97, 38, 15, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 38, 37, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 192, 81, 128, 130, 16, 21, 97, 38, 61, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 96, 81, 96, 2, 129, 16, 97, 38, 85, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 96, 0, 97, 2, 32, 81, 18, 21, 21, 97, 39, 110, 87, 96, 32, 97, 4, 128, 96, 36, 99, 112, 160, 130, 49, 97, 4, 0, 82, 48, 97, 4, 32, 82, 97, 4, 28, 97, 2, 192, 81, 90, 250, 97, 38, 149, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 38, 162, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 128, 81, 97, 3, 224, 82, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 59, 97, 38, 204, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 26, 77, 1, 210, 97, 4, 0, 82, 97, 2, 128, 81, 97, 4, 32, 82, 97, 2, 32, 81, 97, 4, 64, 82, 96, 0, 97, 4, 96, 82, 97, 4, 28, 96, 0, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 241, 97, 39, 22, 87, 96, 0, 128, 253, 91, 96, 32, 97, 4, 128, 96, 36, 99, 112, 160, 130, 49, 97, 4, 0, 82, 48, 97, 4, 32, 82, 97, 4, 28, 97, 2, 192, 81, 90, 250, 97, 39, 61, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 39, 74, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 128, 81, 97, 3, 224, 81, 128, 130, 16, 21, 97, 39, 98, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 91, 96, 100, 53, 97, 2, 128, 81, 16, 21, 97, 39, 128, 87, 96, 0, 128, 253, 91, 97, 40, 142, 86, 91, 96, 32, 97, 3, 96, 96, 36, 99, 112, 160, 130, 49, 97, 2, 224, 82, 48, 97, 3, 0, 82, 97, 2, 252, 97, 2, 192, 81, 90, 250, 97, 39, 172, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 39, 185, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 96, 81, 97, 2, 128, 82, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 59, 97, 39, 227, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 132, 99, 61, 240, 33, 36, 97, 2, 224, 82, 97, 2, 0, 81, 97, 3, 0, 82, 97, 2, 32, 81, 97, 3, 32, 82, 97, 1, 96, 81, 97, 3, 64, 82, 96, 100, 53, 97, 3, 96, 82, 97, 2, 252, 96, 0, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 241, 97, 40, 54, 87, 96, 0, 128, 253, 91, 96, 32, 97, 3, 96, 96, 36, 99, 112, 160, 130, 49, 97, 2, 224, 82, 48, 97, 3, 0, 82, 97, 2, 252, 97, 2, 192, 81, 90, 250, 97, 40, 93, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 40, 106, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 96, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 40, 130, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 91, 97, 2, 192, 81, 59, 97, 40, 156, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 2, 224, 82, 97, 1, 64, 81, 97, 3, 0, 82, 97, 2, 128, 81, 97, 3, 32, 82, 97, 2, 252, 96, 0, 97, 2, 192, 81, 90, 241, 97, 40, 207, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 2, 224, 82, 97, 1, 96, 81, 97, 3, 0, 82, 96, 36, 53, 97, 3, 32, 82, 97, 2, 128, 81, 97, 3, 64, 82, 51, 127, 208, 19, 202, 35, 231, 122, 101, 0, 60, 44, 101, 156, 84, 66, 192, 12, 128, 83, 113, 183, 252, 30, 189, 76, 32, 108, 65, 209, 83, 107, 217, 11, 96, 128, 97, 2, 224, 162, 97, 2, 128, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 91, 54, 56, 156, 129, 20, 21, 97, 41, 61, 87, 51, 97, 1, 64, 82, 97, 41, 110, 86, 91, 99, 62, 177, 113, 159, 129, 20, 21, 97, 41, 105, 87, 96, 100, 53, 96, 160, 28, 21, 97, 41, 89, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 41, 110, 86, 91, 97, 43, 247, 86, 91, 96, 24, 84, 21, 97, 41, 123, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 96, 23, 84, 97, 1, 96, 82, 96, 64, 54, 97, 1, 128, 55, 97, 1, 192, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 192, 81, 96, 2, 129, 16, 97, 41, 191, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 224, 82, 97, 1, 224, 81, 96, 4, 53, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 41, 234, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 128, 128, 97, 42, 0, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 0, 82, 96, 36, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 29, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 97, 2, 0, 81, 16, 21, 97, 42, 49, 87, 96, 0, 128, 253, 91, 97, 1, 224, 81, 97, 2, 0, 81, 128, 130, 16, 21, 97, 42, 70, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 94, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 2, 0, 81, 97, 1, 128, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 130, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 152, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 42, 174, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 2, 32, 82, 97, 1, 64, 81, 97, 2, 64, 82, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 60, 96, 0, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 227, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 42, 250, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 41, 174, 87, 91, 80, 80, 97, 1, 96, 128, 81, 96, 4, 53, 128, 130, 16, 21, 97, 43, 33, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 96, 21, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 96, 4, 53, 128, 130, 16, 21, 97, 43, 75, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 97, 1, 96, 81, 96, 23, 85, 96, 4, 53, 97, 1, 192, 82, 96, 0, 51, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 1, 192, 163, 97, 1, 128, 81, 97, 1, 192, 82, 97, 1, 160, 81, 97, 1, 224, 82, 96, 64, 54, 97, 2, 0, 55, 97, 1, 96, 81, 96, 4, 53, 128, 130, 16, 21, 97, 43, 184, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 64, 82, 51, 127, 124, 54, 56, 84, 204, 247, 150, 35, 65, 31, 137, 149, 179, 98, 188, 229, 237, 223, 241, 140, 146, 126, 220, 111, 93, 187, 181, 224, 88, 25, 168, 44, 96, 160, 97, 1, 192, 162, 96, 0, 96, 24, 85, 96, 64, 97, 1, 128, 243, 91, 99, 227, 16, 50, 115, 129, 20, 21, 97, 44, 13, 87, 51, 97, 1, 64, 82, 97, 44, 62, 86, 91, 99, 82, 210, 207, 221, 129, 20, 21, 97, 44, 57, 87, 96, 100, 53, 96, 160, 28, 21, 97, 44, 41, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 44, 62, 86, 91, 97, 51, 103, 86, 91, 96, 24, 84, 21, 97, 44, 75, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 17, 84, 21, 97, 44, 93, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 97, 1, 64, 81, 97, 1, 96, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 128, 81, 97, 1, 96, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 97, 1, 128, 81, 97, 1, 192, 82, 97, 1, 160, 81, 97, 1, 224, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 1, 128, 81, 97, 2, 32, 82, 97, 1, 160, 81, 97, 2, 64, 82, 97, 1, 96, 81, 97, 2, 96, 82, 97, 2, 96, 81, 97, 2, 64, 81, 97, 2, 32, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 192, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 192, 81, 97, 2, 0, 82, 97, 2, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 192, 97, 2, 32, 81, 96, 2, 129, 16, 97, 45, 89, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 96, 4, 97, 2, 32, 81, 96, 2, 129, 16, 97, 45, 114, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 128, 130, 16, 21, 97, 45, 132, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 45, 69, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 1, 192, 81, 97, 2, 64, 82, 97, 1, 224, 81, 97, 2, 96, 82, 97, 1, 96, 81, 97, 2, 128, 82, 97, 2, 128, 81, 97, 2, 96, 81, 97, 2, 64, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 224, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 224, 81, 97, 2, 32, 82, 96, 2, 84, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 46, 51, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 4, 128, 130, 4, 144, 80, 144, 80, 97, 2, 64, 82, 96, 3, 84, 97, 2, 96, 82, 96, 64, 54, 97, 2, 128, 55, 97, 2, 192, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 2, 32, 81, 97, 1, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 46, 121, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 46, 146, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 128, 128, 97, 46, 168, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 96, 0, 97, 3, 0, 82, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 46, 204, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 224, 81, 17, 21, 97, 47, 21, 87, 97, 2, 224, 81, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 46, 243, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 47, 5, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 0, 82, 97, 47, 75, 86, 91, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 41, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 224, 81, 128, 130, 16, 21, 97, 47, 63, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 0, 82, 91, 97, 2, 64, 81, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 47, 103, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 143, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 168, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 193, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 47, 222, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 47, 255, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 192, 81, 96, 2, 129, 16, 97, 48, 23, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 48, 55, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 97, 2, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 48, 81, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 48, 99, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 46, 97, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 2, 192, 81, 97, 1, 192, 81, 97, 2, 224, 82, 97, 1, 224, 81, 97, 3, 0, 82, 97, 1, 96, 81, 97, 3, 32, 82, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 3, 128, 82, 97, 2, 192, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 128, 81, 97, 2, 192, 82, 96, 23, 84, 97, 2, 224, 82, 97, 2, 0, 81, 97, 2, 192, 81, 128, 130, 16, 21, 97, 49, 61, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 224, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 49, 92, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 128, 128, 97, 49, 114, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 1, 129, 129, 131, 1, 16, 21, 97, 49, 137, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 3, 0, 82, 96, 1, 97, 3, 0, 81, 17, 97, 49, 164, 87, 96, 0, 128, 253, 91, 96, 68, 53, 97, 3, 0, 81, 17, 21, 97, 49, 182, 87, 96, 0, 128, 253, 91, 97, 2, 224, 128, 81, 97, 3, 0, 81, 128, 130, 16, 21, 97, 49, 204, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 97, 2, 224, 81, 96, 23, 85, 96, 21, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 3, 0, 81, 128, 130, 16, 21, 97, 49, 254, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 97, 3, 0, 81, 97, 3, 32, 82, 96, 0, 51, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 3, 32, 163, 97, 3, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 0, 96, 4, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 91, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 24, 21, 97, 50, 237, 87, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 119, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 50, 141, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 3, 64, 82, 97, 1, 64, 81, 97, 3, 96, 82, 96, 4, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 183, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 97, 3, 128, 82, 97, 3, 92, 96, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 214, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 50, 237, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 50, 70, 87, 91, 80, 80, 96, 4, 53, 97, 3, 32, 82, 96, 36, 53, 97, 3, 64, 82, 97, 2, 128, 81, 97, 3, 96, 82, 97, 2, 160, 81, 97, 3, 128, 82, 97, 2, 32, 81, 97, 3, 160, 82, 97, 2, 224, 81, 97, 3, 192, 82, 51, 127, 43, 85, 8, 55, 141, 126, 25, 224, 213, 250, 51, 132, 25, 3, 71, 49, 65, 108, 79, 91, 33, 154, 16, 55, 153, 86, 247, 100, 49, 127, 212, 126, 96, 192, 97, 3, 32, 162, 97, 3, 0, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 204, 43, 39, 215, 129, 20, 21, 97, 51, 126, 87, 96, 0, 97, 1, 64, 82, 97, 51, 175, 86, 91, 99, 197, 50, 167, 116, 129, 20, 21, 97, 51, 170, 87, 96, 68, 53, 96, 1, 28, 21, 97, 51, 154, 87, 96, 0, 128, 253, 91, 96, 32, 96, 68, 97, 1, 64, 55, 96, 0, 80, 97, 51, 175, 86, 91, 97, 52, 161, 86, 91, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 51, 191, 87, 25, 91, 96, 127, 28, 21, 97, 51, 204, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 97, 1, 64, 81, 21, 97, 52, 29, 87, 96, 4, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 96, 4, 53, 97, 1, 160, 82, 96, 36, 53, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 128, 81, 97, 2, 0, 82, 97, 2, 0, 81, 97, 1, 224, 81, 97, 1, 192, 81, 97, 1, 160, 81, 96, 6, 88, 1, 97, 86, 13, 86, 91, 97, 2, 96, 82, 97, 2, 128, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 96, 128, 128, 128, 128, 81, 97, 2, 160, 82, 80, 80, 96, 32, 129, 1, 144, 80, 128, 128, 128, 81, 97, 2, 192, 82, 80, 80, 80, 80, 97, 2, 160, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 26, 77, 1, 210, 129, 20, 21, 97, 52, 183, 87, 51, 97, 1, 64, 82, 97, 52, 232, 86, 91, 99, 8, 21, 121, 165, 129, 20, 21, 97, 52, 227, 87, 96, 100, 53, 96, 160, 28, 21, 97, 52, 211, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 52, 232, 86, 91, 97, 55, 175, 86, 91, 96, 24, 84, 21, 97, 52, 245, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 53, 10, 87, 25, 91, 96, 127, 28, 21, 97, 53, 23, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 17, 84, 21, 97, 53, 39, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 96, 64, 54, 97, 1, 96, 55, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 96, 4, 53, 97, 1, 160, 82, 96, 36, 53, 97, 1, 192, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 224, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 2, 0, 82, 80, 97, 2, 0, 81, 97, 1, 224, 81, 97, 1, 192, 81, 97, 1, 160, 81, 96, 6, 88, 1, 97, 86, 13, 86, 91, 97, 2, 96, 82, 97, 2, 128, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 96, 128, 128, 128, 128, 81, 97, 2, 160, 82, 80, 80, 96, 32, 129, 1, 144, 80, 128, 128, 128, 81, 97, 2, 192, 82, 80, 80, 80, 80, 97, 2, 160, 128, 81, 97, 1, 96, 82, 128, 96, 32, 1, 81, 97, 1, 128, 82, 80, 96, 68, 53, 97, 1, 96, 81, 16, 21, 97, 53, 240, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 2, 129, 16, 97, 54, 0, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 128, 84, 97, 1, 96, 81, 97, 1, 128, 81, 96, 3, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 54, 44, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 54, 79, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 128, 130, 16, 21, 97, 54, 99, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 96, 23, 84, 96, 4, 53, 128, 130, 16, 21, 97, 54, 128, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 160, 82, 97, 1, 160, 81, 96, 23, 85, 96, 21, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 96, 4, 53, 128, 130, 16, 21, 97, 54, 178, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 96, 4, 53, 97, 1, 192, 82, 96, 0, 51, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 1, 192, 163, 96, 36, 53, 96, 2, 129, 16, 97, 54, 253, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 55, 19, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 1, 192, 82, 97, 1, 64, 81, 97, 1, 224, 82, 97, 1, 96, 81, 97, 2, 0, 82, 97, 1, 220, 96, 0, 96, 36, 53, 96, 2, 129, 16, 97, 55, 71, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 55, 94, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 160, 81, 97, 2, 0, 82, 51, 127, 90, 208, 86, 242, 226, 138, 140, 236, 35, 32, 21, 64, 107, 132, 54, 104, 193, 227, 108, 218, 89, 129, 39, 236, 59, 140, 89, 184, 199, 39, 115, 160, 96, 96, 97, 1, 192, 162, 97, 1, 96, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 60, 21, 126, 100, 129, 20, 21, 97, 57, 80, 87, 96, 7, 84, 51, 20, 97, 55, 201, 87, 96, 0, 128, 253, 91, 96, 10, 84, 98, 1, 81, 128, 129, 129, 131, 1, 16, 21, 97, 55, 223, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 66, 16, 21, 97, 55, 242, 87, 96, 0, 128, 253, 91, 66, 98, 1, 81, 128, 129, 129, 131, 1, 16, 21, 97, 56, 6, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 36, 53, 16, 21, 97, 56, 27, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 96, 81, 97, 1, 64, 82, 96, 4, 53, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 56, 81, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 82, 96, 0, 96, 4, 53, 17, 21, 97, 56, 116, 87, 98, 15, 66, 64, 96, 4, 53, 16, 97, 56, 119, 86, 91, 96, 0, 91, 97, 56, 128, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 97, 1, 96, 81, 16, 21, 97, 56, 195, 87, 97, 1, 64, 81, 97, 1, 96, 81, 96, 10, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 56, 172, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 16, 21, 97, 56, 190, 87, 96, 0, 128, 253, 91, 97, 56, 243, 86, 91, 97, 1, 64, 81, 96, 10, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 56, 221, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 17, 21, 97, 56, 243, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 8, 85, 97, 1, 96, 81, 96, 9, 85, 66, 96, 10, 85, 96, 36, 53, 96, 11, 85, 97, 1, 64, 81, 97, 1, 128, 82, 97, 1, 96, 81, 97, 1, 160, 82, 66, 97, 1, 192, 82, 96, 36, 53, 97, 1, 224, 82, 127, 162, 183, 30, 198, 223, 148, 147, 0, 181, 154, 171, 54, 181, 94, 24, 150, 151, 183, 80, 17, 157, 211, 73, 252, 250, 140, 15, 119, 158, 131, 194, 84, 96, 128, 97, 1, 128, 161, 0, 91, 99, 85, 26, 101, 136, 129, 20, 21, 97, 57, 211, 87, 96, 7, 84, 51, 20, 97, 57, 106, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 96, 81, 97, 1, 64, 82, 97, 1, 64, 81, 96, 8, 85, 97, 1, 64, 81, 96, 9, 85, 66, 96, 10, 85, 66, 96, 11, 85, 97, 1, 64, 81, 97, 1, 96, 82, 66, 97, 1, 128, 82, 127, 70, 226, 47, 179, 112, 154, 210, 137, 246, 44, 230, 61, 70, 146, 72, 83, 109, 188, 120, 216, 43, 132, 163, 215, 231, 74, 214, 6, 220, 32, 25, 56, 96, 64, 97, 1, 96, 161, 0, 91, 99, 91, 90, 20, 103, 129, 20, 21, 97, 58, 143, 87, 96, 7, 84, 51, 20, 97, 57, 237, 87, 96, 0, 128, 253, 91, 96, 12, 84, 21, 97, 57, 250, 87, 96, 0, 128, 253, 91, 100, 1, 42, 5, 242, 0, 96, 4, 53, 17, 21, 97, 58, 14, 87, 96, 0, 128, 253, 91, 100, 2, 84, 11, 228, 0, 96, 36, 53, 17, 21, 97, 58, 34, 87, 96, 0, 128, 253, 91, 66, 98, 3, 244, 128, 129, 129, 131, 1, 16, 21, 97, 58, 54, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 1, 64, 82, 97, 1, 64, 81, 96, 12, 85, 96, 4, 53, 96, 14, 85, 96, 36, 53, 96, 15, 85, 96, 4, 53, 97, 1, 96, 82, 96, 36, 53, 97, 1, 128, 82, 97, 1, 64, 81, 127, 53, 31, 197, 218, 47, 191, 72, 15, 34, 37, 222, 191, 54, 100, 164, 188, 144, 250, 153, 35, 116, 58, 173, 88, 180, 96, 63, 100, 142, 147, 31, 224, 96, 64, 97, 1, 96, 162, 0, 91, 99, 79, 18, 254, 151, 129, 20, 21, 97, 59, 33, 87, 96, 7, 84, 51, 20, 97, 58, 169, 87, 96, 0, 128, 253, 91, 96, 12, 84, 66, 16, 21, 97, 58, 184, 87, 96, 0, 128, 253, 91, 96, 0, 96, 12, 84, 24, 97, 58, 199, 87, 96, 0, 128, 253, 91, 96, 0, 96, 12, 85, 96, 14, 84, 97, 1, 64, 82, 96, 15, 84, 97, 1, 96, 82, 97, 1, 64, 81, 96, 2, 85, 97, 1, 96, 81, 96, 3, 85, 97, 1, 64, 81, 97, 1, 128, 82, 97, 1, 96, 81, 97, 1, 160, 82, 127, 190, 18, 133, 155, 99, 106, 237, 96, 125, 82, 48, 178, 204, 39, 17, 246, 141, 112, 229, 16, 96, 230, 204, 161, 245, 117, 239, 93, 47, 204, 149, 209, 96, 64, 97, 1, 128, 161, 0, 91, 99, 34, 104, 64, 251, 129, 20, 21, 97, 59, 66, 87, 96, 7, 84, 51, 20, 97, 59, 59, 87, 96, 0, 128, 253, 91, 96, 0, 96, 12, 85, 0, 91, 99, 107, 68, 26, 64, 129, 20, 21, 97, 59, 212, 87, 96, 4, 53, 96, 160, 28, 21, 97, 59, 94, 87, 96, 0, 128, 253, 91, 96, 7, 84, 51, 20, 97, 59, 108, 87, 96, 0, 128, 253, 91, 96, 13, 84, 21, 97, 59, 121, 87, 96, 0, 128, 253, 91, 66, 98, 3, 244, 128, 129, 129, 131, 1, 16, 21, 97, 59, 141, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 1, 64, 82, 97, 1, 64, 81, 96, 13, 85, 96, 4, 53, 96, 16, 85, 96, 4, 53, 97, 1, 64, 81, 127, 24, 26, 163, 170, 23, 212, 203, 249, 146, 101, 221, 68, 67, 235, 160, 9, 67, 61, 60, 222, 121, 214, 1, 100, 253, 225, 209, 161, 146, 190, 185, 53, 96, 0, 96, 0, 163, 0, 91, 99, 106, 28, 5, 174, 129, 20, 21, 97, 60, 75, 87, 96, 7, 84, 51, 20, 97, 59, 238, 87, 96, 0, 128, 253, 91, 96, 13, 84, 66, 16, 21, 97, 59, 253, 87, 96, 0, 128, 253, 91, 96, 0, 96, 13, 84, 24, 97, 60, 12, 87, 96, 0, 128, 253, 91, 96, 0, 96, 13, 85, 96, 16, 84, 97, 1, 64, 82, 97, 1, 64, 81, 96, 7, 85, 97, 1, 64, 81, 127, 113, 97, 64, 113, 184, 141, 238, 94, 11, 42, 229, 120, 169, 221, 123, 46, 187, 233, 174, 131, 43, 164, 25, 220, 2, 66, 205, 6, 90, 41, 11, 108, 96, 0, 96, 0, 162, 0, 91, 99, 134, 251, 241, 147, 129, 20, 21, 97, 60, 108, 87, 96, 7, 84, 51, 20, 97, 60, 101, 87, 96, 0, 128, 253, 91, 96, 0, 96, 13, 85, 0, 91, 99, 226, 231, 210, 100, 129, 20, 21, 97, 61, 4, 87, 96, 32, 97, 1, 192, 96, 36, 99, 112, 160, 130, 49, 97, 1, 64, 82, 48, 97, 1, 96, 82, 97, 1, 92, 96, 4, 53, 96, 2, 129, 16, 97, 60, 160, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 250, 97, 60, 183, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 60, 196, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 1, 192, 81, 96, 4, 53, 96, 2, 129, 16, 97, 60, 219, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 128, 130, 16, 21, 97, 60, 244, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 48, 197, 64, 133, 129, 20, 21, 97, 62, 25, 87, 96, 7, 84, 51, 20, 97, 61, 30, 87, 96, 0, 128, 253, 91, 97, 1, 64, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 64, 81, 96, 2, 129, 16, 97, 61, 59, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 96, 82, 96, 32, 97, 2, 32, 96, 36, 99, 112, 160, 130, 49, 97, 1, 160, 82, 48, 97, 1, 192, 82, 97, 1, 188, 97, 1, 96, 81, 90, 250, 97, 61, 114, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 61, 127, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 32, 81, 97, 1, 64, 81, 96, 2, 129, 16, 97, 61, 151, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 128, 130, 16, 21, 97, 61, 176, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 128, 82, 96, 0, 97, 1, 128, 81, 17, 21, 97, 62, 5, 87, 97, 1, 96, 81, 59, 97, 61, 213, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 1, 160, 82, 51, 97, 1, 192, 82, 97, 1, 128, 81, 97, 1, 224, 82, 97, 1, 188, 96, 0, 97, 1, 96, 81, 90, 241, 97, 62, 5, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 61, 42, 87, 91, 80, 80, 0, 91, 99, 82, 76, 57, 1, 129, 20, 21, 97, 62, 197, 87, 96, 7, 84, 51, 20, 97, 62, 51, 87, 96, 0, 128, 253, 91, 97, 1, 64, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 32, 97, 1, 224, 96, 36, 99, 112, 160, 130, 49, 97, 1, 96, 82, 48, 97, 1, 128, 82, 97, 1, 124, 97, 1, 64, 81, 96, 2, 129, 16, 97, 62, 104, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 250, 97, 62, 127, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 62, 140, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 1, 224, 81, 97, 1, 64, 81, 96, 2, 129, 16, 97, 62, 164, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 62, 63, 87, 91, 80, 80, 0, 91, 99, 227, 105, 136, 83, 129, 20, 21, 97, 62, 244, 87, 96, 7, 84, 51, 20, 97, 62, 223, 87, 96, 0, 128, 253, 91, 66, 96, 18, 84, 17, 97, 62, 237, 87, 96, 0, 128, 253, 91, 96, 1, 96, 17, 85, 0, 91, 99, 48, 70, 249, 114, 129, 20, 21, 97, 63, 21, 87, 96, 7, 84, 51, 20, 97, 63, 14, 87, 96, 0, 128, 253, 91, 96, 0, 96, 17, 85, 0, 91, 99, 198, 97, 6, 87, 129, 20, 21, 97, 63, 70, 87, 96, 4, 53, 96, 2, 129, 16, 97, 63, 49, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 73, 3, 176, 209, 129, 20, 21, 97, 63, 119, 87, 96, 4, 53, 96, 2, 129, 16, 97, 63, 98, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 221, 202, 63, 67, 129, 20, 21, 97, 63, 143, 87, 96, 2, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 254, 227, 247, 249, 129, 20, 21, 97, 63, 167, 87, 96, 3, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 99, 84, 63, 6, 129, 20, 21, 97, 63, 191, 87, 96, 6, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 141, 165, 203, 91, 129, 20, 21, 97, 63, 215, 87, 96, 7, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 84, 9, 73, 26, 129, 20, 21, 97, 63, 239, 87, 96, 8, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 180, 181, 119, 173, 129, 20, 21, 97, 64, 7, 87, 96, 9, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 32, 129, 6, 108, 129, 20, 21, 97, 64, 31, 87, 96, 10, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 20, 5, 34, 136, 129, 20, 21, 97, 64, 55, 87, 96, 11, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 64, 94, 40, 248, 129, 20, 21, 97, 64, 79, 87, 96, 12, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 224, 160, 181, 134, 129, 20, 21, 97, 64, 103, 87, 96, 13, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 88, 104, 13, 11, 129, 20, 21, 97, 64, 127, 87, 96, 14, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 227, 130, 68, 98, 129, 20, 21, 97, 64, 151, 87, 96, 15, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 30, 192, 205, 193, 129, 20, 21, 97, 64, 175, 87, 96, 16, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 6, 253, 222, 3, 129, 20, 21, 97, 65, 84, 87, 96, 19, 128, 96, 192, 82, 96, 32, 96, 192, 32, 97, 1, 128, 96, 32, 130, 84, 1, 97, 1, 32, 96, 0, 96, 3, 129, 131, 82, 1, 91, 130, 97, 1, 32, 81, 96, 32, 2, 17, 21, 97, 64, 237, 87, 97, 65, 15, 86, 91, 97, 1, 32, 81, 133, 1, 84, 97, 1, 32, 81, 96, 32, 2, 133, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 64, 218, 87, 91, 80, 80, 80, 80, 80, 80, 97, 1, 128, 81, 128, 97, 1, 160, 1, 129, 130, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 3, 54, 130, 55, 80, 80, 96, 32, 97, 1, 96, 82, 96, 64, 97, 1, 128, 81, 1, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 97, 1, 96, 243, 91, 99, 149, 216, 155, 65, 129, 20, 21, 97, 65, 249, 87, 96, 20, 128, 96, 192, 82, 96, 32, 96, 192, 32, 97, 1, 128, 96, 32, 130, 84, 1, 97, 1, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 130, 97, 1, 32, 81, 96, 32, 2, 17, 21, 97, 65, 146, 87, 97, 65, 180, 86, 91, 97, 1, 32, 81, 133, 1, 84, 97, 1, 32, 81, 96, 32, 2, 133, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 65, 127, 87, 91, 80, 80, 80, 80, 80, 80, 97, 1, 128, 81, 128, 97, 1, 160, 1, 129, 130, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 3, 54, 130, 55, 80, 80, 96, 32, 97, 1, 96, 82, 96, 64, 97, 1, 128, 81, 1, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 97, 1, 96, 243, 91, 99, 112, 160, 130, 49, 129, 20, 21, 97, 66, 47, 87, 96, 4, 53, 96, 160, 28, 21, 97, 66, 21, 87, 96, 0, 128, 253, 91, 96, 21, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 221, 98, 237, 62, 129, 20, 21, 97, 66, 131, 87, 96, 4, 53, 96, 160, 28, 21, 97, 66, 75, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 160, 28, 21, 97, 66, 91, 87, 96, 0, 128, 253, 91, 96, 22, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 96, 36, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 24, 22, 13, 221, 129, 20, 21, 97, 66, 155, 87, 96, 23, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 80, 91, 96, 0, 96, 0, 253, 91, 97, 1, 160, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 96, 21, 97, 1, 64, 81, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 1, 128, 81, 128, 130, 16, 21, 97, 66, 215, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 96, 21, 97, 1, 96, 81, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 1, 128, 81, 129, 129, 131, 1, 16, 21, 97, 67, 7, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 85, 80, 97, 1, 128, 81, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 64, 81, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 1, 192, 163, 97, 1, 160, 81, 86, 91, 97, 1, 64, 82, 66, 96, 6, 84, 128, 130, 16, 21, 97, 67, 99, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 96, 82, 96, 0, 97, 1, 96, 81, 17, 21, 97, 68, 58, 87, 97, 1, 128, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 128, 81, 96, 2, 129, 16, 97, 67, 151, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 97, 1, 128, 81, 96, 2, 129, 16, 97, 67, 184, 87, 96, 0, 128, 253, 91, 96, 5, 96, 192, 82, 96, 32, 96, 192, 32, 1, 128, 84, 97, 1, 160, 81, 97, 1, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 67, 225, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 67, 247, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 85, 80, 97, 1, 160, 81, 97, 1, 128, 81, 96, 2, 129, 16, 97, 68, 22, 87, 96, 0, 128, 253, 91, 96, 4, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 67, 134, 87, 91, 80, 80, 66, 96, 6, 85, 91, 97, 1, 64, 81, 86, 91, 97, 1, 64, 82, 96, 11, 84, 97, 1, 96, 82, 96, 9, 84, 97, 1, 128, 82, 97, 1, 96, 81, 66, 16, 21, 97, 69, 198, 87, 96, 8, 84, 97, 1, 160, 82, 96, 10, 84, 97, 1, 192, 82, 97, 1, 160, 81, 97, 1, 128, 81, 17, 21, 97, 69, 32, 87, 97, 1, 160, 81, 97, 1, 128, 81, 97, 1, 160, 81, 128, 130, 16, 21, 97, 68, 146, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 66, 97, 1, 192, 81, 128, 130, 16, 21, 97, 68, 171, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 68, 198, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 68, 226, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 68, 244, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 69, 9, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 0, 82, 96, 0, 81, 97, 1, 64, 81, 86, 97, 69, 193, 86, 91, 97, 1, 160, 81, 97, 1, 160, 81, 97, 1, 128, 81, 128, 130, 16, 21, 97, 69, 57, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 66, 97, 1, 192, 81, 128, 130, 16, 21, 97, 69, 82, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 69, 109, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 69, 137, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 69, 155, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 69, 174, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 0, 82, 96, 0, 81, 97, 1, 64, 81, 86, 91, 97, 69, 214, 86, 91, 97, 1, 128, 81, 96, 0, 82, 96, 0, 81, 97, 1, 64, 81, 86, 91, 0, 91, 97, 1, 128, 82, 97, 1, 64, 82, 97, 1, 96, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 160, 82, 96, 32, 97, 2, 64, 96, 4, 99, 187, 123, 139, 128, 97, 1, 224, 82, 97, 1, 252, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 70, 41, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 70, 54, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 64, 81, 97, 1, 192, 82, 97, 1, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 160, 97, 1, 224, 81, 96, 2, 129, 16, 97, 70, 97, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 64, 97, 1, 224, 81, 96, 2, 129, 16, 97, 70, 122, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 70, 147, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 97, 1, 160, 97, 1, 224, 81, 96, 2, 129, 16, 97, 70, 190, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 70, 77, 87, 91, 80, 80, 96, 64, 97, 1, 224, 82, 91, 96, 0, 97, 1, 224, 81, 17, 21, 21, 97, 70, 239, 87, 97, 71, 11, 86, 91, 96, 32, 97, 1, 224, 81, 3, 97, 1, 160, 1, 81, 96, 32, 97, 1, 224, 81, 3, 97, 1, 224, 82, 97, 70, 221, 86, 91, 97, 1, 128, 81, 86, 91, 97, 1, 160, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 96, 64, 54, 97, 1, 192, 55, 97, 2, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 32, 97, 2, 32, 81, 2, 97, 1, 64, 1, 81, 97, 2, 0, 82, 97, 1, 192, 128, 81, 97, 2, 0, 81, 129, 129, 131, 1, 16, 21, 97, 71, 92, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 71, 52, 87, 91, 80, 80, 97, 1, 192, 81, 21, 21, 97, 71, 145, 87, 96, 0, 96, 0, 82, 96, 0, 81, 97, 1, 160, 81, 86, 91, 97, 1, 192, 81, 97, 2, 0, 82, 97, 1, 128, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 71, 179, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 32, 82, 97, 2, 64, 96, 0, 96, 255, 129, 131, 82, 1, 91, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 160, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 32, 97, 2, 160, 81, 2, 97, 1, 64, 1, 81, 97, 2, 128, 82, 97, 2, 96, 81, 97, 2, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 10, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 128, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 43, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 72, 61, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 71, 222, 87, 91, 80, 80, 97, 2, 0, 81, 97, 1, 224, 82, 97, 2, 32, 81, 97, 1, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 126, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 4, 144, 80, 144, 80, 97, 2, 96, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 168, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 72, 190, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 221, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 32, 81, 96, 100, 128, 130, 16, 21, 97, 72, 247, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 73, 22, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 4, 144, 80, 144, 80, 96, 3, 97, 2, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 73, 64, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 73, 86, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 128, 128, 97, 73, 104, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 0, 82, 97, 1, 224, 81, 97, 2, 0, 81, 17, 21, 97, 73, 188, 87, 96, 1, 97, 2, 0, 81, 97, 1, 224, 81, 128, 130, 16, 21, 97, 73, 151, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 73, 183, 87, 97, 2, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 160, 81, 86, 91, 97, 73, 243, 86, 91, 96, 1, 97, 1, 224, 81, 97, 2, 0, 81, 128, 130, 16, 21, 97, 73, 211, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 73, 243, 87, 97, 2, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 160, 81, 86, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 71, 202, 87, 91, 80, 80, 96, 0, 96, 0, 253, 91, 97, 1, 160, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 64, 81, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 224, 81, 97, 1, 192, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 64, 82, 97, 2, 96, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 64, 128, 81, 97, 2, 128, 82, 128, 96, 32, 1, 81, 97, 2, 160, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 192, 82, 97, 2, 160, 81, 97, 2, 224, 82, 97, 1, 128, 81, 97, 3, 0, 82, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 3, 96, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 96, 81, 96, 0, 82, 96, 0, 81, 97, 1, 160, 81, 86, 91, 97, 1, 224, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 64, 81, 24, 97, 75, 66, 87, 96, 0, 128, 253, 91, 96, 0, 97, 1, 96, 81, 18, 21, 97, 75, 83, 87, 96, 0, 128, 253, 91, 96, 2, 97, 1, 96, 81, 18, 97, 75, 99, 87, 96, 0, 128, 253, 91, 96, 0, 97, 1, 64, 81, 18, 21, 97, 75, 116, 87, 96, 0, 128, 253, 91, 96, 2, 97, 1, 64, 81, 18, 97, 75, 132, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 32, 81, 97, 2, 0, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 1, 160, 81, 97, 2, 64, 82, 97, 1, 192, 81, 97, 2, 96, 82, 97, 2, 0, 81, 97, 2, 128, 82, 97, 2, 128, 81, 97, 2, 96, 81, 97, 2, 64, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 2, 224, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 224, 81, 97, 2, 32, 82, 97, 2, 0, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 76, 100, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 82, 97, 2, 32, 81, 97, 2, 96, 82, 96, 96, 54, 97, 2, 128, 55, 97, 2, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 64, 81, 97, 2, 224, 81, 20, 21, 97, 76, 165, 87, 97, 1, 128, 81, 97, 2, 160, 82, 97, 76, 218, 86, 91, 97, 1, 96, 81, 97, 2, 224, 81, 24, 21, 97, 76, 213, 87, 97, 1, 160, 97, 2, 224, 81, 96, 2, 129, 16, 97, 76, 199, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 82, 97, 76, 218, 86, 91, 97, 77, 86, 86, 91, 97, 2, 128, 128, 81, 97, 2, 160, 81, 129, 129, 131, 1, 16, 21, 97, 76, 242, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 2, 96, 81, 97, 2, 32, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 24, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 160, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 57, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 77, 75, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 76, 138, 87, 91, 80, 80, 97, 2, 96, 81, 97, 2, 32, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 132, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 161, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 194, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 77, 212, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 97, 2, 128, 81, 97, 2, 32, 81, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 252, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 128, 128, 97, 78, 18, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 78, 39, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 224, 82, 97, 2, 32, 81, 97, 3, 0, 82, 97, 3, 32, 96, 0, 96, 255, 129, 131, 82, 1, 91, 97, 3, 0, 81, 97, 2, 192, 82, 97, 3, 0, 81, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 78, 106, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 96, 81, 129, 129, 131, 1, 16, 21, 97, 78, 132, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 2, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 78, 165, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 224, 81, 129, 129, 131, 1, 16, 21, 97, 78, 191, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 32, 81, 128, 130, 16, 21, 97, 78, 215, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 78, 233, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 0, 82, 97, 2, 192, 81, 97, 3, 0, 81, 17, 21, 97, 79, 61, 87, 96, 1, 97, 3, 0, 81, 97, 2, 192, 81, 128, 130, 16, 21, 97, 79, 24, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 79, 56, 87, 97, 3, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 97, 79, 116, 86, 91, 96, 1, 97, 2, 192, 81, 97, 3, 0, 81, 128, 130, 16, 21, 97, 79, 84, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 79, 116, 87, 97, 3, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 78, 70, 87, 91, 80, 80, 96, 0, 96, 0, 253, 91, 97, 1, 224, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 192, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 2, 0, 82, 96, 32, 97, 2, 160, 96, 4, 99, 187, 123, 139, 128, 97, 2, 64, 82, 97, 2, 92, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 79, 233, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 79, 246, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 160, 81, 97, 2, 32, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 1, 160, 81, 97, 2, 128, 82, 97, 1, 192, 81, 97, 2, 160, 82, 97, 2, 160, 81, 97, 2, 128, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 3, 0, 82, 97, 3, 32, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 0, 128, 81, 97, 2, 64, 82, 128, 96, 32, 1, 81, 97, 2, 96, 82, 80, 97, 2, 64, 97, 1, 64, 81, 96, 2, 129, 16, 97, 80, 161, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 128, 81, 97, 2, 0, 97, 1, 64, 81, 96, 2, 129, 16, 97, 80, 190, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 80, 215, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 80, 253, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 128, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 1, 64, 81, 97, 2, 192, 82, 97, 1, 96, 81, 97, 2, 224, 82, 97, 2, 128, 81, 97, 3, 0, 82, 97, 2, 64, 81, 97, 3, 32, 82, 97, 2, 96, 81, 97, 3, 64, 82, 97, 3, 64, 81, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 3, 160, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 160, 81, 97, 2, 160, 82, 97, 2, 64, 97, 1, 96, 81, 96, 2, 129, 16, 97, 81, 205, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 81, 128, 130, 16, 21, 97, 81, 227, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 81, 249, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 192, 82, 96, 2, 84, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 82, 31, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 97, 2, 192, 81, 97, 2, 224, 81, 128, 130, 16, 21, 97, 82, 76, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 82, 112, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 97, 1, 96, 81, 96, 2, 129, 16, 97, 82, 139, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 82, 155, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 0, 81, 97, 1, 224, 81, 86, 91, 97, 1, 224, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 192, 82, 96, 0, 97, 1, 96, 81, 18, 21, 97, 82, 214, 87, 96, 0, 128, 253, 91, 96, 2, 97, 1, 96, 81, 18, 97, 82, 230, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 83, 0, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 82, 97, 1, 192, 81, 97, 2, 32, 82, 96, 96, 54, 97, 2, 64, 55, 97, 2, 160, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 96, 81, 97, 2, 160, 81, 24, 21, 97, 83, 86, 87, 97, 1, 128, 97, 2, 160, 81, 96, 2, 129, 16, 97, 83, 72, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 82, 97, 83, 91, 86, 91, 97, 83, 215, 86, 91, 97, 2, 64, 128, 81, 97, 2, 96, 81, 129, 129, 131, 1, 16, 21, 97, 83, 115, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 2, 32, 81, 97, 1, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 83, 153, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 96, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 83, 186, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 83, 204, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 32, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 83, 38, 87, 91, 80, 80, 97, 2, 32, 81, 97, 1, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 5, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 34, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 67, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 84, 85, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 32, 82, 97, 2, 64, 81, 97, 1, 192, 81, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 125, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 128, 128, 97, 84, 147, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 84, 168, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 160, 82, 97, 1, 192, 81, 97, 2, 192, 82, 97, 2, 224, 96, 0, 96, 255, 129, 131, 82, 1, 91, 97, 2, 192, 81, 97, 2, 128, 82, 97, 2, 192, 81, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 235, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 32, 81, 129, 129, 131, 1, 16, 21, 97, 85, 5, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 2, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 85, 38, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 160, 81, 129, 129, 131, 1, 16, 21, 97, 85, 64, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 1, 192, 81, 128, 130, 16, 21, 97, 85, 88, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 85, 106, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 97, 2, 128, 81, 97, 2, 192, 81, 17, 21, 97, 85, 190, 87, 96, 1, 97, 2, 192, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 85, 153, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 85, 185, 87, 97, 2, 192, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 97, 85, 245, 86, 91, 96, 1, 97, 2, 128, 81, 97, 2, 192, 81, 128, 130, 16, 21, 97, 85, 213, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 85, 245, 87, 97, 2, 192, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 84, 199, 87, 91, 80, 80, 96, 0, 96, 0, 253, 91, 97, 1, 192, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 0, 81, 97, 1, 224, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 1, 128, 81, 97, 2, 64, 82, 97, 1, 160, 81, 97, 2, 96, 82, 97, 2, 96, 81, 97, 2, 64, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 192, 82, 97, 2, 224, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 192, 128, 81, 97, 2, 0, 82, 128, 96, 32, 1, 81, 97, 2, 32, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 32, 81, 97, 2, 128, 82, 97, 1, 224, 81, 97, 2, 160, 82, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 96, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 3, 0, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 0, 81, 97, 2, 64, 82, 96, 23, 84, 97, 2, 96, 82, 97, 2, 64, 81, 97, 1, 64, 81, 97, 2, 64, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 87, 138, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 96, 81, 128, 128, 97, 87, 160, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 87, 179, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 1, 224, 81, 97, 2, 192, 82, 97, 1, 96, 81, 97, 2, 224, 82, 97, 2, 0, 81, 97, 3, 0, 82, 97, 2, 32, 81, 97, 3, 32, 82, 97, 2, 128, 81, 97, 3, 64, 82, 97, 3, 64, 81, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 82, 173, 86, 91, 97, 3, 160, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 160, 81, 97, 2, 160, 82, 96, 2, 84, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 88, 136, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 4, 128, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 2, 224, 82, 96, 32, 97, 3, 128, 96, 4, 99, 187, 123, 139, 128, 97, 3, 32, 82, 97, 3, 60, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 88, 225, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 88, 238, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 128, 81, 97, 3, 0, 82, 97, 2, 0, 81, 97, 3, 32, 82, 97, 2, 32, 81, 97, 3, 64, 82, 97, 2, 0, 97, 1, 96, 81, 96, 2, 129, 16, 97, 89, 29, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 81, 128, 130, 16, 21, 97, 89, 51, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 89, 87, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 224, 97, 1, 96, 81, 96, 2, 129, 16, 97, 89, 114, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 89, 130, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 96, 82, 97, 3, 128, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 0, 97, 3, 160, 82, 97, 1, 96, 81, 97, 3, 128, 81, 20, 21, 97, 90, 26, 87, 97, 2, 0, 97, 3, 128, 81, 96, 2, 129, 16, 97, 89, 192, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 89, 221, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 128, 128, 97, 89, 243, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 160, 81, 128, 130, 16, 21, 97, 90, 10, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 160, 82, 97, 90, 153, 86, 91, 97, 2, 0, 97, 3, 128, 81, 96, 2, 129, 16, 97, 90, 46, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 0, 97, 3, 128, 81, 96, 2, 129, 16, 97, 90, 71, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 90, 100, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 128, 128, 97, 90, 122, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 90, 141, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 160, 82, 91, 97, 3, 32, 97, 3, 128, 81, 96, 2, 129, 16, 97, 90, 173, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 97, 2, 192, 81, 97, 3, 160, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 90, 207, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 90, 240, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 89, 152, 87, 91, 80, 80, 97, 3, 32, 97, 1, 96, 81, 96, 2, 129, 16, 97, 91, 33, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 64, 97, 3, 160, 82, 91, 97, 3, 160, 81, 81, 96, 32, 97, 3, 160, 81, 1, 97, 3, 160, 82, 97, 3, 160, 97, 3, 160, 81, 16, 21, 97, 91, 80, 87, 97, 91, 46, 86, 91, 97, 1, 224, 81, 97, 3, 192, 82, 97, 1, 96, 81, 97, 3, 224, 82, 97, 3, 32, 81, 97, 4, 0, 82, 97, 3, 64, 81, 97, 4, 32, 82, 97, 2, 128, 81, 97, 4, 64, 82, 97, 4, 64, 81, 97, 4, 32, 81, 97, 4, 0, 81, 97, 3, 224, 81, 97, 3, 192, 81, 96, 6, 88, 1, 97, 82, 173, 86, 91, 97, 4, 160, 82, 97, 3, 128, 97, 3, 160, 82, 91, 97, 3, 160, 81, 82, 96, 32, 97, 3, 160, 81, 3, 97, 3, 160, 82, 97, 1, 64, 97, 3, 160, 81, 16, 21, 21, 97, 91, 196, 87, 97, 91, 161, 86, 91, 97, 4, 160, 81, 128, 130, 16, 21, 97, 91, 213, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 128, 82, 97, 3, 128, 81, 96, 1, 128, 130, 16, 21, 97, 91, 243, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 92, 23, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 224, 97, 1, 96, 81, 96, 2, 129, 16, 97, 92, 50, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 92, 66, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 128, 82, 97, 3, 224, 97, 3, 128, 81, 129, 82, 97, 3, 96, 81, 97, 3, 128, 81, 128, 130, 16, 21, 97, 92, 106, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 96, 32, 1, 82, 80, 96, 64, 97, 4, 32, 82, 91, 96, 0, 97, 4, 32, 81, 17, 21, 21, 97, 92, 144, 87, 97, 92, 172, 86, 91, 96, 32, 97, 4, 32, 81, 3, 97, 3, 224, 1, 81, 96, 32, 97, 4, 32, 81, 3, 97, 4, 32, 82, 97, 92, 126, 86, 91, 97, 1, 192, 81, 86, +} diff --git a/go/interpreter/sfvm/gas.go b/go/interpreter/sfvm/gas.go new file mode 100644 index 00000000..aa903ba1 --- /dev/null +++ b/go/interpreter/sfvm/gas.go @@ -0,0 +1,298 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "github.com/0xsoniclabs/tosca/go/tosca" +) + +const ( + CallNewAccountGas tosca.Gas = 25000 // Paid for CALL when the destination address didn't exist prior. + CallValueTransferGas tosca.Gas = 9000 // Paid for CALL when the value transfer is non-zero. + CallStipend tosca.Gas = 2300 // Free gas given at beginning of call. + + ColdSloadCostEIP2929 tosca.Gas = 2100 // Cost of cold SLOAD after EIP 2929 + ColdAccountAccessCostEIP2929 tosca.Gas = 2600 // Cost of cold account access after EIP 2929 + + SloadGasEIP2200 tosca.Gas = 800 // Cost of SLOAD after EIP 2200 (part of Istanbul) + SstoreClearsScheduleRefundEIP2200 tosca.Gas = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + + SstoreResetGasEIP2200 tosca.Gas = 5000 // Once per SSTORE operation from clean non-zero to something else + SstoreSetGasEIP2200 tosca.Gas = 20000 // Once per SSTORE operation from clean zero to non-zero + WarmStorageReadCostEIP2929 tosca.Gas = 100 // Cost of reading warm storage after EIP 2929 + + UNKNOWN_GAS_PRICE = 999999 +) + +var static_gas_prices = newOpCodePropertyMap(getStaticGasPriceInternal) +var static_gas_prices_berlin = newOpCodePropertyMap(getBerlinGasPriceInternal) + +func getBerlinGasPriceInternal(op OpCode) tosca.Gas { + gp := getStaticGasPriceInternal(op) + + // Changed static gas prices with EIP2929 + switch op { + case SLOAD: + gp = 0 + case EXTCODECOPY: + gp = 0 + case EXTCODESIZE: + gp = 0 + case EXTCODEHASH: + gp = 0 + case BALANCE: + gp = 0 + case CALL: + gp = 0 + case CALLCODE: + gp = 0 + case STATICCALL: + gp = 0 + case DELEGATECALL: + gp = 0 + } + return gp +} + +func getStaticGasPrices(revision tosca.Revision) *opCodePropertyMap[tosca.Gas] { + if revision >= tosca.R09_Berlin { + return &static_gas_prices_berlin + } + return &static_gas_prices +} + +func getStaticGasPriceInternal(op OpCode) tosca.Gas { + if PUSH1 <= op && op <= PUSH32 { + return 3 + } + if DUP1 <= op && op <= DUP16 { + return 3 + } + if SWAP1 <= op && op <= SWAP16 { + return 3 + } + // this range covers: LT, GT, SLT, SGT, EQ, ISZERO, + // AND, OR, XOR, NOT, BYTE, SHL, SHR, SAR + if LT <= op && op <= SAR { + return 3 + } + // this range covers: COINBASE, TIMESTAMP, NUMBER, + // DIFFICULTY/PREVRANDO, GAS, GASLIMIT, CHAINID + if COINBASE <= op && op <= CHAINID { + return 2 + } + switch op { + case CLZ: + return 5 + case POP: + return 2 + case PUSH0: + return 2 + case ADD: + return 3 + case SUB: + return 3 + case MUL: + return 5 + case DIV: + return 5 + case SDIV: + return 5 + case MOD: + return 5 + case SMOD: + return 5 + case ADDMOD: + return 8 + case MULMOD: + return 8 + case EXP: + return 10 + case SIGNEXTEND: + return 5 + case SHA3: + return 30 + case ADDRESS: + return 2 + case BALANCE: + return 700 // Should be 100 for warm access, 2600 for cold access + case ORIGIN: + return 2 + case CALLER: + return 2 + case CALLVALUE: + return 2 + case CALLDATALOAD: + return 3 + case CALLDATASIZE: + return 2 + case CALLDATACOPY: + return 3 + case CODESIZE: + return 2 + case CODECOPY: + return 3 + case GASPRICE: + return 2 + case EXTCODESIZE: + return 700 // This seems to be different than documented on evm.codes (it should be 100) + case EXTCODECOPY: + return 700 // From EIP150 it is 700, was 20 + case RETURNDATASIZE: + return 2 + case RETURNDATACOPY: + return 3 + case EXTCODEHASH: + return 700 // Should be 100 for warm access, 2600 for cold access + case BLOCKHASH: + return 20 + case SELFBALANCE: + return 5 + case BASEFEE: + return 2 + case BLOBHASH: + return 3 + case BLOBBASEFEE: + return 2 + case MLOAD: + return 3 + case MSTORE: + return 3 + case MSTORE8: + return 3 + case SLOAD: + return 800 // This is supposed to be 100 for warm and 2100 for cold accesses + case SSTORE: + return 0 // Costs are handled in gasSStore(..) function below + case JUMP: + return 8 + case JUMPI: + return 10 + case JUMPDEST: + return 1 + case JUMP_TO: + return 0 + case TLOAD: + return 100 + case TSTORE: + return 100 + case PC: + return 2 + case MSIZE: + return 2 + case MCOPY: + return 3 + case GAS: + return 2 + case LOG0: + return 375 + case LOG1: + return 750 + case LOG2: + return 1125 + case LOG3: + return 1500 + case LOG4: + return 1875 + case CREATE: + return 32000 + case CREATE2: + return 32000 + case CALL: + return 700 + case CALLCODE: + return 700 + case STATICCALL: + return 700 + case RETURN: + return 0 + case STOP: + return 0 + case REVERT: + return 0 + case INVALID: + return 0 + case DELEGATECALL: + return 700 + case SELFDESTRUCT: + return 5000 + } + + if op.isSuperInstruction() { + var sum tosca.Gas + for _, subOp := range op.decompose() { + sum += getStaticGasPriceInternal(subOp) + } + return sum + } + + return UNKNOWN_GAS_PRICE +} + +func getDynamicCostsForSstore( + revision tosca.Revision, + storageStatus tosca.StorageStatus, +) tosca.Gas { + switch storageStatus { + case tosca.StorageAdded: + return 20000 + case tosca.StorageModified, + tosca.StorageDeleted: + if revision >= tosca.R09_Berlin { + return 2900 + } else { + return 5000 + } + default: + if revision >= tosca.R09_Berlin { + return 100 + } + return 800 + } +} + +func getRefundForSstore( + revision tosca.Revision, + storageStatus tosca.StorageStatus, +) tosca.Gas { + switch storageStatus { + case tosca.StorageDeleted, + tosca.StorageModifiedDeleted: + if revision >= tosca.R10_London { + return 4800 + } + return 15000 + case tosca.StorageDeletedAdded: + if revision >= tosca.R10_London { + return -4800 + } + return -15000 + case tosca.StorageDeletedRestored: + if revision >= tosca.R10_London { + return -4800 + 5000 - 2100 - 100 + } else if revision >= tosca.R09_Berlin { + return -15000 + 5000 - 2100 - 100 + } + return -15000 + 4200 + case tosca.StorageAddedDeleted: + if revision >= tosca.R09_Berlin { + return 19900 + } + return 19200 + case tosca.StorageModifiedRestored: + if revision >= tosca.R09_Berlin { + return 5000 - 2100 - 100 + } + return 4200 + default: + return 0 + } +} diff --git a/go/interpreter/sfvm/gas_test.go b/go/interpreter/sfvm/gas_test.go new file mode 100644 index 00000000..1b28aaaf --- /dev/null +++ b/go/interpreter/sfvm/gas_test.go @@ -0,0 +1,189 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" +) + +// --- SStore --- + +func TestGas_getDynamicCostsForSstore_exhaustive(t *testing.T) { + // This test exhaustively checks the computation of the dynamic gas costs for + // the SSTORE instruction by enumerating every possible input combination + // and comparing the result with the specification found on + // https://www.evm.codes. + + // The specification found on https://www.evm.codes is provided in the form + // of a python code snippet. The following function is a direct translation + // of the python code to Go, parameterized to allow for easy testing of + // different revisions. + makeSpec := func(create, update, touch tosca.Gas) func(s storageStateExample) tosca.Gas { + return func(s storageStateExample) tosca.Gas { + // source: https://www.evm.codes + gas := tosca.Gas(0) + if s.value == s.current { + gas = touch + } else if s.current == s.original { + if s.original == 0 { + gas = create + } else { + gas = update + } + } else { + gas = touch + } + return gas + } + } + + specs := map[tosca.Revision]func(storageStateExample) tosca.Gas{ + // source: https://www.evm.codes/?fork=istanbul + tosca.R07_Istanbul: makeSpec(20000, 5000, 800), + // source: https://www.evm.codes/?fork=berlin + tosca.R09_Berlin: makeSpec(20000, 2900, 100), + } + + // All other revisions inherit the definition from their predecessor. + specs[tosca.R10_London] = specs[tosca.R09_Berlin] + specs[tosca.R11_Paris] = specs[tosca.R10_London] + specs[tosca.R12_Shanghai] = specs[tosca.R11_Paris] + specs[tosca.R13_Cancun] = specs[tosca.R12_Shanghai] + specs[tosca.R14_Prague] = specs[tosca.R13_Cancun] + specs[tosca.R15_Osaka] = specs[tosca.R14_Prague] + + // Check that gas prices are computed correctly. + for _, revision := range tosca.GetAllKnownRevisions() { + spec, found := specs[revision] + if !found { + t.Errorf("missing specification for revision %v", revision) + continue + } + for storageStatus, example := range getStorageStateExamples() { + want := spec(example) + got := getDynamicCostsForSstore(revision, storageStatus) + if got != want { + t.Errorf( + "unexpected result for (%v,%v), wanted %d, got %d", + revision, + storageStatus, + want, + got, + ) + } + } + } +} + +func TestGas_getRefundForSstore_exhaustive(t *testing.T) { + // This test exhaustively checks the computation of the refunds granted by + // the SSTORE instruction by enumerating every possible input combination + // and comparing the result with the specification found on + // https://www.evm.codes. + + // The specification found on https://www.evm.codes is provided in the form + // of a python code snippet. The following function is a direct translation + // of the python code to Go, parameterized to allow for easy testing of + // different revisions. + makeSpec := func(delete, resetToZero, resetToNonZero tosca.Gas) func(s storageStateExample) tosca.Gas { + return func(s storageStateExample) tosca.Gas { + // source: https://www.evm.codes + refund := tosca.Gas(0) + if s.value != s.current { + if s.current == s.original { + if s.original != 0 && s.value == 0 { + refund += delete + } + } else { + if s.original != 0 { + if s.current == 0 { + refund -= delete + } else if s.value == 0 { + refund += delete + } + } + if s.value == s.original { + if s.original == 0 { + refund += resetToZero + } else { + refund += resetToNonZero + } + } + } + } + return refund + } + } + + specs := map[tosca.Revision]func(storageStateExample) tosca.Gas{ + // source: https://www.evm.codes/?fork=istanbul + tosca.R07_Istanbul: makeSpec(15000, 19200, 4200), + // source: https://www.evm.codes/?fork=berlin + tosca.R09_Berlin: makeSpec(15000, 20000-100, 5000-2100-100), + // source: https://www.evm.codes/?fork=london + tosca.R10_London: makeSpec(4800, 20000-100, 5000-2100-100), + } + // All other revisions inherit the definition from their predecessor. + specs[tosca.R11_Paris] = specs[tosca.R10_London] + specs[tosca.R12_Shanghai] = specs[tosca.R11_Paris] + specs[tosca.R13_Cancun] = specs[tosca.R12_Shanghai] + specs[tosca.R14_Prague] = specs[tosca.R13_Cancun] + specs[tosca.R15_Osaka] = specs[tosca.R14_Prague] + + // Check that gas prices are computed correctly. + for _, revision := range tosca.GetAllKnownRevisions() { + spec, found := specs[revision] + if !found { + t.Errorf("missing specification for revision %v", revision) + continue + } + for storageStatus, example := range getStorageStateExamples() { + want := spec(example) + got := getRefundForSstore(revision, storageStatus) + if got != want { + t.Errorf( + "unexpected result for (%v,%v), wanted %d, got %d", + revision, + storageStatus, + want, + got, + ) + } + } + } +} + +// getStorageStateExamples returns a map enumerating all possible storage state +// and mapping them to a tipple if original, current and new values of a storage +// slot that constitutes the associated storage state. +// +// This function is intended for testing storage related gas costs and refunds +// functions by providing a complete set of test-case inputs. +func getStorageStateExamples() map[tosca.StorageStatus]storageStateExample { + X, Y, Z := 1, 2, 3 + return map[tosca.StorageStatus]storageStateExample{ + tosca.StorageAssigned: {X, Y, Z}, + tosca.StorageAdded: {0, 0, Z}, + tosca.StorageDeleted: {X, X, 0}, + tosca.StorageModified: {X, X, Z}, + tosca.StorageDeletedAdded: {X, 0, Z}, + tosca.StorageModifiedDeleted: {X, Y, 0}, + tosca.StorageDeletedRestored: {X, 0, X}, + tosca.StorageAddedDeleted: {0, X, 0}, + tosca.StorageModifiedRestored: {X, Y, X}, + } +} + +type storageStateExample struct { + original, current, value int +} diff --git a/go/interpreter/sfvm/hash_cache.go b/go/interpreter/sfvm/hash_cache.go new file mode 100644 index 00000000..33686ba4 --- /dev/null +++ b/go/interpreter/sfvm/hash_cache.go @@ -0,0 +1,163 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "sync" + + "github.com/0xsoniclabs/tosca/go/tosca" +) + +// sha3HashCache is an LRU governed fixed-capacity cache for SHA3 hashes. +// The cache maintains hashes for hashed input data of size 32 and 64, +// which are the vast majority of values hashed when running EVM +// instructions. Inputs of other sizes are hashed on demand without caching. +type sha3HashCache struct { + cache32 *hashCache[[32]byte] + cache64 *hashCache[[64]byte] +} + +// newSha3HashCache creates a Sha3HashCache with the given capacity of entries. +func newSha3HashCache(capacity32 int, capacity64 int) *sha3HashCache { + return &sha3HashCache{ + cache32: newHashCache(capacity32, func(key [32]byte) tosca.Hash { + return Keccak256For32byte(key) + }), + cache64: newHashCache(capacity64, func(key [64]byte) tosca.Hash { + return Keccak256(key[:]) + }), + } +} + +// hash fetches a cached hash or computes the hash for the provided data. +func (h *sha3HashCache) hash(data []byte) tosca.Hash { + if len(data) == 32 { + var key [32]byte + copy(key[:], data) + return h.cache32.getHash(key) + } + if len(data) == 64 { + var key [64]byte + copy(key[:], data) + return h.cache64.getHash(key) + } + return Keccak256(data) +} + +// hashCache is an LRU governed fixed-capacity cache for hashes of values of +// type K. The cache is thread-safe. +type hashCache[K comparable] struct { + hash func(K) tosca.Hash // Hash function for the keys. + entries []hashCacheEntry[K] // Fixed capacity cache entries. + index map[K]*hashCacheEntry[K] // Index of cache entries by key. + head, tail *hashCacheEntry[K] // LRU order. + nextFree int // Index of the next free entry. + lock sync.Mutex // Lock for the cache. +} + +// newHashCache creates a hashCache with the given capacity of entries. For +// efficiency reasons, the capacity must be at least 2. If it is less than 2, +// the capacity is set to 2. +func newHashCache[K comparable](capacity int, hash func(K) tosca.Hash) *hashCache[K] { + if capacity < 2 { + capacity = 2 + } + res := &hashCache[K]{ + entries: make([]hashCacheEntry[K], capacity), + index: make(map[K]*hashCacheEntry[K], capacity), + hash: hash, + } + + // To avoid the need for handling the special case of an empty cache + // in every lookup operation we initialize the cache with one value. + // Since values are never removed, just evicted to make space for another, + // the cache will never be empty. + + // Insert first element (zero value). + res.head = res.getFree() + res.tail = res.head + var key K + res.head.hash = hash(key) + res.index[key] = res.head + return res +} + +func (h *hashCache[K]) getHash(key K) tosca.Hash { + h.lock.Lock() + if entry, found := h.index[key]; found { + // Move entry to the front. + if entry != h.head { + // Remove from current place. + entry.pred.succ = entry.succ + if entry.succ != nil { + entry.succ.pred = entry.pred + } else { + h.tail = entry.pred + } + // Add to front + entry.pred = nil + entry.succ = h.head + h.head.pred = entry + h.head = entry + } + h.lock.Unlock() + return entry.hash + } + + // Compute the hash without holding the lock. + h.lock.Unlock() + hash := h.hash(key) + h.lock.Lock() + + // We need to check that the key has not be added concurrently. + if _, found := h.index[key]; found { + // If it was added concurrently, we are done. + h.lock.Unlock() + return hash + } + + // The key is still not present, so we add it. + entry := h.getFree() + entry.key = key + entry.hash = hash + entry.pred = nil + entry.succ = h.head + h.head.pred = entry + h.head = entry + h.index[key] = entry + h.lock.Unlock() + return hash +} + +func (h *hashCache[K]) getFree() *hashCacheEntry[K] { + // If there are still free entries, use one of those. + if h.nextFree < len(h.entries) { + res := &h.entries[h.nextFree] + h.nextFree++ + return res + } + // Use the tail. + res := h.tail + h.tail = h.tail.pred + h.tail.succ = nil + delete(h.index, res.key) + return res +} + +// hashCacheEntry is an entry of a cache for hashes of values of type K. +type hashCacheEntry[K any] struct { + // key is the input value cache entries are indexed by. + key K + // hash is the cached (Sha3) hash of the key. + hash tosca.Hash + // pred/succ pointers are used for a double linked list for the LRU order. + pred, succ *hashCacheEntry[K] +} diff --git a/go/interpreter/sfvm/hash_cache_test.go b/go/interpreter/sfvm/hash_cache_test.go new file mode 100644 index 00000000..63a5d454 --- /dev/null +++ b/go/interpreter/sfvm/hash_cache_test.go @@ -0,0 +1,334 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "math/rand" + "slices" + "sync" + "testing" + "time" + + "github.com/0xsoniclabs/tosca/go/tosca" +) + +func TestSha3HashCache_hash_ProducesCorrectHashesForInputs(t *testing.T) { + inputs := [][]byte{ + {}, + {0}, + {1, 2, 3, 4, 5}, + make([]byte, 32), + make([]byte, 64), + make([]byte, 123), + } + + r := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < 100; i++ { + input := make([]byte, r.Intn(150)) + r.Read(input) + inputs = append(inputs, input) + } + + cache := newSha3HashCache(10, 10) + for _, input := range inputs { + want := Keccak256(input) + got := cache.hash(input) + if want != got { + t.Errorf("expected hash to be %x, but got %x", want, got) + } + } +} + +func TestHashCache_UsesProvidedHashingFunction(t *testing.T) { + hash := func(i int) tosca.Hash { + return tosca.Hash{byte(i)} + } + + cache := newHashCache(10, hash) + for i := 0; i < 100; i++ { + want := hash(i) + got := cache.getHash(i) + if want != got { + t.Errorf("expected hash to be %x, but got %x", want, got) + } + } +} + +func TestHashCache_HashesAreCached(t *testing.T) { + // To test whether results are cached, we use a hash function that + // returns different results at each call. + counter := 0 + hash := func(int) tosca.Hash { + counter++ + return tosca.Hash{byte(counter)} + } + + cache := newHashCache(10, hash) + + hash1 := cache.getHash(1) + hash2 := cache.getHash(2) + + if hash1 == hash2 { + t.Fatalf("expected different hashes for different inputs") + } + + if want, got := hash1, cache.getHash(1); want != got { + t.Errorf("expected hash to be %v, but got %v", want, got) + } + if want, got := hash2, cache.getHash(2); want != got { + t.Errorf("expected hash to be %v, but got %v", want, got) + } +} + +func TestHashCache_CapacityIsIncreasedToAtLeast2(t *testing.T) { + capacity := []int{-1000, -1, 0, 1, 2, 3, 1000} + for _, c := range capacity { + cache := newHashCache(c, func(int) tosca.Hash { + return tosca.Hash{} + }) + want := c + if want < 2 { + want = 2 + } + if got := len(cache.entries); got != want { + t.Errorf("expected cache to have %d entries, but got %d", want, got) + } + } +} + +func TestHashCache_RespectsCapacity(t *testing.T) { + hash := func(int) tosca.Hash { + return tosca.Hash{} + } + for _, capacity := range []int{2, 10, 42, 123} { + t.Run(fmt.Sprintf("capacity=%d", capacity), func(t *testing.T) { + cache := newHashCache(capacity, hash) + for i := 0; i < 2*capacity; i++ { + cache.getHash(i) + + if got := len(cache.entries); got != capacity { + t.Errorf("expected cache to have %d entries, but got %d", capacity, got) + } + + want := i + 1 + if want > capacity { + want = capacity + } + if got := len(cache.index); got != want { + t.Errorf("expected cache to have %d entries in the index, but got %d", want, got) + } + } + }) + } +} + +func TestHashCache_UsesLruReplacementOrder(t *testing.T) { + sequence := []struct { + touchedKey int + lruOrder []int + }{ + {0, []int{0}}, // < not really needed, just here for clarity + // New keys are added to the front, and the last key is removed when the + // capacity is reached. + {1, []int{1, 0}}, + {2, []int{2, 1, 0}}, + {3, []int{3, 2, 1}}, + {4, []int{4, 3, 2}}, + // Accessing an existing key moves it to the front. + {2, []int{2, 4, 3}}, // < moves last element to the front + {4, []int{4, 2, 3}}, // < moves an element in the middle to the front + {4, []int{4, 2, 3}}, // < touches the front element + } + + cache := newHashCache(3, func(int) tosca.Hash { + return tosca.Hash{} + }) + + // The cache is initiated with the zero value. + got := checkIndexAndGetLruOrder(t, cache) + if want := []int{0}; !slices.Equal(want, got) { + t.Errorf("expected initial order to be %v, but got %v", want, got) + } + + for i, step := range sequence { + cache.getHash(step.touchedKey) + got := checkIndexAndGetLruOrder(t, cache) + if want := step.lruOrder; !slices.Equal(want, got) { + t.Errorf( + "after step %d expected order to be %v, but got %v", + i, want, got, + ) + } + } +} + +func TestHashCache_ReusingKeysBeforeReachingTheCapacityLimitDoesNotLeadToDuplicates(t *testing.T) { + sequence := []struct { + touchedKey int + lruOrder []int + }{ + {1, []int{1, 0}}, + {0, []int{0, 1}}, + {1, []int{1, 0}}, + {0, []int{0, 1}}, + {1, []int{1, 0}}, + } + + cache := newHashCache(3, func(int) tosca.Hash { + return tosca.Hash{} + }) + + for i, step := range sequence { + cache.getHash(step.touchedKey) + got := checkIndexAndGetLruOrder(t, cache) + if want := step.lruOrder; !slices.Equal(want, got) { + t.Errorf( + "after step %d expected order to be %v, but got %v", + i, want, got, + ) + } + } +} + +func checkIndexAndGetLruOrder(t *testing.T, h *hashCache[int]) []int { + t.Helper() + + getForwardOrder := func(h *hashCache[int]) []int { + var res []int + for e := h.head; e != nil; e = e.succ { + res = append(res, e.key) + } + return res + } + + getBackwardOrder := func(h *hashCache[int]) []int { + var res []int + for e := h.tail; e != nil; e = e.pred { + res = append(res, e.key) + } + slices.Reverse(res) + return res + } + + forward := getForwardOrder(h) + backward := getBackwardOrder(h) + + // Check that the double-linked list is consistent. + if !slices.Equal(forward, backward) { + t.Errorf("expected forward and backward order to be identical but got %v and %v", forward, backward) + } + + // Check that there are no duplicates in the keys. + seen := make(map[int]struct{}) + for _, k := range forward { + if _, found := seen[k]; found { + t.Errorf("expected key %d to be unique, but it is duplicated", k) + } + seen[k] = struct{}{} + } + + // Check that the index has the same size as the keys. + if want, got := len(forward), len(h.index); want != got { + t.Errorf("expected index to have %d entries, but got %d", want, got) + } + + // Check that the keys are in the index. + for _, k := range forward { + if _, found := h.index[k]; !found { + t.Errorf("expected key %d to be in the index, but it is not", k) + } + } + return forward +} + +func TestHashCache_AccessesAreThreadSafe(t *testing.T) { + // This test is designed to detect race conditions in cases in combination + // with Go's data race detection. It should be run with the -race flag. + cache := newHashCache(10, func(int) tosca.Hash { + return tosca.Hash{} + }) + + const ( + threads = 10 + accesses = 1000 + ) + + var wg sync.WaitGroup + wg.Add(threads) + for i := 0; i < threads; i++ { + go func() { + defer wg.Done() + for j := 0; j < accesses; j++ { + cache.getHash(j % 10) + } + }() + } + wg.Wait() + + // Check that the cache is consistent. + checkIndexAndGetLruOrder(t, cache) +} + +func TestHashCache_ConcurrentThreadsCanNotIntroduceDuplicates(t *testing.T) { + const key = 12 + var barrier sync.WaitGroup + barrier.Add(2) + cache := newHashCache(10, func(i int) tosca.Hash { + if i != key { + return tosca.Hash{} + } + // Wait for two go-routines to compute the hash at the same time. + barrier.Done() + barrier.Wait() + return tosca.Hash{} + }) + + var wg sync.WaitGroup + wg.Add(2) + for i := 0; i < 2; i++ { + go func() { + defer wg.Done() + cache.getHash(key) + }() + } + wg.Wait() + + // Check that the cache is consistent. + checkIndexAndGetLruOrder(t, cache) +} + +func benchmarkSha3HashCache(b *testing.B, inputSize int, mutateInput bool) { + input := make([]byte, inputSize) + cache := newSha3HashCache(128, 128) + for i := 0; i < b.N; i++ { + if mutateInput { + input[0] = byte(i) + } + cache.hash(input) + } +} + +func BenchmarkSha3HashCache_Hits(b *testing.B) { + for _, size := range []int{16, 32, 64, 128} { + b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) { + benchmarkSha3HashCache(b, size, false) + }) + } +} + +func BenchmarkSha3HashCache_Miss(b *testing.B) { + for _, size := range []int{16, 32, 64, 128} { + b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) { + benchmarkSha3HashCache(b, size, true) + }) + } +} diff --git a/go/interpreter/sfvm/instruction.go b/go/interpreter/sfvm/instruction.go new file mode 100644 index 00000000..e08bdf93 --- /dev/null +++ b/go/interpreter/sfvm/instruction.go @@ -0,0 +1,42 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "fmt" +) + +// Instruction encodes an instruction for the long-form virtual machine (LFVM). +type Instruction struct { + // The op-code of this instruction. + opcode OpCode + // An argument value for this instruction. + arg uint16 +} + +// Code for the LFVM is a slice of instructions. +type Code []Instruction + +func (i Instruction) String() string { + if i.opcode.HasArgument() { + return fmt.Sprintf("%v 0x%04x", i.opcode, i.arg) + } + return i.opcode.String() +} + +func (c Code) String() string { + var buffer bytes.Buffer + for i, instruction := range c { + buffer.WriteString(fmt.Sprintf("0x%04x: %v\n", i, instruction)) + } + return buffer.String() +} diff --git a/go/interpreter/sfvm/instruction_logger.go b/go/interpreter/sfvm/instruction_logger.go new file mode 100644 index 00000000..99584440 --- /dev/null +++ b/go/interpreter/sfvm/instruction_logger.go @@ -0,0 +1,50 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "io" +) + +// loggingRunner is a runner that logs the execution of the contract code to an io.Writer. +// If the output log is nil, nothing is logged. +type loggingRunner struct { + log io.Writer +} + +// newLogger creates a new logging runner that writes to the provided +// io.Writer. +func newLogger(writer io.Writer) loggingRunner { + return loggingRunner{log: writer} +} + +func (l loggingRunner) run(c *context) (status, error) { + status := statusRunning + var err error + for status == statusRunning { + // log format: , , \n + if int(c.pc) < len(c.code) { + top := "-empty-" + if c.stack.len() > 0 { + top = c.stack.peek().ToBig().String() + } + if l.log != nil { + _, err = fmt.Fprintf(l.log, "%v, %d, %v\n", c.code[c.pc].opcode, c.gas, top) + if err != nil { + return status, err + } + } + } + status = execute(c, true) + } + return status, nil +} diff --git a/go/interpreter/sfvm/instruction_logger_test.go b/go/interpreter/sfvm/instruction_logger_test.go new file mode 100644 index 00000000..de93f238 --- /dev/null +++ b/go/interpreter/sfvm/instruction_logger_test.go @@ -0,0 +1,146 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" +) + +func TestInterpreter_Logger_ExecutesCodeAndLogs(t *testing.T) { + + tests := map[string]struct { + code []Instruction + want string + }{ + "empty": {}, + "stop": { + code: []Instruction{{STOP, 0}}, + want: "STOP, 3, -empty-\n", + }, + "multiple codes": { + code: []Instruction{{PUSH4, 0}, {DATA, 1}, {STOP, 0}}, + want: "PUSH4, 3, -empty-\nSTOP, 0, 1\n", + }, + "out of gas": { + code: []Instruction{ + {PUSH1, 0}, + {PUSH1, 64}, + {MSTORE8, 0}, + {STOP, 0}, + }, + want: "PUSH1, 3, -empty-\nPUSH1, 0, 0\n", + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + + // Get tosca.Parameters + params := tosca.Parameters{Gas: 3} + code := test.code + buffer := bytes.NewBuffer([]byte{}) + logger := newLogger(buffer) + config := config{ + runner: logger, + } + _, err := run(config, params, code) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if strings.Compare(buffer.String(), test.want) != 0 { + t.Errorf("unexpected log: want %v, got %v", test.want, buffer.String()) + } + }) + } +} + +func TestInterpreter_Logger_RunsWithoutOutput(t *testing.T) { + + // Get tosca.Parameters + params := tosca.Parameters{} + code := []Instruction{{STOP, 0}} + + // redirect stdout + oldOut := os.Stdout + rOut, wOut, _ := os.Pipe() + os.Stdout = wOut + defer func() { os.Stdout = oldOut }() + + oldErr := os.Stderr + rErr, wErr, _ := os.Pipe() + os.Stderr = wErr + defer func() { os.Stderr = oldErr }() + + logger := newLogger(nil) + config := config{ + runner: logger, + } + + _, err := run(config, params, code) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + err = wOut.Close() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + outOut, err := io.ReadAll(rOut) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + err = wErr.Close() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + outErr, err := io.ReadAll(rErr) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if len(outOut) != 0 { + t.Errorf("unexpected stdout: want \"\", got \"%v\"", outOut) + } + if len(outErr) != 0 { + t.Errorf("unexpected stderr: want \"\", got \"%v\"", outErr) + } +} + +type loggerErrorMock struct{} + +func (l loggerErrorMock) Write(p []byte) (n int, err error) { + return 0, fmt.Errorf("error") +} + +func TestInterpreter_logger_PropagatesWriterError(t *testing.T) { + + logger := newLogger(loggerErrorMock{}) + config := config{ + runner: logger, + } + // Get tosca.Parameters + params := tosca.Parameters{} + code := []Instruction{{STOP, 0}} + + _, err := run(config, params, code) + if strings.Compare(err.Error(), "error") != 0 { + t.Errorf("unexpected error: want error, got %v", err) + } +} diff --git a/go/interpreter/sfvm/instruction_statistcs_test.go b/go/interpreter/sfvm/instruction_statistcs_test.go new file mode 100644 index 00000000..7c2b180e --- /dev/null +++ b/go/interpreter/sfvm/instruction_statistcs_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "strings" + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" +) + +func TestStatisticsRunner_RunWithStatistics(t *testing.T) { + // Get tosca.Parameters + params := tosca.Parameters{ + Input: []byte{}, + Static: true, + Gas: 10, + Code: []byte{byte(STOP), 0}, + } + code := []Instruction{{STOP, 0}} + + statsRunner := &statisticRunner{ + stats: newStatistics(), + } + config := config{ + runner: statsRunner, + } + _, err := run(config, params, code) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if got := statsRunner.stats.singleCount[uint64(STOP)]; got != 1 { + t.Errorf("unexpected statistics: want 1 stop, got %v", got) + } +} + +func TestStatisticsRunner_DumpProfilePrintsExpectedOutput(t *testing.T) { + + tests := map[string]struct { + code tosca.Code + findInOutput []string + }{ + "singles": {tosca.Code{byte(vm.STOP)}, + []string{ + "Steps: 1", + "STOP : 1 (100.00%)", + }}, + "pairs": {tosca.Code{byte(vm.PUSH1), 0x01, byte(vm.STOP)}, + []string{ + "Steps: 2", + "PUSH1 : 1 (50.00%)", + "STOP : 1 (50.00%)", + "PUSH1 STOP : 1"}}, + "triples": {tosca.Code{byte(vm.PUSH1), 0x01, byte(vm.PUSH1), 0x01, byte(vm.STOP)}, + []string{ + "Steps: 3", + "PUSH1 : 2 (66.67%)", + "STOP : 1 (33.33%)", + "PUSH1 PUSH1 STOP : 1"}}, + "quads": {tosca.Code{byte(vm.PUSH1), 0x01, byte(vm.PUSH1), 0x01, byte(vm.PUSH1), 0x01, byte(vm.STOP)}, + []string{ + "Steps: 4", + "PUSH1 : 3 (75.00%)", + "STOP : 1 (25.00%)", + "PUSH1 PUSH1 PUSH1 : 1 (25.00%)", + "PUSH1 PUSH1 STOP : 1 (25.00%)", + "PUSH1 PUSH1 PUSH1 STOP : 1 (25.00%)", + }}, + } + + for name, test := range tests { + t.Run(fmt.Sprintf("%v", name), func(t *testing.T) { + statsRunner := &statisticRunner{ + stats: newStatistics(), + } + + instance, err := newVm(config{ + runner: statsRunner, + }) + if err != nil { + t.Fatalf("Failed to create VM: %v", err) + } + instance.ResetProfile() + //run code + _, err = instance.Run(tosca.Parameters{Input: []byte{}, Static: true, Gas: 10, + Code: test.code}) + if err != nil { + t.Fatalf("Failed to run code: %v", err) + } + + // Run testing code + instance.DumpProfile() + + out := statsRunner.stats.print() + for _, s := range test.findInOutput { + if !strings.Contains(string(out), s) { + t.Errorf("did not find occurrences of %v in %v", s, string(out)) + } + } + }) + } +} + +func TestStatisticsRunner_getSummaryInitializesNewStatsWhenUninitialized(t *testing.T) { + statsRunner := &statisticRunner{ + stats: nil, + } + _ = statsRunner.getSummary() + if statsRunner.stats == nil { + t.Errorf("summary should have been initialized") + } +} + +func TestStatisticsRunner_runInitializesNewStatsWhenUninitialized(t *testing.T) { + statsRunner := &statisticRunner{ + stats: nil, + } + _, _ = statsRunner.run(&context{ + code: []Instruction{{STOP, 0}}, + stack: NewStack(), + }) + if statsRunner.stats == nil { + t.Errorf("run should have initialized stats") + } +} + +func TestStatisticsRunner_statisticsStopsWhenExecutionEncountersAnError(t *testing.T) { + statsRunner := &statisticRunner{ + stats: nil, + } + _, _ = statsRunner.run(&context{ + // this code should not reach a STOP since MCOPY should fail because + // there are not enough items on the stack + code: []Instruction{{MCOPY, 0}, {STOP, 0}}, + stack: NewStack(), + }) + if statsRunner.stats.singleCount[uint64(STOP)] != 0 { + t.Errorf("unexpected statistics: stop should not be executed, got %v", statsRunner.stats.singleCount[uint64(STOP)]) + } +} + +func TestStatisticsRunner_print_getTopN_returnFirstNElementsOfManyMore(t *testing.T) { + want := "\n----- Statistics ------\n" + want += "\nSteps: 0\n" + want += "\nSingles:\n" + want += "\tPUSH5 : 5 (+Inf%)\n" + want += "\tPUSH4 : 4 (+Inf%)\n" + want += "\tPUSH3 : 3 (+Inf%)\n" + want += "\tPUSH2 : 2 (+Inf%)\n" + want += "\tSTOP : 1 (+Inf%)\n" + want += "\nPairs:\n\nTriples:\n\nQuads:\n\n" + + stats := statistics{ + singleCount: map[uint64]uint64{ + uint64(STOP): 1, + uint64(PUSH1): 1, + uint64(PUSH2): 2, + uint64(PUSH3): 3, + uint64(PUSH4): 4, + uint64(PUSH5): 5, + }, + } + got := stats.print() + if got != want { + t.Errorf("unexpected output: want %v, got %v", want, got) + } +} diff --git a/go/interpreter/sfvm/instruction_statistics.go b/go/interpreter/sfvm/instruction_statistics.go new file mode 100644 index 00000000..89f6eeaf --- /dev/null +++ b/go/interpreter/sfvm/instruction_statistics.go @@ -0,0 +1,184 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "sort" + "strings" + "sync" +) + +// statisticRunner is a runner that collects statistics about the instruction +// sequence of the executed code. +type statisticRunner struct { + mutex sync.Mutex + stats *statistics +} + +func (s *statisticRunner) run(c *context) (status, error) { + stats := statsCollector{stats: newStatistics()} + status := statusRunning + for status == statusRunning { + if c.pc < int32(len(c.code)) { + stats.nextOp(c.code[c.pc].opcode) + } + status = execute(c, true) + } + s.mutex.Lock() + defer s.mutex.Unlock() + if s.stats == nil { + s.stats = newStatistics() + } + s.stats.insert(stats.stats) + return status, nil +} + +// getSummary returns a summary of the collected statistics in a human-readable +// format. +func (s *statisticRunner) getSummary() string { + s.mutex.Lock() + defer s.mutex.Unlock() + if s.stats == nil { + s.stats = newStatistics() + } + return s.stats.print() +} + +// reset clears the collected statistics. +func (s *statisticRunner) reset() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.stats = newStatistics() +} + +// statistics contains the instruction sequence statistics of a code execution. +// It counts the number of times each instruction is executed, as well as the +// number of times each pair, triple, and quad of instructions are executed. +type statistics struct { + count uint64 + singleCount map[uint64]uint64 + pairCount map[uint64]uint64 + tripleCount map[uint64]uint64 + quadCount map[uint64]uint64 +} + +func newStatistics() *statistics { + return &statistics{ + singleCount: map[uint64]uint64{}, + pairCount: map[uint64]uint64{}, + tripleCount: map[uint64]uint64{}, + quadCount: map[uint64]uint64{}, + } +} + +// insert adds the instruction counts of the given statistics to this instance. +func (s *statistics) insert(src *statistics) { + s.count += src.count + for k, v := range src.singleCount { + s.singleCount[k] += v + } + for k, v := range src.pairCount { + s.pairCount[k] += v + } + for k, v := range src.tripleCount { + s.tripleCount[k] += v + } + for k, v := range src.quadCount { + s.quadCount[k] += v + } +} + +// print returns a human-readable summary of the collected statistics. +func (s *statistics) print() string { + + type entry struct { + value uint64 + count uint64 + } + + getTopN := func(data map[uint64]uint64, n int) []entry { + list := make([]entry, 0, len(data)) + for k, c := range data { + list = append(list, entry{k, c}) + } + sort.Slice(list, func(i, j int) bool { + if list[i].count != list[j].count { + return list[i].count > list[j].count + } + return list[i].value < list[j].value + }) + if len(list) < n { + return list + } + return list[0:n] + } + + builder := strings.Builder{} + write := func(format string, args ...interface{}) { + builder.WriteString(fmt.Sprintf(format, args...)) + } + + write("\n----- Statistics ------\n") + write("\nSteps: %d\n", s.count) + write("\nSingles:\n") + for _, e := range getTopN(s.singleCount, 5) { + write("\t%-30v: %d (%.2f%%)\n", OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) + } + write("\nPairs:\n") + for _, e := range getTopN(s.pairCount, 5) { + write("\t%-30v%-30v: %d (%.2f%%)\n", OpCode(e.value>>16), OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) + } + write("\nTriples:\n") + for _, e := range getTopN(s.tripleCount, 5) { + write("\t%-30v%-30v%-30v: %d (%.2f%%)\n", OpCode(e.value>>32), OpCode(e.value>>16), OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) + } + + write("\nQuads:\n") + for _, e := range getTopN(s.quadCount, 5) { + write("\t%-30v%-30v%-30v%-30v: %d (%.2f%%)\n", OpCode(e.value>>48), OpCode(e.value>>32), OpCode(e.value>>16), OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) + } + write("\n") + + return builder.String() +} + +// statsCollector is a helper struct that keeps track of the resent history of +// instructions executed by the VM to collect instruction sequence statistics. +type statsCollector struct { + stats *statistics + + last uint64 + secondLast uint64 + thirdLast uint64 +} + +func (s *statsCollector) nextOp(op OpCode) { + cur := uint64(op) + s.stats.count++ + s.stats.singleCount[cur]++ + if s.stats.count == 1 { + s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast + return + } + s.stats.pairCount[s.last<<16|cur]++ + if s.stats.count == 2 { + s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast + return + } + s.stats.tripleCount[s.secondLast<<32|s.last<<16|cur]++ + if s.stats.count == 3 { + s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast + return + } + s.stats.quadCount[s.thirdLast<<48|s.secondLast<<32|s.last<<16|cur]++ + s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast +} diff --git a/go/interpreter/sfvm/instruction_test.go b/go/interpreter/sfvm/instruction_test.go new file mode 100644 index 00000000..48687986 --- /dev/null +++ b/go/interpreter/sfvm/instruction_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import "testing" + +func TestInstruction_String(t *testing.T) { + + tests := []struct { + instruction Instruction + want string + }{ + {Instruction{opcode: STOP, arg: 0x0000}, "STOP"}, + {Instruction{opcode: PUSH1, arg: 0x0001}, "PUSH1 0x0001"}, + {Instruction{opcode: PUSH2, arg: 0x0002}, "PUSH2 0x0002"}, + {Instruction{opcode: DATA, arg: 0x0002}, "DATA 0x0002"}, + {Instruction{opcode: JUMP_TO, arg: 0x0002}, "JUMP_TO 0x0002"}, + {Instruction{opcode: PUSH2_JUMP, arg: 0x0002}, "PUSH2_JUMP 0x0002"}, + {Instruction{opcode: PUSH2_JUMPI, arg: 0x0002}, "PUSH2_JUMPI 0x0002"}, + {Instruction{opcode: PUSH1_PUSH4_DUP3, arg: 0x0002}, "PUSH1_PUSH4_DUP3 0x0002"}, + } + + for _, tt := range tests { + if got := tt.instruction.String(); got != tt.want { + t.Errorf("Instruction.String() = %q, want %q", got, tt.want) + } + } + +} + +func TestCode_String(t *testing.T) { + code := Code{ + Instruction{opcode: STOP, arg: 0x0000}, + Instruction{opcode: PUSH1, arg: 0x0001}, + } + if got, want := code.String(), "0x0000: STOP\n0x0001: PUSH1 0x0001\n"; got != want { + t.Errorf("Code.String() = %q, want %q", got, want) + } +} diff --git a/go/interpreter/sfvm/instructions.go b/go/interpreter/sfvm/instructions.go new file mode 100644 index 00000000..75719033 --- /dev/null +++ b/go/interpreter/sfvm/instructions.go @@ -0,0 +1,1171 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "math" + + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/holiman/uint256" +) + +func opStop() status { + return statusStopped +} + +func opEndWithResult(c *context) error { + offset := c.stack.pop() + size := c.stack.pop() + var err error + c.returnData, err = c.memory.getSlice(offset, size, c) + return err +} + +func opPc(c *context) { + c.stack.pushUndefined().SetUint64(uint64(c.code[c.pc].arg)) +} + +func checkJumpDest(c *context) error { + if int(c.pc+1) >= len(c.code) || c.code[c.pc+1].opcode != JUMPDEST { + return errInvalidJump + } + return nil +} + +func opJump(c *context) error { + destination := c.stack.pop() + // overflow check + if !destination.IsUint64() || destination.Uint64() > math.MaxInt32 { + return errInvalidJump + } + // Update the PC to the jump destination -1 since interpreter will increase PC by 1 afterward. + c.pc = int32(destination.Uint64()) - 1 + return checkJumpDest(c) +} + +func opJumpi(c *context) error { + destination := c.stack.pop() + condition := c.stack.pop() + if !condition.IsZero() { + // overflow check + if !destination.IsUint64() || destination.Uint64() > math.MaxInt32 { + return errInvalidJump + } + // Update the PC to the jump destination -1 since interpreter will increase PC by 1 afterward. + c.pc = int32(destination.Uint64()) - 1 + return checkJumpDest(c) + } + return nil +} + +func opJumpTo(c *context) { + // Update the PC to the jump destination -1 since interpreter will increase PC by 1 afterward. + c.pc = int32(c.code[c.pc].arg) - 1 +} + +func opPop(c *context) { + c.stack.pop() +} + +func opPush(c *context, n int) { + z := c.stack.pushUndefined() + num_instructions := int32(n/2 + n%2) + data := c.code[c.pc : c.pc+num_instructions] + + _ = data[num_instructions-1] + var value [32]byte + for i := 0; i < n; i++ { + if i%2 == 0 { + value[i] = byte(data[i/2].arg >> 8) + } else { + value[i] = byte(data[i/2].arg) + } + } + z.SetBytes(value[0:n]) + c.pc += num_instructions - 1 +} + +func opPush0(c *context) error { + if !c.isAtLeast(tosca.R12_Shanghai) { + return errInvalidRevision + } + z := c.stack.pushUndefined() + z[3], z[2], z[1], z[0] = 0, 0, 0, 0 + return nil +} + +func opPush1(c *context) { + z := c.stack.pushUndefined() + z[3], z[2], z[1] = 0, 0, 0 + z[0] = uint64(c.code[c.pc].arg >> 8) +} + +func opPush2(c *context) { + z := c.stack.pushUndefined() + z[3], z[2], z[1] = 0, 0, 0 + z[0] = uint64(c.code[c.pc].arg) +} + +func opPush3(c *context) { + z := c.stack.pushUndefined() + z[3], z[2], z[1] = 0, 0, 0 + data := c.code[c.pc : c.pc+2] + _ = data[1] + z[0] = uint64(data[0].arg)<<8 | uint64(data[1].arg>>8) + c.pc += 1 +} + +func opPush4(c *context) { + z := c.stack.pushUndefined() + z[3], z[2], z[1] = 0, 0, 0 + + data := c.code[c.pc : c.pc+2] + _ = data[1] // causes bound check to be performed only once (may become unneeded in the future) + z[0] = (uint64(data[0].arg) << 16) | uint64(data[1].arg) + c.pc += 1 +} + +func opPush32(c *context) { + z := c.stack.pushUndefined() + + data := c.code[c.pc : c.pc+16] + _ = data[15] // causes bound check to be performed only once (may become unneeded in the future) + z[3] = (uint64(data[0].arg) << 48) | (uint64(data[1].arg) << 32) | (uint64(data[2].arg) << 16) | uint64(data[3].arg) + z[2] = (uint64(data[4].arg) << 48) | (uint64(data[5].arg) << 32) | (uint64(data[6].arg) << 16) | uint64(data[7].arg) + z[1] = (uint64(data[8].arg) << 48) | (uint64(data[9].arg) << 32) | (uint64(data[10].arg) << 16) | uint64(data[11].arg) + z[0] = (uint64(data[12].arg) << 48) | (uint64(data[13].arg) << 32) | (uint64(data[14].arg) << 16) | uint64(data[15].arg) + c.pc += 15 +} + +func opDup(c *context, pos int) { + c.stack.dup(pos - 1) +} + +func opSwap(c *context, pos int) { + c.stack.swap(pos) +} + +func opMstore(c *context) error { + var addr = c.stack.pop() + var value = c.stack.pop() + v := value.Bytes32() + return c.memory.set(addr, v[:], c) +} + +func opMstore8(c *context) error { + var addr = c.stack.pop() + var value = c.stack.pop() + return c.memory.set(addr, []byte{byte(value.Uint64())}, c) +} + +func opMcopy(c *context) error { + + if !c.isAtLeast(tosca.R13_Cancun) { + return errInvalidRevision + } + + var ( + destAddr = c.stack.pop() + srcAddr = c.stack.pop() + size = c.stack.pop() + ) + + data, err := c.memory.getSlice(srcAddr, size, c) + if err != nil { + return err + } + + price := tosca.Gas(3 * tosca.SizeInWords(size.Uint64())) + if err := c.useGas(price); err != nil { + return err + } + + if err := c.memory.set(destAddr, data, c); err != nil { + return err + } + return nil +} + +func opMload(c *context) error { + var addr = c.stack.peek() + return c.memory.readWord(addr, addr, c) +} + +func opMsize(c *context) { + c.stack.pushUndefined().SetUint64(uint64(c.memory.length())) +} + +func opSstore(c *context) error { + + // SStore is a write instruction, it shall not be executed in static mode. + if c.params.Static { + return errStaticContextViolation + } + + // EIP-2200 demands that at least 2300 gas is available for SSTORE + if c.gas <= 2300 { + return errOutOfGas + } + + var key = tosca.Key(c.stack.pop().Bytes32()) + var value = tosca.Word(c.stack.pop().Bytes32()) + + cost := tosca.Gas(0) + if c.isAtLeast(tosca.R09_Berlin) && + c.context.AccessStorage(c.params.Recipient, key) == tosca.ColdAccess { + cost += 2100 + } + + storageStatus := c.context.SetStorage(c.params.Recipient, key, value) + + cost += getDynamicCostsForSstore(c.params.Revision, storageStatus) + if err := c.useGas(cost); err != nil { + return err + } + + c.refund += getRefundForSstore(c.params.Revision, storageStatus) + return nil +} + +func opSload(c *context) error { + var top = c.stack.peek() + + addr := c.params.Recipient + slot := tosca.Key(top.Bytes32()) + if c.isAtLeast(tosca.R09_Berlin) { + // charge costs for warm/cold slot access + costs := tosca.Gas(100) + if c.context.AccessStorage(addr, slot) == tosca.ColdAccess { + costs = 2100 + } + if err := c.useGas(costs); err != nil { + return err + } + } + value := c.context.GetStorage(addr, slot) + top.SetBytes32(value[:]) + return nil +} + +func opTstore(c *context) error { + + if !c.isAtLeast(tosca.R13_Cancun) { + return errInvalidRevision + } + + // Although not mentioned in the yellow paper, nor in CALL description at + // website (https://www.evm.codes/#FA) Geth treats this Op as a write instruction. + // therefore it shall not be executed in static mode. + if c.params.Static { + return errStaticContextViolation + } + + key := tosca.Key(c.stack.pop().Bytes32()) + value := tosca.Word(c.stack.pop().Bytes32()) + c.context.SetTransientStorage(c.params.Recipient, key, value) + return nil +} + +func opTload(c *context) error { + if !c.isAtLeast(tosca.R13_Cancun) { + return errInvalidRevision + } + + top := c.stack.peek() + key := tosca.Key(top.Bytes32()) + value := c.context.GetTransientStorage(c.params.Recipient, key) + top.SetBytes32(value[:]) + return nil +} + +func opCaller(c *context) { + c.stack.pushUndefined().SetBytes20(c.params.Sender[:]) +} + +func opCallvalue(c *context) { + c.stack.pushUndefined().SetBytes32(c.params.Value[:]) +} + +func opCallDatasize(c *context) { + size := len(c.params.Input) + c.stack.pushUndefined().SetUint64(uint64(size)) +} + +func opCallDataload(c *context) { + top := c.stack.peek() + value := getData(c.params.Input, top, 32) + top.SetBytes(value) +} + +func genericDataCopy(c *context, source []byte) error { + var ( + memOffset = c.stack.pop() + dataOffset = c.stack.pop() + length = c.stack.pop() + ) + + // Charge for the copy costs + words := tosca.SizeInWords(length.Uint64()) + if err := c.useGas(tosca.Gas(3 * words)); err != nil { + return err + } + + data, err := c.memory.getSlice(memOffset, length, c) + if err != nil { + return err + } + + // length overflow has been checked by getSlice + dataCopy := getData(source, dataOffset, length.Uint64()) + copy(data, dataCopy) + return nil +} + +func opAnd(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.And(a, b) +} + +func opOr(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.Or(a, b) +} + +func opNot(c *context) { + a := c.stack.peek() + a.Not(a) +} +func opXor(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.Xor(a, b) +} + +func opIszero(c *context) { + top := c.stack.peek() + if top.IsZero() { + top.SetOne() + } else { + top.Clear() + } +} + +func opEq(c *context) { + a := c.stack.pop() + b := c.stack.peek() + res := a.Cmp(b) + for i := range b { + b[i] = 0 + } + if res == 0 { + b[0] = 1 + } else { + b[0] = 0 + } +} + +func opLt(c *context) { + a := c.stack.pop() + b := c.stack.peek() + if a.Lt(b) { + b.SetOne() + } else { + b.Clear() + } +} + +func opGt(c *context) { + a := c.stack.pop() + b := c.stack.peek() + if a.Gt(b) { + b.SetOne() + } else { + b.Clear() + } +} + +func opSlt(c *context) { + a := c.stack.pop() + b := c.stack.peek() + if a.Slt(b) { + b.SetOne() + } else { + b.Clear() + } +} + +func opSgt(c *context) { + a := c.stack.pop() + b := c.stack.peek() + if a.Sgt(b) { + b.SetOne() + } else { + b.Clear() + } +} + +func opShr(c *context) { + a := c.stack.pop() + b := c.stack.peek() + if a.LtUint64(256) { + b.Rsh(b, uint(a.Uint64())) + } else { + b.Clear() + } +} + +func opShl(c *context) { + a := c.stack.pop() + b := c.stack.peek() + if a.LtUint64(256) { + b.Lsh(b, uint(a.Uint64())) + } else { + b.Clear() + } +} + +func opSar(c *context) { + a := c.stack.pop() + b := c.stack.peek() + if a.GtUint64(256) { + if b.Sign() >= 0 { + b.Clear() + } else { + b.SetAllOne() + } + return + } + b.SRsh(b, uint(a.Uint64())) +} + +func opClz(c *context) error { + if !c.isAtLeast(tosca.R15_Osaka) { + return errInvalidRevision + } + a := c.stack.peek() + a.SetUint64(256 - uint64(a.BitLen())) + return nil +} + +func opSignExtend(c *context) { + back, num := c.stack.pop(), c.stack.peek() + num.ExtendSign(num, back) +} + +func opByte(c *context) { + th, val := c.stack.pop(), c.stack.peek() + val.Byte(th) +} + +func opAdd(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.Add(a, b) +} + +func opSub(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.Sub(a, b) +} + +func opMul(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.Mul(a, b) +} + +func opMulMod(c *context) { + a := c.stack.pop() + b := c.stack.pop() + n := c.stack.peek() + n.MulMod(a, b, n) +} + +func opDiv(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.Div(a, b) +} + +func opSDiv(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.SDiv(a, b) +} + +func opMod(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.Mod(a, b) +} + +func opAddMod(c *context) { + a := c.stack.pop() + b := c.stack.pop() + n := c.stack.peek() + n.AddMod(a, b, n) +} + +func opSMod(c *context) { + a := c.stack.pop() + b := c.stack.peek() + b.SMod(a, b) +} + +func opExp(c *context) error { + base, exponent := c.stack.pop(), c.stack.peek() + if err := c.useGas(tosca.Gas(50 * exponent.ByteLen())); err != nil { + return err + } + exponent.Exp(base, exponent) + return nil +} + +// Evaluations show a 96% hit rate of this configuration. +var sha3Cache = newSha3HashCache(1<<16, 1<<18) + +func opSha3(c *context) error { + offset, size := c.stack.pop(), c.stack.peek() + + data, err := c.memory.getSlice(offset, size, c) + if err != nil { + return err + } + + words := tosca.SizeInWords(size.Uint64()) + price := tosca.Gas(6 * words) + if err := c.useGas(price); err != nil { + return err + } + + var hash tosca.Hash + if c.withShaCache { + // Cache hashes since identical values are frequently re-hashed. + hash = sha3Cache.hash(data) + } else { + hash = Keccak256(data) + } + + size.SetBytes32(hash[:]) + return nil +} + +func opGas(c *context) { + c.stack.pushUndefined().SetUint64(uint64(c.gas)) +} + +// opPrevRandao / opDifficulty +func opPrevRandao(c *context) { + prevRandao := c.params.PrevRandao + c.stack.pushUndefined().SetBytes32(prevRandao[:]) +} + +func opTimestamp(c *context) { + time := c.params.Timestamp + c.stack.pushUndefined().SetUint64(uint64(time)) +} + +func opNumber(c *context) { + number := c.params.BlockNumber + c.stack.pushUndefined().SetUint64(uint64(number)) +} + +func opCoinbase(c *context) { + coinbase := c.params.Coinbase + c.stack.pushUndefined().SetBytes20(coinbase[:]) +} + +func opGasLimit(c *context) { + limit := c.params.GasLimit + c.stack.pushUndefined().SetUint64(uint64(limit)) +} + +func opGasPrice(c *context) { + price := c.params.GasPrice + c.stack.pushUndefined().SetBytes32(price[:]) +} + +func opBalance(c *context) error { + slot := c.stack.peek() + address := tosca.Address(slot.Bytes20()) + if c.isAtLeast(tosca.R09_Berlin) { + if err := c.useGas(getAccessCost(c.context.AccessAccount(address))); err != nil { + return err + } + } + balance := c.context.GetBalance(address) + slot.SetBytes32(balance[:]) + return nil +} + +func opSelfbalance(c *context) { + balance := c.context.GetBalance(c.params.Recipient) + c.stack.pushUndefined().SetBytes32(balance[:]) +} + +func opBaseFee(c *context) error { + if !c.isAtLeast(tosca.R10_London) { + return errInvalidRevision + } + fee := c.params.BaseFee + c.stack.pushUndefined().SetBytes32(fee[:]) + return nil +} + +func opBlobHash(c *context) error { + if !c.isAtLeast(tosca.R13_Cancun) { + return errInvalidRevision + } + + index := c.stack.pop() + blobHashesLength := uint64(len(c.params.BlobHashes)) + if index.IsUint64() && index.Uint64() < blobHashesLength { + c.stack.pushUndefined().SetBytes32(c.params.BlobHashes[index.Uint64()][:]) + } else { + c.stack.push(uint256.NewInt(0)) + } + return nil +} + +func opBlobBaseFee(c *context) error { + if !c.isAtLeast(tosca.R13_Cancun) { + return errInvalidRevision + } + fee := c.params.BlobBaseFee + c.stack.pushUndefined().SetBytes32(fee[:]) + return nil +} + +func opSelfdestruct(c *context) (status, error) { + + // SelfDestruct is a write instruction, it shall not be executed in static mode. + if c.params.Static { + return statusStopped, errStaticContextViolation + } + + beneficiary := tosca.Address(c.stack.pop().Bytes20()) + // Selfdestruct gas cost defined in EIP-105 (see https://eips.ethereum.org/EIPS/eip-150) + cost := tosca.Gas(0) + if c.isAtLeast(tosca.R09_Berlin) { + // as https://eips.ethereum.org/EIPS/eip-2929#selfdestruct-changes says, + // selfdestruct does not charge for warm access + if accessStatus := c.context.AccessAccount(beneficiary); accessStatus != tosca.WarmAccess { + cost += getAccessCost(accessStatus) + } + } + + cost += selfDestructNewAccountCost( + isEmpty(c.context, beneficiary), + c.context.GetBalance(c.params.Recipient), + ) + // even death is not for free + if err := c.useGas(cost); err != nil { + return statusStopped, err + } + + destructed := c.context.SelfDestruct(c.params.Recipient, beneficiary) + c.refund += selfDestructRefund(destructed, c.params.Revision) + return statusSelfDestructed, nil +} + +func selfDestructNewAccountCost(beneficiaryEmpty bool, balance tosca.Value) tosca.Gas { + if beneficiaryEmpty && balance != (tosca.Value{}) { + // cost of creating an account defined in eip-150 (see https://eips.ethereum.org/EIPS/eip-150) + // CreateBySelfdestructGas is used when the refunded account is one that does + // not exist. This logic is similar to call. + return 25_000 + } + return 0 +} + +func selfDestructRefund(destructed bool, revision tosca.Revision) tosca.Gas { + // Since London and after there is no more refund (see https://eips.ethereum.org/EIPS/eip-3529) + if destructed && revision < tosca.R10_London { + return 24_000 + } + return 0 +} + +func opChainId(c *context) { + id := c.params.ChainID + c.stack.pushUndefined().SetBytes32(id[:]) +} + +func opBlockhash(c *context) { + top := c.stack.peek() + + requestedBlockNumber := top.Uint64() + currentBlockNumber := uint64(c.params.BlockNumber) + if !top.IsUint64() || requestedBlockNumber >= currentBlockNumber { + top.Clear() + return + } + + oldestInHistory := uint64(256) + if currentBlockNumber < 256 { + oldestInHistory = uint64(currentBlockNumber) + } + oldestInHistory = currentBlockNumber - oldestInHistory + if requestedBlockNumber < oldestInHistory { + top.Clear() + return + } + + hash := c.context.GetBlockHash(int64(requestedBlockNumber)) + top.SetBytes(hash[:]) +} + +func opAddress(c *context) { + c.stack.pushUndefined().SetBytes20(c.params.Recipient[:]) +} + +func opOrigin(c *context) { + origin := c.params.Origin + c.stack.pushUndefined().SetBytes20(origin[:]) +} + +func opCodeSize(c *context) { + size := len(c.params.Code) + c.stack.pushUndefined().SetUint64(uint64(size)) +} + +func opExtcodesize(c *context) error { + top := c.stack.peek() + address := tosca.Address(top.Bytes20()) + if c.isAtLeast(tosca.R09_Berlin) { + if err := c.useGas(getAccessCost(c.context.AccessAccount(address))); err != nil { + return err + } + } + top.SetUint64(uint64(c.context.GetCodeSize(address))) + return nil +} + +func opExtcodehash(c *context) error { + slot := c.stack.peek() + address := tosca.Address(slot.Bytes20()) + if c.isAtLeast(tosca.R09_Berlin) { + if err := c.useGas(getAccessCost(c.context.AccessAccount(address))); err != nil { + return err + } + } + + if isEmpty(c.context, address) { + slot.Clear() + } else { + hash := c.context.GetCodeHash(address) + slot.SetBytes32(hash[:]) + } + return nil +} + +func genericCreate(c *context, kind tosca.CallKind) error { + + // Create is a write instruction, it shall not be executed in static mode. + if c.params.Static { + return errStaticContextViolation + } + + var ( + value = c.stack.pop() + offset = c.stack.pop() + size = c.stack.pop() + salt = tosca.Hash{} + ) + if kind == tosca.Create2 { + salt = c.stack.pop().Bytes32() // pop salt value for Create2 + } + + input, err := c.memory.getSlice(offset, size, c) + if err != nil { + return err + } + + if c.isAtLeast(tosca.R12_Shanghai) { + initCodeCost, err := computeCodeSizeCost(size.Uint64()) + if err != nil { + return err + } + if err = c.useGas(tosca.Gas(initCodeCost)); err != nil { + return err + } + } + + if kind == tosca.Create2 { + // Charge for hashing the init code to compute the target address. + words := tosca.SizeInWords(size.Uint64()) + if err := c.useGas(tosca.Gas(6 * words)); err != nil { + return err + } + } + + if !value.IsZero() { + balance := c.context.GetBalance(c.params.Recipient) + balanceU256 := new(uint256.Int).SetBytes(balance[:]) + + if value.Gt(balanceU256) { + c.stack.pushUndefined().Clear() + c.returnData = nil + return nil + } + } + + // compute and apply eip150 https://eips.ethereum.org/EIPS/eip-150 + nestedCallGas := c.gas + nestedCallGas -= nestedCallGas / 64 + + res, err := c.context.Call(kind, tosca.CallParameters{ + Sender: c.params.Recipient, + Value: tosca.Value(value.Bytes32()), + Input: input, + Gas: nestedCallGas, + Salt: salt, + }) + + // Push item on the stack based on the returned error. + success := c.stack.pushUndefined() + if !res.Success || err != nil { + success.Clear() + } else { + success.SetBytes20(res.CreatedAddress[:]) + } + + if !res.Success && err == nil { + c.returnData = res.Output + } else { + c.returnData = nil + } + + c.gas -= nestedCallGas + c.gas += res.GasLeft + c.refund += res.GasRefund + return nil +} + +// computeCodeSizeCost checks the size of the init code. +// Returns the gas cost for the size of the init code and nil, or +// zero and an error if size is greater than MaxInitCodeSize. +func computeCodeSizeCost(size uint64) (tosca.Gas, error) { + const ( + maxCodeSize = 24576 // Maximum bytecode to permit for a contract + maxInitCodeSize = 2 * maxCodeSize // Maximum initcode to permit in a creation transaction and create instructions + ) + if size > maxInitCodeSize { + return 0, errInitCodeTooLarge + } + // Once per word of the init code when creating a contract. + const initCodeWordGas = 2 + return tosca.Gas(initCodeWordGas * tosca.SizeInWords(size)), nil +} + +// getData performs offsetting checks when accessing slices which are not +// handled by the VM memory component: +// - CallData (input) +// - Code & ExtCode +// Because such buffers cannot be resized, out of bounds access will be padded +// with zeroes. +// getData returns a new slice of size length, with the data copied from the +// original slice starting at offset. +// If size is zero, the function returns an empty slice (nil). +func getData(data []byte, offset *uint256.Int, size uint64) []byte { + dataSize := uint64(len(data)) + + // zero length: empty slice + if size == 0 { + return nil + } + // Right-padding: when offset is greater than data length, it is entirely + // inside of padded area + if !offset.IsUint64() || offset.Uint64() >= dataSize { + return make([]byte, size) + } + + start := offset.Uint64() + end := start + size + + // Right-padding: fill with zeroes + res := make([]byte, size) + if end > dataSize { + end = dataSize + } + + copy(res, data[start:end]) + return res +} + +func opExtCodeCopy(c *context) error { + + address := c.stack.pop().Bytes20() + + if c.isAtLeast(tosca.R09_Berlin) { + if err := c.useGas(getAccessCost(c.context.AccessAccount(address))); err != nil { + return err + } + } + + return genericDataCopy(c, c.context.GetCode(address)) +} + +func getAccessCost(accessStatus tosca.AccessStatus) tosca.Gas { + // EIP-2929 says that cold access cost is 2600 and warm is 100. + // (https://eips.ethereum.org/EIPS/eip-2929) + if accessStatus == tosca.ColdAccess { + return tosca.Gas(2600) + } + return tosca.Gas(100) +} + +func genericCall(c *context, kind tosca.CallKind) error { + stack := c.stack + value := uint256.NewInt(0) + + // Pop call parameters. + provided_gas, addr := stack.pop(), stack.pop() + if kind == tosca.Call || kind == tosca.CallCode { + value = stack.pop() + } + inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop() + + // We need to check the existence of the target account before removing + // the gas price for the other cost factors to make sure that the read + // in the state DB is always happening. This is the current EVM behavior, + // and not doing it would be identified by the replay tool as an error. + toAddr := tosca.Address(addr.Bytes20()) + + // Get arguments from the memory. + args, err := c.memory.getSlice(inOffset, inSize, c) + if err != nil { + return err + } + output, err := c.memory.getSlice(retOffset, retSize, c) + if err != nil { + return err + } + + // from berlin onwards access cost changes depending on warm/cold access. + if c.isAtLeast(tosca.R09_Berlin) { + if err := c.useGas(getAccessCost(c.context.AccessAccount(toAddr))); err != nil { + return err + } + } + + // EIP-7702 delegation designation warm/cold cost + if c.isAtLeast(tosca.R14_Prague) { + target, isDelegation := parseDelegationDesignation(c.context.GetCode(toAddr)) + if isDelegation { + if err := c.useGas(getAccessCost(c.context.AccessAccount(target))); err != nil { + return err + } + } + } + + // for static and delegate calls, the following value checks will always be zero. + // Charge for transferring value to a new address + if !value.IsZero() { + if err := c.useGas(CallValueTransferGas); err != nil { + return err + } + } + + // EIP158 states that non-zero value calls that create a new account should + // be charged an additional gas fee. + if kind == tosca.Call && !value.IsZero() && isEmpty(c.context, toAddr) { + if err := c.useGas(CallNewAccountGas); err != nil { + return err + } + } + + // The Homestead hard-fork introduced a limit on the amount of gas that can be + // forwarded to recursive calls. EIP-150 (https://eips.ethereum.org/EIPS/eip-150) + // defines that at all but one 64th of the available gas in one scope may be passed + // to a nested call. + nestedCallGas := tosca.Gas(c.gas - c.gas/64) + if provided_gas.IsUint64() && provided_gas.Uint64() <= math.MaxInt64 && (nestedCallGas >= tosca.Gas(provided_gas.Uint64())) { + nestedCallGas = tosca.Gas(provided_gas.Uint64()) + } + c.gas -= nestedCallGas + + // first use static and dynamic gas cost and then resize the memory + // when out of gas is happening, then mem should not be resized + if !value.IsZero() { + nestedCallGas += CallStipend + } + + // Check that the caller has enough balance to transfer the requested value. + if (kind == tosca.Call || kind == tosca.CallCode) && !value.IsZero() { + balance := c.context.GetBalance(c.params.Recipient) + balanceU256 := new(uint256.Int).SetBytes32(balance[:]) + if balanceU256.Lt(value) { + c.stack.pushUndefined().Clear() + c.returnData = nil + c.gas += nestedCallGas // the gas send to the nested contract is returned + return nil + } + } + + // If we are in static mode, recursive calls are to be treated like + // static calls. This is a consequence of the unification of the + // interpreter interfaces of EVMC and Geth. + // This problem was encountered in block 58413779, transaction 7. + if c.params.Static && kind == tosca.Call { + kind = tosca.StaticCall + } + + // Prepare arguments, depending on call kind + callParams := tosca.CallParameters{ + Input: args, + Gas: nestedCallGas, + Value: tosca.Value(value.Bytes32()), + CodeAddress: toAddr, + } + + switch kind { + case tosca.Call, tosca.StaticCall: + callParams.Sender = c.params.Recipient + callParams.Recipient = toAddr + + case tosca.CallCode: + callParams.Sender = c.params.Recipient + callParams.Recipient = c.params.Recipient + + case tosca.DelegateCall: + callParams.Sender = c.params.Sender + callParams.Recipient = c.params.Recipient + callParams.Value = c.params.Value + } + + // Perform the call. + ret, err := c.context.Call(kind, callParams) + + if err == nil { + copy(output, ret.Output) + } + + success := stack.pushUndefined() + if err != nil || !ret.Success { + success.Clear() + } else { + success.SetOne() + } + c.gas += ret.GasLeft + c.refund += ret.GasRefund + c.returnData = ret.Output + return nil +} + +func opCall(c *context) error { + value := c.stack.peekN(2) + // In a static call, no value must be transferred. + if c.params.Static && !value.IsZero() { + return errStaticContextViolation + } + return genericCall(c, tosca.Call) +} + +func parseDelegationDesignation(code tosca.Code) (tosca.Address, bool) { + if len(code) == 23 && bytes.HasPrefix(code, []byte{0xef, 0x01, 0x00}) { + address := tosca.Address(code[3:23]) + return address, true + } + return tosca.Address{}, false +} + +func opCallCode(c *context) error { + return genericCall(c, tosca.CallCode) +} + +func opStaticCall(c *context) error { + return genericCall(c, tosca.StaticCall) +} + +func opDelegateCall(c *context) error { + return genericCall(c, tosca.DelegateCall) +} + +func opReturnDataSize(c *context) { + c.stack.pushUndefined().SetUint64(uint64(len(c.returnData))) +} + +func opReturnDataCopy(c *context) error { + var ( + memOffset = c.stack.pop() + dataOffset = c.stack.pop() + length = c.stack.pop() + ) + + if !dataOffset.IsUint64() || !length.IsUint64() { + return errOverflow + } + + words := tosca.SizeInWords(length.Uint64()) + if err := c.useGas(tosca.Gas(3 * words)); err != nil { + return errOutOfGas + } + + start := dataOffset.Uint64() + end := start + length.Uint64() + if end < start { + return errOverflow + } + if end > uint64(len(c.returnData)) { + return errOverflow + } + return c.memory.set(memOffset, c.returnData[start:end], c) +} + +func opLog(c *context, n int) error { + + // LogN op codes are write instructions, they shall not be executed in static mode. + if c.params.Static { + return errStaticContextViolation + } + + var ( + offset = c.stack.pop() + size = c.stack.pop() + ) + + topics := make([]tosca.Hash, n) + for i := 0; i < n; i++ { + addr := c.stack.pop() + topics[i] = addr.Bytes32() + } + + data, err := c.memory.getSlice(offset, size, c) + if err != nil { + return err + } + + if err := c.useGas(tosca.Gas(8 * size.Uint64())); err != nil { + return err + } + + // make a copy of the data to disconnect from memory + log_data := bytes.Clone(data) + c.context.EmitLog(tosca.Log{ + Address: c.params.Recipient, + Topics: topics, + Data: log_data, + }) + return nil +} + +// isEmpty is a utility function that checks if an account is empty. An account +// is considered empty if it has no nonce, no balance, and no code. +func isEmpty(c tosca.RunContext, addr tosca.Address) bool { + return c.GetNonce(addr) == 0 && + c.GetBalance(addr) == (tosca.Value{}) && + c.GetCodeSize(addr) == 0 +} diff --git a/go/interpreter/sfvm/instructions_test.go b/go/interpreter/sfvm/instructions_test.go new file mode 100644 index 00000000..455599ee --- /dev/null +++ b/go/interpreter/sfvm/instructions_test.go @@ -0,0 +1,2402 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "crypto/rand" + "errors" + "fmt" + "math" + "slices" + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/holiman/uint256" + "go.uber.org/mock/gomock" +) + +func TestPushN(t *testing.T) { + data := make([]byte, 32) + for i := range data { + data[i] = byte(i + 1) + } + + code := make([]Instruction, 16) + for i := 0; i < 32; i++ { + code[i/2].arg = code[i/2].arg<<8 | uint16(data[i]) + } + + for n := 1; n <= 32; n++ { + ctxt := context{ + code: code, + stack: NewStack(), + } + + opPush(&ctxt, n) + ctxt.pc++ + + if ctxt.stack.len() != 1 { + t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) + return + } + + if int(ctxt.pc) != n/2+n%2 { + t.Errorf("for PUSH%d program counter did not progress to %d, got %d", n, n/2+n%2, ctxt.pc) + } + + got := ctxt.stack.peek().Bytes() + if len(got) != n { + t.Errorf("expected %d bytes on the stack, got %d with values %v", n, len(got), got) + } + + for i := range got { + if data[i] != got[i] { + t.Errorf("for PUSH%d expected value %d to be %d, got %d", n, i, data[i], got[i]) + } + } + } +} + +func TestPush1(t *testing.T) { + code := []Instruction{ + {opcode: PUSH1, arg: 0x1234}, + } + + ctxt := context{ + code: code, + stack: NewStack(), + } + + opPush1(&ctxt) + ctxt.pc++ + + if ctxt.stack.len() != 1 { + t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) + return + } + + if int(ctxt.pc) != 1 { + t.Errorf("program counter did not progress to %d, got %d", 1, ctxt.pc) + } + + got := ctxt.stack.peek().Bytes() + if len(got) != 1 { + t.Errorf("expected 1 byte on the stack, got %d with values %v", len(got), got) + } + if got[0] != 0x12 { + t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) + } +} + +func TestPush2(t *testing.T) { + code := []Instruction{ + {opcode: PUSH2, arg: 0x1234}, + } + + ctxt := context{ + code: code, + stack: NewStack(), + } + + opPush2(&ctxt) + ctxt.pc++ + + if ctxt.stack.len() != 1 { + t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) + return + } + + if int(ctxt.pc) != 1 { + t.Errorf("program counter did not progress to %d, got %d", 1, ctxt.pc) + } + + got := ctxt.stack.peek().Bytes() + if len(got) != 2 { + t.Errorf("expected 2 byte on the stack, got %d with values %v", len(got), got) + } + if got[0] != 0x12 { + t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) + } + if got[1] != 0x34 { + t.Errorf("expected %d for second byte, got %d", 0x34, got[1]) + } +} + +func TestPush3(t *testing.T) { + code := []Instruction{ + {opcode: PUSH2, arg: 0x1234}, + {opcode: DATA, arg: 0x5678}, + } + + ctxt := context{ + code: code, + stack: NewStack(), + } + + opPush3(&ctxt) + ctxt.pc++ + + if ctxt.stack.len() != 1 { + t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) + return + } + + if int(ctxt.pc) != 2 { + t.Errorf("program counter did not progress to %d, got %d", 2, ctxt.pc) + } + + got := ctxt.stack.peek().Bytes() + if len(got) != 3 { + t.Errorf("expected 3 byte on the stack, got %d with values %v", len(got), got) + } + if got[0] != 0x12 { + t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) + } + if got[1] != 0x34 { + t.Errorf("expected %d for second byte, got %d", 0x34, got[1]) + } + if got[2] != 0x56 { + t.Errorf("expected %d for third byte, got %d", 0x56, got[2]) + } +} + +func TestPush4(t *testing.T) { + code := []Instruction{ + {opcode: PUSH2, arg: 0x1234}, + {opcode: DATA, arg: 0x5678}, + } + + ctxt := context{ + code: code, + stack: NewStack(), + } + + opPush4(&ctxt) + ctxt.pc++ + + if ctxt.stack.len() != 1 { + t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) + return + } + + if int(ctxt.pc) != 2 { + t.Errorf("program counter did not progress to %d, got %d", 2, ctxt.pc) + } + + got := ctxt.stack.peek().Bytes() + if len(got) != 4 { + t.Errorf("expected 3 byte on the stack, got %d with values %v", len(got), got) + } + if got[0] != 0x12 { + t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) + } + if got[1] != 0x34 { + t.Errorf("expected %d for second byte, got %d", 0x34, got[1]) + } + if got[2] != 0x56 { + t.Errorf("expected %d for third byte, got %d", 0x56, got[2]) + } + if got[3] != 0x78 { + t.Errorf("expected %d for 4th byte, got %d", 0x78, got[3]) + } +} + +func TestCallChecksBalances(t *testing.T) { + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + + source := tosca.Address{1} + target := tosca.Address{2} + ctxt := context{ + params: tosca.Parameters{ + Recipient: source, + }, + context: runContext, + stack: NewStack(), + memory: NewMemory(), + gas: 1 << 20, + } + + // Prepare stack arguments. + ctxt.stack.stackPointer = 7 + ctxt.stack.data[4].Set(uint256.NewInt(1)) // < the value to be transferred + ctxt.stack.data[5].SetBytes(target[:]) // < the target address for the call + + // The target account should exist and the source account without funds. + runContext.EXPECT().GetNonce(target).Return(uint64(1)) + runContext.EXPECT().GetBalance(source).Return(tosca.Value{}) + + err := opCall(&ctxt) + if err != nil { + t.Errorf("opCall failed: %v", err) + } + + if want, got := 1, ctxt.stack.len(); want != got { + t.Fatalf("unexpected stack size, wanted %d, got %d", want, got) + } + + if want, got := *uint256.NewInt(0), ctxt.stack.data[0]; want != got { + t.Fatalf("unexpected value on top of stack, wanted %v, got %v", want, got) + } +} + +func TestCreateChecksBalance(t *testing.T) { + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + + source := tosca.Address{1} + ctxt := context{ + params: tosca.Parameters{ + Recipient: source, + }, + context: runContext, + stack: NewStack(), + memory: NewMemory(), + gas: 1 << 20, + } + + // Prepare stack arguments. + ctxt.stack.stackPointer = 3 + ctxt.stack.data[2].Set(uint256.NewInt(1)) // < the value to be transferred + + // The source account should have enough funds. + runContext.EXPECT().GetBalance(source).Return(tosca.Value{}) + + err := genericCreate(&ctxt, tosca.Create) + if err != nil { + t.Errorf("opCreate failed: %v", err) + } + if want, got := 1, ctxt.stack.len(); want != got { + t.Fatalf("unexpected stack size, wanted %d, got %d", want, got) + } + if want, got := *uint256.NewInt(0), ctxt.stack.data[0]; want != got { + t.Fatalf("unexpected value on top of stack, wanted %v, got %v", want, got) + } +} + +func TestBlobHash_PushesCorrectValueOnStack(t *testing.T) { + hash := tosca.Hash{1} + + tests := map[string]struct { + setup func(*tosca.Parameters, *stack) + want tosca.Hash + }{ + "regular": { + setup: func(params *tosca.Parameters, stack *stack) { + stack.push(uint256.NewInt(0)) + params.BlobHashes = []tosca.Hash{hash} + }, + want: hash, + }, + "no-hashes": { + setup: func(params *tosca.Parameters, stack *stack) { + stack.push(uint256.NewInt(0)) + }, + want: tosca.Hash{}, + }, + "target-non-existent": { + setup: func(params *tosca.Parameters, stack *stack) { + stack.push(uint256.NewInt(1)) + }, + want: tosca.Hash{}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + + ctxt := context{ + stack: NewStack(), + } + ctxt.params.Revision = tosca.R13_Cancun + test.setup(&ctxt.params, ctxt.stack) + + err := opBlobHash(&ctxt) + if err != nil { + t.Fatalf("unexpected return: %v", err) + } + if want, got := test.want, ctxt.stack.data[0]; tosca.Hash(got.Bytes32()) != want { + t.Fatalf("unexpected value on top of stack, wanted %v, got %v", want, got) + } + }) + } +} + +func TestBlobBaseFee_ReturnsErrorWhenCalledWithUnsupportedRevision(t *testing.T) { + ctxt := context{ + stack: NewStack(), + } + ctxt.params.Revision = tosca.R12_Shanghai + + err := opBlobBaseFee(&ctxt) + if want, got := errInvalidRevision, err; want != got { + t.Fatalf("unexpected return, wanted %v, got %v", want, got) + } +} + +func TestCreateShanghaiInitCodeSize(t *testing.T) { + maxInitCodeSize := uint64(49152) + tests := map[string]struct { + revision tosca.Revision + init_code_size uint64 + expecedErr error + }{ + "paris-0-running": { + revision: tosca.R11_Paris, + init_code_size: 0, + }, + "paris-1-running": { + revision: tosca.R11_Paris, + init_code_size: 1, + }, + "paris-2k-running": { + revision: tosca.R11_Paris, + init_code_size: 2000, + }, + "paris-max-1-running": { + revision: tosca.R11_Paris, + init_code_size: maxInitCodeSize - 1, + }, + "paris-max-running": { + revision: tosca.R11_Paris, + init_code_size: maxInitCodeSize, + }, + "paris-max+1-running": { + revision: tosca.R11_Paris, + init_code_size: maxInitCodeSize + 1, + }, + "paris-100k-running": { + revision: tosca.R11_Paris, + init_code_size: 100000, + }, + "paris-maxuint64-running": { + revision: tosca.R11_Paris, + init_code_size: math.MaxUint64, + expecedErr: errOverflow, + }, + + "shanghai-0-running": { + revision: tosca.R12_Shanghai, + init_code_size: 0, + }, + "shanghai-1-running": { + revision: tosca.R12_Shanghai, + init_code_size: 1, + }, + "shanghai-2k-running": { + revision: tosca.R12_Shanghai, + init_code_size: 2000, + }, + "shanghai-max-1-running": { + revision: tosca.R12_Shanghai, + init_code_size: maxInitCodeSize - 1, + }, + "shanghai-max-running": { + revision: tosca.R12_Shanghai, + init_code_size: maxInitCodeSize, + }, + "shanghai-max+1-running": { + revision: tosca.R12_Shanghai, + init_code_size: maxInitCodeSize + 1, + expecedErr: errInitCodeTooLarge, + }, + "shanghai-100k-running": { + revision: tosca.R12_Shanghai, + init_code_size: 100000, + expecedErr: errInitCodeTooLarge, + }, + "shanghai-maxuint64-running": { + revision: tosca.R12_Shanghai, + init_code_size: math.MaxUint64, + expecedErr: errOverflow, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + + source := tosca.Address{1} + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: test.revision, + }, + Recipient: source, + }, + context: runContext, + stack: NewStack(), + memory: NewMemory(), + gas: 50000, + } + + // Prepare stack arguments. + ctxt.stack.push(uint256.NewInt(test.init_code_size)) + ctxt.stack.push(uint256.NewInt(0)) + ctxt.stack.push(uint256.NewInt(0)) + + if test.expecedErr == nil { + runContext.EXPECT().Call(tosca.Create, gomock.Any()).Return(tosca.CallResult{}, nil) + } + + err := genericCreate(&ctxt, tosca.Create) + if want, got := test.expecedErr, err; want != got { + t.Fatalf("unexpected return, wanted %v, got %v", want, got) + } + }) + } +} + +func TestCreateShanghaiDeploymentCost(t *testing.T) { + tests := []struct { + revision tosca.Revision + initCodeSize uint64 + }{ + // gas cost from evm.codes + {tosca.R11_Paris, 0}, + {tosca.R11_Paris, 1}, + {tosca.R11_Paris, 2000}, + {tosca.R11_Paris, 49152}, + + {tosca.R12_Shanghai, 0}, + {tosca.R12_Shanghai, 1}, + {tosca.R12_Shanghai, 2000}, + {tosca.R12_Shanghai, 49152}, + } + + dynamicCost := func(revision tosca.Revision, size uint64) uint64 { + words := tosca.SizeInWords(size) + // prevent overflow just like geth does + if size > maxMemoryExpansionSize { + return math.MaxInt64 + } + memoryExpansionCost := (words*words)/512 + 3*words + if revision >= tosca.R12_Shanghai { + return 2*words + memoryExpansionCost + } + return memoryExpansionCost + } + + for _, test := range tests { + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + + cost := dynamicCost(test.revision, test.initCodeSize) + + source := tosca.Address{1} + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: test.revision, + }, + Recipient: source, + }, + context: runContext, + stack: NewStack(), + memory: NewMemory(), + gas: tosca.Gas(cost), + } + + // Prepare stack arguments. + ctxt.stack.push(uint256.NewInt(test.initCodeSize)) + ctxt.stack.push(uint256.NewInt(0)) + ctxt.stack.push(uint256.NewInt(0)) + + runContext.EXPECT().Call(tosca.Create, gomock.Any()).Return(tosca.CallResult{}, nil) + + err := genericCreate(&ctxt, tosca.Create) + if err != nil { + t.Errorf("opCreate failed: %v", err) + } + if ctxt.gas != 0 { + t.Errorf("unexpected gas cost, wanted %d, got %d", cost, cost-uint64(ctxt.gas)) + } + } +} + +func TestTransientStorageOperations(t *testing.T) { + address := tosca.Address{} + _, _ = rand.Read(address[:]) + key := tosca.Key{} + _, _ = rand.Read(key[:]) + value := tosca.Word{} + _, _ = rand.Read(value[:]) + + tests := map[string]struct { + op func(*context) error + setup func(*tosca.MockRunContext) + stack []uint256.Int + revision tosca.Revision + err error + }{ + "tload-regular": { + op: opTload, + setup: func(runContext *tosca.MockRunContext) { + runContext.EXPECT().GetTransientStorage(address, key).Return(tosca.Word{}) + }, + stack: []uint256.Int{ + *new(uint256.Int).SetBytes(key[:]), + }, + revision: tosca.R13_Cancun, + }, + "tload-old-revision": { + op: opTload, + revision: tosca.R11_Paris, + err: errInvalidRevision, + }, + "tstore-regular": { + op: opTstore, + setup: func(runContext *tosca.MockRunContext) { + runContext.EXPECT().SetTransientStorage(address, key, value) + }, + stack: []uint256.Int{ + *new(uint256.Int).SetBytes(key[:]), + *new(uint256.Int).SetBytes(value[:]), + }, + revision: tosca.R13_Cancun, + }, + "tstore-old-revision": { + op: opTstore, + revision: tosca.R11_Paris, + err: errInvalidRevision, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctrl := gomock.NewController(t) + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: test.revision, + }, + Recipient: tosca.Address{1}, + }, + stack: NewStack(), + } + if test.setup != nil { + runContext := tosca.NewMockRunContext(ctrl) + test.setup(runContext) + ctxt.context = runContext + } + ctxt.stack = fillStack(test.stack...) + ctxt.params.Recipient = address + + err := test.op(&ctxt) + if want, got := test.err, err; want != got { + t.Fatalf("unexpected return, wanted %v, got %v", want, got) + } + }) + } +} + +func TestGenericDataCopy_CopiesDataIntoMemoryAndPadsExcessWithZeroes(t *testing.T) { + + testBuffer := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + + ctxt := getEmptyContext() + ctxt.stack = fillStack( + *uint256.NewInt(0), // memory offset + *uint256.NewInt(0), // data offset + *uint256.NewInt(15), // size + ) + + err := genericDataCopy(&ctxt, testBuffer) + if err != nil { + t.Fatalf("genericDataCopy failed: %v", err) + } + + // 15 bytes read, expanded to 32 bytes: 0-14 are copied, 15-31 are zeroed. + expected := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 31: 0} + if want, got := expected[:], ctxt.memory.store; !bytes.Equal(want, got) { + t.Errorf("unexpected memory, wanted %v, got %v", want, got) + } +} + +func TestGenericDataCopy_ReturnsErrorOn(t *testing.T) { + testBuffer := make([]byte, 1024) + size := *uint256.NewInt(10) + + tests := map[string]struct { + memOffset uint256.Int + gas tosca.Gas + expectedError error + }{ + "not enough gas": { + memOffset: *uint256.NewInt(10), + gas: 1, + expectedError: errOutOfGas, + }, + + "expansion failure": { + memOffset: *uint256.NewInt(math.MaxUint64), + gas: 1 << 32, + expectedError: errOverflow, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + + ctxt := getEmptyContext() + ctxt.gas = test.gas + ctxt.stack = fillStack( + test.memOffset, + *uint256.NewInt(0), // data offset + size, + ) + + err := genericDataCopy(&ctxt, testBuffer) + if want, got := test.expectedError, err; want != got { + t.Fatalf("unexpected return, wanted %v, got %v", want, got) + } + }) + } +} + +func TestGetAccessCost_RespondsWithProperGasPrice(t *testing.T) { + if want, got := tosca.Gas(100), getAccessCost(tosca.WarmAccess); want != got { + t.Errorf("unexpected gas cost, wanted %d, got %d", want, got) + } + if want, got := tosca.Gas(2600), getAccessCost(tosca.ColdAccess); want != got { + t.Errorf("unexpected gas cost, wanted %d, got %d", want, got) + } +} + +func TestCall_ChargesNothingForColdAccessBeforeBerlin(t *testing.T) { + zero := *uint256.NewInt(0) + one := *uint256.NewInt(1) + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + runContext.EXPECT().Call(tosca.Call, gomock.Any()).Return(tosca.CallResult{}, nil) + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: tosca.R07_Istanbul, + }, + }, + stack: NewStack(), + memory: NewMemory(), + context: runContext, + gas: 0, + } + + ctxt.stack = fillStack(zero, one, zero, zero, zero, zero, zero, zero) + + err := genericCall(&ctxt, tosca.Call) + if err != nil { + t.Errorf("genericCall failed: %v", err) + } + if ctxt.gas != 0 { + t.Errorf("unexpected gas cost, wanted 0, got %v", ctxt.gas) + } +} + +func TestCall_ChargesForAccessAfterBerlin(t *testing.T) { + one := *uint256.NewInt(1) + zero := *uint256.NewInt(0) + for _, accessStatus := range []tosca.AccessStatus{tosca.WarmAccess, tosca.ColdAccess} { + + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + runContext.EXPECT().AccessAccount(one.Bytes20()).Return(accessStatus) + runContext.EXPECT().Call(tosca.Call, gomock.Any()).Return(tosca.CallResult{}, nil) + delta := tosca.Gas(1) + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: tosca.R09_Berlin, + }, + }, + stack: NewStack(), + memory: NewMemory(), + context: runContext, + gas: 2600 + delta, + } + ctxt.stack = fillStack(zero, one, zero, zero, zero, zero, zero, zero) + + err := genericCall(&ctxt, tosca.Call) + if err != nil { + t.Errorf("genericCall failed: %v", err) + } + + want := tosca.Gas(delta) + if accessStatus == tosca.WarmAccess { + want = 2500 + delta + } + if ctxt.gas != want { + t.Errorf("unexpected gas cost, wanted %v, got %v", want, ctxt.gas) + } + } +} + +func TestSelfDestruct_Refund(t *testing.T) { + tests := map[string]struct { + destructed bool + revision tosca.Revision + refund tosca.Gas + }{ + "istanbul": { + revision: tosca.R07_Istanbul, + }, + "berlin-first-destructed": { + destructed: true, + revision: tosca.R09_Berlin, + refund: 24_000, + }, + "berlin-not-first-destructed": { + revision: tosca.R09_Berlin, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + refund := selfDestructRefund(test.destructed, test.revision) + if refund != test.refund { + t.Errorf("unexpected refund, wanted %d, got %d", test.refund, refund) + } + }) + } +} + +func TestSelfDestruct_NewAccountCost(t *testing.T) { + + tests := map[string]struct { + beneficiaryEmpty bool + balance tosca.Value + cost tosca.Gas + }{ + "beneficiary empty no balance": { + beneficiaryEmpty: true, + balance: tosca.Value{}, + cost: 0, + }, + "beneficiary empty with balance": { + beneficiaryEmpty: true, + balance: tosca.Value{1}, + cost: 25_000, + }, + "beneficiary not empty without balance": { + beneficiaryEmpty: false, + balance: tosca.Value{}, + cost: 0, + }, + "beneficiary not empty with balance": { + beneficiaryEmpty: false, + balance: tosca.Value{1}, + cost: 0, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + cost := selfDestructNewAccountCost(test.beneficiaryEmpty, test.balance) + if cost != test.cost { + t.Errorf("unexpected gas, wanted %d, got %d", test.cost, cost) + } + }) + } +} + +func TestSelfDestruct_ExistingAccountToNewBeneficiary(t *testing.T) { + // This tests produces the combination of context calls/results for the maximum dynamic gas cost possible. + + beneficiaryAddress := tosca.Address{1} + selfAddress := tosca.Address{2} + // added to gas to ensure operation is not simply setting gas to zero. + gasDelta := tosca.Gas(1) + + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + runContext.EXPECT().AccessAccount(beneficiaryAddress).Return(tosca.ColdAccess) + runContext.EXPECT().GetBalance(beneficiaryAddress) + runContext.EXPECT().GetNonce(beneficiaryAddress) + runContext.EXPECT().GetCodeSize(beneficiaryAddress) + runContext.EXPECT().GetBalance(selfAddress).Return(tosca.Value{1}) + runContext.EXPECT().SelfDestruct(selfAddress, beneficiaryAddress).Return(true) + + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: tosca.R13_Cancun, + }, + Recipient: selfAddress, + }, + stack: NewStack(), + memory: NewMemory(), + context: runContext, + // 25_000 for new account, 2_600 for beneficiary access + gas: 27_600 + gasDelta, + } + ctxt.stack.push(new(uint256.Int).SetBytes(beneficiaryAddress[:])) + + status, err := opSelfdestruct(&ctxt) + if err != nil { + t.Fatalf("unexpected error, got %v", err) + } + if want, got := statusSelfDestructed, status; want != got { + t.Fatalf("unexpected status, wanted %v, got %v", want, got) + } + if ctxt.gas != gasDelta { + t.Errorf("unexpected remaining gas, wanted %v, got %d", gasDelta, ctxt.gas) + } +} + +func TestSelfDestruct_ProperlyReportsNotEnoughGas(t *testing.T) { + for _, beneficiaryAccess := range []tosca.AccessStatus{tosca.WarmAccess, tosca.ColdAccess} { + for _, accountEmpty := range []bool{true, false} { + t.Run(fmt.Sprintf("beneficiaryAccess:%v_accountEmpty:%v", beneficiaryAccess, accountEmpty), func(t *testing.T) { + beneficiaryAddress := tosca.Address{1} + selfAddress := tosca.Address{2} + + ctrl := gomock.NewController(t) + runContext := tosca.NewMockRunContext(ctrl) + runContext.EXPECT().AccessAccount(beneficiaryAddress).Return(beneficiaryAccess) + + if accountEmpty { + runContext.EXPECT().GetCodeSize(beneficiaryAddress).Return(1) + } else { + runContext.EXPECT().GetCodeSize(beneficiaryAddress).Return(0) + } + runContext.EXPECT().GetBalance(beneficiaryAddress).AnyTimes() + runContext.EXPECT().GetNonce(beneficiaryAddress).AnyTimes() + + runContext.EXPECT().GetBalance(selfAddress).Return(tosca.Value{1}) + + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: tosca.R13_Cancun, + }, + Recipient: selfAddress, + }, + stack: NewStack(), + memory: NewMemory(), + context: runContext, + } + if beneficiaryAccess == tosca.ColdAccess { + ctxt.gas += 2600 + } + if !accountEmpty { + ctxt.gas += 25000 + } + ctxt.gas -= 1 + + ctxt.stack.push(new(uint256.Int).SetBytes(beneficiaryAddress[:])) + + _, err := opSelfdestruct(&ctxt) + if err != errOutOfGas { + t.Fatalf("expected out of gas but got %v", err) + } + + }) + } + } +} + +func TestComputeCodeSizeCost(t *testing.T) { + if cost, err := computeCodeSizeCost(24576*2 + 1); err == nil || cost != 0 { + t.Errorf("check should have failed with size 49153 but did not. err %v, cost %v", err, cost) + } + if cost, err := computeCodeSizeCost(24576 * 2); err != nil || cost != 3072 { + t.Errorf("should not have failed with size 49152, err %v, cost %v", err, cost) + } +} + +func TestGenericCreate_ReportsErrors(t *testing.T) { + one := uint256.NewInt(1) + tests := map[string]struct { + offset, size uint256.Int + kind tosca.CallKind + revision tosca.Revision + expectedError error + }{ + "not enough gas for code size": { + offset: *one, + size: *uint256.NewInt(31), + revision: tosca.R12_Shanghai, + kind: tosca.Create, + expectedError: errOutOfGas, + }, + "gas not checked for max code size before shanghai": { + offset: *one, + size: *uint256.NewInt(31), + revision: tosca.R11_Paris, + kind: tosca.Create, + expectedError: nil, + }, + "not enough gas for create2 init code hashing": { + offset: *one, + size: *one, + kind: tosca.Create2, + expectedError: errOutOfGas, + }, + "does not charge init code hashing in create": { + offset: *one, + size: *one, + kind: tosca.Create, + expectedError: nil, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + mockRunContext := tosca.NewMockRunContext(gomock.NewController(t)) + mockRunContext.EXPECT().Call(gomock.Any(), gomock.Any()).Return(tosca.CallResult{}, nil).AnyTimes() + ctxt := getEmptyContext() + ctxt.context = mockRunContext + ctxt.params.Revision = test.revision + ctxt.gas = 3 + + ctxt.stack.push(uint256.NewInt(0)) // salt + ctxt.stack.push(&test.size) + ctxt.stack.push(&test.offset) + ctxt.stack.push(uint256.NewInt(0)) // value + + err := genericCreate(&ctxt, test.kind) + if err != test.expectedError { + t.Errorf("unexpected err. wanted %v, got %v", test.expectedError, err) + } + }) + } +} + +func TestGenericCreate_ResultIsWrittenToStack(t *testing.T) { + CreatedAddress := tosca.Address([20]byte{19: 0x1}) + for _, success := range []bool{true, false} { + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().Call(gomock.Any(), gomock.Any()).Return(tosca.CallResult{Success: success, CreatedAddress: CreatedAddress}, nil) + ctxt := getEmptyContext() + ctxt.context = runContext + ctxt.stack.push(uint256.NewInt(0)) + ctxt.stack.push(uint256.NewInt(0)) + ctxt.stack.push(uint256.NewInt(0)) + err := genericCreate(&ctxt, tosca.Create) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + want := uint256.NewInt(0) + if success { + want = new(uint256.Int).SetBytes(CreatedAddress[:]) + } + if got := ctxt.stack.peek(); !want.Eq(got) { + t.Errorf("unexpected return value, wanted %v, got %v", want, got) + } + } +} + +func TestOpEndWithResult_ReturnsExpectedState(t *testing.T) { + c := getEmptyContext() + c.stack.push(uint256.NewInt(1)) + c.stack.push(uint256.NewInt(1)) + c.memory.store = []byte{0x1, 0xff, 0x2} + + err := opEndWithResult(&c) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !bytes.Equal(c.returnData, []byte{0xff}) { + t.Errorf("unexpected return data, wanted %v, got %v", []byte{0x1}, c.returnData) + } +} + +func TestOpEndWithResult_ReportOverflow(t *testing.T) { + overflow64 := new(uint256.Int).Add(uint256.NewInt(math.MaxUint64), uint256.NewInt(math.MaxUint64)) + c := getEmptyContext() + c.stack.push(overflow64) + c.stack.push(overflow64) + c.memory.store = []byte{0x1, 0xff, 0x2} + err := opEndWithResult(&c) + if err != errOverflow { + t.Fatalf("should have produced overflow error, instead got: %v", err) + } +} + +func TestInstructions_EIP2929_staticGasCostIsZero(t *testing.T) { + ops := []OpCode{BALANCE, EXTCODECOPY, EXTCODEHASH, EXTCODESIZE, CALL, CALLCODE, DELEGATECALL, STATICCALL} + for _, op := range ops { + if getBerlinGasPriceInternal(op) != 0 { + t.Errorf("expected zero gas cost for %v", op) + } + } +} + +func TestInstructions_EIP2929_dynamicGasCostReportsOutOfGas(t *testing.T) { + type accessCost struct { + warm tosca.Gas + cold tosca.Gas + } + + var eip2929AccessCost = newOpCodePropertyMap(func(op OpCode) accessCost { + switch op { + case SLOAD: + return accessCost{warm: 100, cold: 2100} + case SSTORE: + return accessCost{warm: 100, cold: 2100 + 100} + } + return accessCost{warm: 100, cold: 2600} + }) + + tests := map[OpCode]func(*context) error{ + BALANCE: opBalance, + EXTCODECOPY: opExtCodeCopy, + EXTCODEHASH: opExtcodehash, + EXTCODESIZE: opExtcodesize, + CALL: opCall, + CALLCODE: opCallCode, + DELEGATECALL: opDelegateCall, + STATICCALL: opStaticCall, + SLOAD: opSload, + } + + for op, implementation := range tests { + for revision := tosca.R09_Berlin; revision <= newestSupportedRevision; revision++ { + for _, access := range []tosca.AccessStatus{tosca.WarmAccess, tosca.ColdAccess} { + t.Run(fmt.Sprintf("%v/%v/%v", op, revision, access), func(t *testing.T) { + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: revision, + }, + }, + stack: NewStack(), + memory: NewMemory(), + } + + accessCosts := eip2929AccessCost.get(op) + + ctxt.gas = accessCosts.warm - 1 + if access == tosca.ColdAccess { + ctxt.gas = accessCosts.cold - 1 + } + mockRunContext := tosca.NewMockRunContext(gomock.NewController(t)) + mockRunContext.EXPECT().AccessStorage(gomock.Any(), gomock.Any()).Return(access).AnyTimes() + mockRunContext.EXPECT().AccessAccount(gomock.Any()).Return(access).AnyTimes() + ctxt.context = mockRunContext + ctxt.stack.stackPointer = 7 + + err := implementation(&ctxt) + if err != errOutOfGas { + t.Errorf("unexpected error: %v", err) + } + }) + } + } + } +} + +func TestInstructions_EIP2929_SSTOREReportsOutOfGas(t *testing.T) { + // SSTORE needs to be tested on its own because it demands that at least 2300 gas are available. + // Hence we cannot take the same testing approach as for the other operations in EIP-2929. + + testGasValues := []tosca.Gas{ + 2300, //< SSTORE demands at least 2300 gas to be available + 2301, //< not enough to afford StorageAdded, StorageModified, or StorageDeleted. + } + + // dynamic gas check can only fail for the following storage status values + failsForDynamicGas := []tosca.StorageStatus{tosca.StorageAdded, tosca.StorageModified, tosca.StorageDeleted} + + for _, availableGas := range testGasValues { + for _, storageStatus := range failsForDynamicGas { + for revision := tosca.R09_Berlin; revision <= newestSupportedRevision; revision++ { + for _, access := range []tosca.AccessStatus{tosca.WarmAccess, tosca.ColdAccess} { + t.Run(fmt.Sprintf("%v/%v/%v/%v", SSTORE, revision, access, storageStatus), func(t *testing.T) { + + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: revision, + }, + }, + stack: NewStack(), + } + ctxt.gas = availableGas + mockRunContext := tosca.NewMockRunContext(gomock.NewController(t)) + mockRunContext.EXPECT().AccessStorage(gomock.Any(), gomock.Any()).Return(access).AnyTimes() + mockRunContext.EXPECT().SetStorage(gomock.Any(), gomock.Any(), gomock.Any()).Return(storageStatus).AnyTimes() + ctxt.context = mockRunContext + ctxt.stack.push(uint256.NewInt(1)) + ctxt.stack.push(uint256.NewInt(1)) + + err := opSstore(&ctxt) + if err != errOutOfGas { + t.Errorf("unexpected error: %v", err) + } + }) + } + } + } + } +} + +func TestInstructions_StorageOps_CallStorageContext(t *testing.T) { + address := tosca.Address{} + _, _ = rand.Read(address[:]) + key := tosca.Key{} + _, _ = rand.Read(key[:]) + value := tosca.Word{} + _, _ = rand.Read(value[:]) + + tests := map[OpCode]struct { + implementation func(*context) error + stack []uint256.Int + }{ + SLOAD: { + implementation: opSload, + stack: []uint256.Int{ + *new(uint256.Int).SetBytes(key[:]), + }, + }, + SSTORE: { + implementation: opSstore, + stack: []uint256.Int{ + *new(uint256.Int).SetBytes(key[:]), + *new(uint256.Int).SetBytes(value[:]), + }, + }, + } + + for op, test := range tests { + t.Run(op.String(), func(t *testing.T) { + forEachRevision(t, op, func(t *testing.T, revision tosca.Revision) { + + ctxt := getEmptyContext() + ctxt.params.Recipient = address + ctxt.params.Revision = revision + ctxt.stack = fillStack(test.stack...) + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + if revision >= tosca.R09_Berlin { + runContext.EXPECT().AccessStorage(address, key).Return(tosca.WarmAccess) + } + if op == SLOAD { + runContext.EXPECT().GetStorage(address, key).Return(value) + } + if op == SSTORE { + runContext.EXPECT().SetStorage(address, key, value) + } + ctxt.context = runContext + + err := test.implementation(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if op == SLOAD { + if got := ctxt.stack.peek(); got.Cmp(new(uint256.Int).SetBytes(value[:])) != 0 { + t.Errorf("unexpected return value, wanted %v, got %v", value, got) + } + } + }) + }) + } +} + +func TestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { + tests := map[OpCode]struct { + implementation func(*context) error + stack []uint64 + }{ + JUMP: { + implementation: opJump, + stack: []uint64{1}, + }, + JUMPI: { + implementation: opJumpi, + stack: []uint64{1, 1}, + }, + SWAP2_SWAP1_POP_JUMP: { + implementation: opSwap2_Swap1_Pop_Jump, + stack: []uint64{1, 1, 1}, + }, + POP_JUMP: { + implementation: opPop_Jump, + stack: []uint64{1, 1}, + }, + PUSH2_JUMP: { + implementation: opPush2_Jump, + stack: []uint64{1}, + }, + PUSH2_JUMPI: { + implementation: opPush2_Jumpi, + stack: []uint64{1}, + }, + ISZERO_PUSH2_JUMPI: { + implementation: opIsZero_Push2_Jumpi, + stack: []uint64{0}, + }, + } + + // test that all jump instructions are tested + for _, op := range allOpCodesWhere(isJump) { + if _, ok := tests[op]; !ok { + t.Fatalf("missing test for jump instruction %v", op) + } + } + + for op, test := range tests { + t.Run(op.String(), func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.code = Code{{op, 0}} + for _, v := range test.stack { + ctxt.stack.push(uint256.NewInt(v)) + } + + err := test.implementation(&ctxt) + if want, got := errInvalidJump, err; want != got { + t.Fatalf("unexpected error, wanted %v, got %v", want, got) + } + }) + } +} + +func TestInstructions_ConditionalJumpOpsIgnoreDestinationWhenJumpNotTaken(t *testing.T) { + zero := *uint256.NewInt(0) + one := *uint256.NewInt(1) + maxUint256 := *uint256.NewInt(0).Sub(uint256.NewInt(0), uint256.NewInt(1)) + + tests := map[OpCode]struct { + implementation func(*context) error + stack []uint256.Int + }{ + JUMPI: { + implementation: opJumpi, + // ignores destination, even if it would overflow + stack: []uint256.Int{maxUint256, zero}, + }, + PUSH2_JUMPI: { + implementation: opPush2_Jumpi, + stack: []uint256.Int{zero}, + }, + ISZERO_PUSH2_JUMPI: { + implementation: opIsZero_Push2_Jumpi, + stack: []uint256.Int{one}, + }, + } + + for op, test := range tests { + t.Run(op.String(), func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.code = Code{{op, 0}} + ctxt.stack = fillStack(test.stack...) + + err := test.implementation(&ctxt) + if want, got := error(nil), err; want != got { + t.Fatalf("unexpected error, wanted %v, got %v", want, got) + } + }) + } +} + +func TestInstructions_JumpOpsReturnErrorWithJumpDestinationOutOfBounds(t *testing.T) { + tests := map[OpCode]struct { + implementation func(*context) error + stack []uint256.Int + }{ + JUMP: { + implementation: opJump, + stack: []uint256.Int{ + *uint256.NewInt(math.MaxInt32 + 1), + }, + }, + JUMPI: { + implementation: opJumpi, + stack: []uint256.Int{ + *uint256.NewInt(math.MaxInt32 + 1), + *uint256.NewInt(1), + }, + }, + } + + for op, test := range tests { + t.Run(op.String(), func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.code = Code{{op, 0}} + ctxt.stack = fillStack(test.stack...) + + err := test.implementation(&ctxt) + if want, got := errInvalidJump, err; want != got { + t.Fatalf("unexpected error, wanted %v, got %v", want, got) + } + }) + } + +} + +func TestGetData(t *testing.T) { + + tests := map[string]struct { + data []byte + offset *uint256.Int + size uint64 + expectedResult []byte + }{ + "returns slice in bounds": { + data: []byte{0x00, 0x1, 0x2, 0x3, 0xFF}, + offset: uint256.NewInt(1), + size: 3, + expectedResult: []byte{0x1, 0x2, 0x3}, + }, + "returns empty slice when size is 0": { + data: []byte{}, + offset: uint256.NewInt(0), + size: 0, + expectedResult: nil, + }, + "adds zeroes right padding": { + data: []byte{0xFF}, + offset: uint256.NewInt(0), + size: 2, + expectedResult: []byte{0xFF, 0x0}, + }, + "reads beyond limit yield zeroes": { + data: []byte{0xFF, 0x1}, + offset: uint256.NewInt(12), + size: 2, + expectedResult: []byte{0x0, 0x0}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + res := getData(test.data, test.offset, test.size) + if want, got := test.expectedResult, res; !bytes.Equal(want, got) { + t.Errorf("unexpected data, wanted %v, got %v", want, got) + } + }) + } +} + +func TestGenericCall_ProperlyReportsErrors(t *testing.T) { + + one := uint256.NewInt(1) + u64overflow := new(uint256.Int).Add(uint256.NewInt(2), uint256.NewInt(math.MaxUint64)) + address := tosca.Address{1} + + tests := map[string]struct { + // stack order + retSize, retOffset, inSize, inOffset, value, provided_gas *uint256.Int + gas tosca.Gas + expectedError error + }{ + "input offset overflow": { + // size needs to be one, otherwise offset is ignored. + inSize: one, + inOffset: u64overflow, + expectedError: errOverflow, + }, + "return Size overflow": { + retSize: u64overflow, + expectedError: errOverflow, + }, + "input memory too big": { + inSize: uint256.NewInt(maxMemoryExpansionSize + 1), + expectedError: errMaxMemoryExpansionSize, + }, + "not enough gas for output memory expansion": { + retSize: one, + gas: 1, + expectedError: errOutOfGas, + }, + "not enough gas for access cost": { + gas: 99, + expectedError: errOutOfGas, + }, + "not enough gas for value transfer": { + value: one, + gas: 9099, // 9000 for value transfer, 100 for warm access cost + expectedError: errOutOfGas, + }, + "not enough gas for new account": { + value: one, + gas: 33099, // 25000 for new account, 9000 for value transfer, 100 for warm access cost + expectedError: errOutOfGas, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().AccessAccount(address).Return(tosca.WarmAccess).AnyTimes() + runContext.EXPECT().GetNonce(address).AnyTimes() + runContext.EXPECT().GetBalance(address).AnyTimes() + runContext.EXPECT().GetCodeSize(address).AnyTimes() + + ctxt := getEmptyContext() + ctxt.context = runContext + ctxt.params.Revision = tosca.R13_Cancun + ctxt.gas = test.gas + + getValueOrZeroOf := func(i *uint256.Int) *uint256.Int { + if i == nil { + return uint256.NewInt(0) + } + return i + } + + ctxt.stack.push(getValueOrZeroOf(test.retSize)) + ctxt.stack.push(getValueOrZeroOf(test.retOffset)) + ctxt.stack.push(getValueOrZeroOf(test.inSize)) + ctxt.stack.push(getValueOrZeroOf(test.inOffset)) + ctxt.stack.push(getValueOrZeroOf(test.value)) + ctxt.stack.push(uint256.NewInt(0).SetBytes20(address[:])) + ctxt.stack.push(getValueOrZeroOf(test.provided_gas)) + + err := genericCall(&ctxt, tosca.Call) + + if err != test.expectedError { + t.Errorf("unexpected status after call, wanted %v, got %v", test.expectedError, err) + } + }) + } +} + +func TestGenericCall_CallKindPropagatesStaticMode(t *testing.T) { + zero := *uint256.NewInt(0) + one := *uint256.NewInt(1) + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().Call(tosca.StaticCall, gomock.Any()).Return(tosca.CallResult{}, nil) + ctxt := getEmptyContext() + ctxt.context = runContext + ctxt.params.Static = true + ctxt.stack = fillStack(zero, one, zero, zero, zero, zero, zero) + + err := genericCall(&ctxt, tosca.Call) + if err != nil { + t.Errorf("unexpected error: %v", err) + } +} + +func TestGenericCall_ResultIsWrittenToStack(t *testing.T) { + zero := *uint256.NewInt(0) + one := *uint256.NewInt(1) + for _, success := range []bool{true, false} { + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().Call(tosca.Call, gomock.Any()).Return(tosca.CallResult{Success: success}, nil) + ctxt := getEmptyContext() + ctxt.context = runContext + ctxt.stack = fillStack(zero, one, zero, zero, zero, zero, zero) + _ = genericCall(&ctxt, tosca.Call) + want := uint256.NewInt(0) + if success { + want = uint256.NewInt(1) + } + if got := ctxt.stack.data[0]; !want.Eq(&got) { + t.Errorf("unexpected return value, wanted %v, got %v", want, got) + } + } +} + +func TestGenericCall_HandlesBigProvidedGasValues(t *testing.T) { + zero := *uint256.NewInt(0) + gas := tosca.Gas(50_000) // value big enough to cover all gas costs + tests := map[string]uint256.Int{ + "maxInt64-1": *uint256.NewInt(math.MaxInt64 - 1), + "maxInt64": *uint256.NewInt(math.MaxInt64), + "maxInt64+1": *uint256.NewInt(math.MaxInt64 + 1), + "maxUint64": *uint256.NewInt(math.MaxUint64), + } + + for name, providedGas := range tests { + t.Run(name, func(t *testing.T) { + nestedGas := tosca.Gas(gas - gas/64) + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().Call(tosca.Call, tosca.CallParameters{Gas: nestedGas}).Return(tosca.CallResult{}, nil) + ctxt := context{gas: gas} + ctxt.context = runContext + ctxt.stack = fillStack(providedGas, zero, zero, zero, zero, zero, zero) + + err := genericCall(&ctxt, tosca.Call) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + }) + } +} + +func TestGenericCall_ForwardsCallParamsDependingOnCallKind(t *testing.T) { + + zero := *uint256.NewInt(0) + sender := tosca.Address{1} + recipient := tosca.Address{2} + value := tosca.Value{3} + targetAddress := tosca.Address{4} + targetAddressU256 := *new(uint256.Int).SetBytes20(targetAddress[:]) + + tests := map[tosca.CallKind]struct { + sender, recipient tosca.Address + value tosca.Value + }{ + tosca.Call: { + sender: recipient, + recipient: targetAddress, + }, + tosca.StaticCall: { + sender: recipient, + recipient: targetAddress, + }, + tosca.DelegateCall: { + sender: sender, + recipient: recipient, + value: value, + }, + tosca.CallCode: { + sender: recipient, + recipient: recipient, + }, + } + + for kind, test := range tests { + t.Run(kind.String(), func(t *testing.T) { + + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + wantParams := tosca.CallParameters{ + Sender: test.sender, + Recipient: test.recipient, + Value: test.value, + CodeAddress: targetAddress, + } + runContext.EXPECT().Call(kind, wantParams).Return(tosca.CallResult{}, nil) + ctxt := getEmptyContext() + ctxt.context = runContext + ctxt.params.Sender = sender + ctxt.params.Recipient = recipient + ctxt.params.Value = value + + ctxt.stack = fillStack(zero, targetAddressU256, zero, zero, zero, zero, zero) + + err := genericCall(&ctxt, kind) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + }) + } +} + +func TestGenericCall_DelegationDesignationIsBilledOnlyInPrague(t *testing.T) { + targetAddress := tosca.Address{0x42} + delegationCode := append(tosca.Code{0xef, 0x01, 0x00}, targetAddress[:]...) + noDelegationCode := append(tosca.Code{0x00, 0x00, 0x00}, targetAddress[:]...) + zero := *uint256.NewInt(0) + tests := map[string]struct { + revision tosca.Revision + toAddressCode tosca.Code + access tosca.AccessStatus + gasForDelegation tosca.Gas + }{ + "pre prague, no delegation": { + revision: tosca.R13_Cancun, + toAddressCode: noDelegationCode, + gasForDelegation: 0, + }, + "pre prague, delegation": { + revision: tosca.R13_Cancun, + toAddressCode: delegationCode, + gasForDelegation: 0, + }, + "prague, no delegation": { + revision: tosca.R14_Prague, + toAddressCode: noDelegationCode, + gasForDelegation: 0, + }, + "prague, delegation warm": { + revision: tosca.R14_Prague, + toAddressCode: delegationCode, + access: tosca.WarmAccess, + gasForDelegation: 100, + }, + "prague, delegation cold": { + revision: tosca.R14_Prague, + toAddressCode: delegationCode, + access: tosca.ColdAccess, + gasForDelegation: 2600, + }, + } + + for _, kind := range []tosca.CallKind{tosca.Call, tosca.StaticCall, tosca.DelegateCall, tosca.CallCode} { + for name, test := range tests { + t.Run(kind.String()+"/"+name, func(t *testing.T) { + + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + // EIP-2929 call destination access cost + runContext.EXPECT().AccessAccount(tosca.Address{}).Return(tosca.WarmAccess) + + runContext.EXPECT().GetCode(gomock.Any()).Return(test.toAddressCode).MaxTimes(1) + runContext.EXPECT().AccessAccount(targetAddress).Return(test.access).MaxTimes(1) + runContext.EXPECT().Call(kind, gomock.Any()).Return(tosca.CallResult{}, nil) + + ctxt := getEmptyContext() + ctxt.context = runContext + ctxt.params.Revision = test.revision + ctxt.stack = fillStack(zero, zero, zero, zero, zero, zero, zero) + spareGas := tosca.Gas(100) + toAddressWarmAccessCost := tosca.Gas(100) + ctxt.gas = spareGas + toAddressWarmAccessCost + test.gasForDelegation + + err := genericCall(&ctxt, kind) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if ctxt.gas != spareGas { + t.Errorf("Delegation designation gas was not billed correctly: want %d, got %d", spareGas, ctxt.gas) + } + }) + } + } +} + +func TestGenericCall_DelegationDesignationInsufficientGas(t *testing.T) { + zero := *uint256.NewInt(0) + targetAddress := tosca.Address{0x42} + delegationCode := append(tosca.Code{0xef, 0x01, 0x00}, targetAddress[:]...) + + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + // EIP-2929 call destination access cost + runContext.EXPECT().AccessAccount(tosca.Address{}).Return(tosca.WarmAccess) + runContext.EXPECT().GetCode(gomock.Any()).Return(delegationCode) + runContext.EXPECT().AccessAccount(targetAddress).Return(tosca.WarmAccess) + + ctxt := getEmptyContext() + ctxt.context = runContext + ctxt.params.Revision = tosca.R14_Prague + ctxt.stack = fillStack(zero, zero, zero, zero, zero, zero, zero) + toAddressWarmAccessCost := tosca.Gas(100) + ctxt.gas = toAddressWarmAccessCost + 99 + + if err := genericCall(&ctxt, tosca.Call); !errors.Is(err, errOutOfGas) { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestInstructions_ComparisonAndShiftOperations(t *testing.T) { + + zero := *uint256.NewInt(0) + one := *uint256.NewInt(1) + two := *uint256.NewInt(2) + signedMinusOne := *uint256.NewInt(0).Sub(&zero, &one) + signedMinusTwo := *uint256.NewInt(0).Sub(&zero, &two) + u256 := *uint256.NewInt(256) + u257 := *uint256.NewInt(257) + + tests := map[string]struct { + opImplementation func(*context) + stackInputs *stack + expectedOutput uint256.Int + }{ + "isZero/true": { + opImplementation: opIszero, + stackInputs: fillStack(zero), + expectedOutput: one, + }, + "isZero/false": { + opImplementation: opIszero, + stackInputs: fillStack(one), + expectedOutput: zero, + }, + "eq/true": { + opImplementation: opEq, + stackInputs: fillStack(one, one), + expectedOutput: one, + }, + "eq/false": { + opImplementation: opEq, + stackInputs: fillStack(one, two), + expectedOutput: zero, + }, + "lt/true": { + opImplementation: opLt, + stackInputs: fillStack(one, two), + expectedOutput: one, + }, + "lt/false": { + opImplementation: opLt, + stackInputs: fillStack(one, one), + expectedOutput: zero, + }, + "gt/true": { + opImplementation: opGt, + stackInputs: fillStack(two, one), + expectedOutput: one, + }, + "gt/false": { + opImplementation: opGt, + stackInputs: fillStack(one, one), + expectedOutput: zero, + }, + "slt/true": { + opImplementation: opSlt, + stackInputs: fillStack(signedMinusOne, one), + expectedOutput: one, + }, + "slt/false": { + opImplementation: opSlt, + stackInputs: fillStack(one, one), + expectedOutput: zero, + }, + "sgt/true": { + opImplementation: opSgt, + stackInputs: fillStack(one, signedMinusOne), + expectedOutput: one, + }, + "sgt/false": { + opImplementation: opSgt, + stackInputs: fillStack(signedMinusOne, one), + expectedOutput: zero, + }, + "shr/under256": { + opImplementation: opShr, + stackInputs: fillStack(one, two), + expectedOutput: one, + }, + "shr/over256": { + opImplementation: opShr, + stackInputs: fillStack(u256, one), + expectedOutput: zero, + }, + "shl/under256": { + opImplementation: opShl, + stackInputs: fillStack(one, one), + expectedOutput: two, + }, + "shl/over256": { + opImplementation: opShl, + stackInputs: fillStack(u256, one), + expectedOutput: zero, + }, + "sar/under256": { + opImplementation: opSar, + stackInputs: fillStack(one, signedMinusTwo), + expectedOutput: signedMinusOne, + }, + "sar/over256/signed": { + opImplementation: opSar, + stackInputs: fillStack(u257, signedMinusOne), + expectedOutput: signedMinusOne, + }, + "sar/over256/unsigned": { + opImplementation: opSar, + stackInputs: fillStack(u257, one), + expectedOutput: zero, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctxt := context{ + stack: test.stackInputs, + } + + test.opImplementation(&ctxt) + result := ctxt.stack.pop() + if result.Cmp(&test.expectedOutput) != 0 { + t.Errorf("unexpected result, wanted %d, got %d", test.expectedOutput, result) + } + }) + } +} + +func TestInstructions_CLZRangeOfInputs(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + ctxt := context{ + stack: stack, + } + ctxt.params.Revision = tosca.R15_Osaka + + stack.push(uint256.NewInt(0)) + err := opClz(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result := ctxt.stack.pop(); result.Cmp(uint256.NewInt(256)) != 0 { + t.Errorf("unexpected result for 0, wanted 256, got %d", result) + } + + for i := range 256 { + stack.push(uint256.NewInt(1).Lsh(uint256.NewInt(1), uint(i))) + err = opClz(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if result := ctxt.stack.pop(); result.Cmp(uint256.NewInt(255-uint64(i))) != 0 { + t.Errorf("unexpected result for 1<<%d, wanted %d, got %d", i, 255-uint64(i), result) + } + } +} + +func TestInstructions_CLZReturnsErrorPreOsaka(t *testing.T) { + for _, revision := range tosca.GetAllKnownRevisions() { + stack := NewStack() + defer ReturnStack(stack) + + ctxt := context{ + stack: stack, + } + + stack.push(uint256.NewInt(0)) + ctxt.params.Revision = revision + + err := opClz(&ctxt) + if revision < tosca.R15_Osaka { + if err != errInvalidRevision { + t.Fatalf("unexpected error, wanted %v, got %v", errInvalidRevision, err) + } + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + } + } +} + +func TestInstructions_OpExtCodeCopy_CallsContextAndCopiesCodeSlice(t *testing.T) { + + code := []byte{0x1, 0x2, 0x3, 0x4} + address := tosca.Address{} + _, _ = rand.Read(address[:]) + var offset uint64 = 1 + var size uint64 = 2 + + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().GetCode(address).Return(code) + + ctxt := getEmptyContext() + ctxt.context = runContext + + ctxt.stack = fillStack( + *new(uint256.Int).SetBytes(address[:]), + *uint256.NewInt(0), // memOffset + *uint256.NewInt(offset), // codeOffset + *uint256.NewInt(size), // length + ) + + err := opExtCodeCopy(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if want, got := code[offset:offset+size], ctxt.memory.store[0:size]; !bytes.Equal(want, got) { + t.Errorf("unexpected memory, wanted %v, got %v", want, got) + } +} + +func TestInstructions_opExtcodesize_CallsContextAndWritesResultInStack(t *testing.T) { + + address := tosca.Address{} + _, _ = rand.Read(address[:]) + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().GetCodeSize(address).Return(1234) + + ctxt := getEmptyContext() + ctxt.context = runContext + + ctxt.stack.push(new(uint256.Int).SetBytes(address[:])) + + err := opExtcodesize(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if want, got := uint256.NewInt(1234), ctxt.stack.pop(); want.Cmp(got) != 0 { + t.Errorf("unexpected result, wanted %v, got %v", want, got) + } +} + +func TestOpBlockhash(t *testing.T) { + + hash := tosca.Hash{} + _, _ = rand.Read(hash[:]) + zeroHash := tosca.Hash{} + u64overflow := new(uint256.Int).Add(uint256.NewInt(2), uint256.NewInt(math.MaxUint64)) + + type testInput struct { + currentBlockNumber int64 + requestedBlockNumber *uint256.Int + } + + tests := map[string]struct { + expectedValue tosca.Hash + inputs map[string]testInput + }{ + "produces zero if requested block number is older than available history": { + expectedValue: zeroHash, + inputs: map[string]testInput{ + "default": { + currentBlockNumber: 1024, requestedBlockNumber: uint256.NewInt(36), + }, + }, + }, + "produces zero if requested block number is newer than history": { + expectedValue: zeroHash, + inputs: map[string]testInput{ + "current is not included in history": { + currentBlockNumber: 500, requestedBlockNumber: uint256.NewInt(500), + }, + "and history has less than 256 elements": { + currentBlockNumber: 35, requestedBlockNumber: uint256.NewInt(36), + }, + "and request overflows uint64": { + currentBlockNumber: 5000, requestedBlockNumber: u64overflow, + }, + }, + }, + "produces existing hash if requested is in history range": { + expectedValue: hash, + inputs: map[string]testInput{ + "default": {currentBlockNumber: 5000, requestedBlockNumber: uint256.NewInt(4990)}, + "and history has less than 256 elements": { + currentBlockNumber: 128, requestedBlockNumber: uint256.NewInt(16), + }, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + + for name, input := range test.inputs { + t.Run(name, func(t *testing.T) { + + ctxt := getEmptyContext() + ctxt.stack = fillStack(*input.requestedBlockNumber) + ctxt.params.BlockNumber = input.currentBlockNumber + + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + if hash == test.expectedValue { + runContext.EXPECT().GetBlockHash(gomock.Any()).Return(hash).AnyTimes() + } + ctxt.context = runContext + + opBlockhash(&ctxt) + + if want, got := new(uint256.Int).SetBytes(test.expectedValue[:]), ctxt.stack.pop(); want.Cmp(got) != 0 { + t.Errorf("unexpected result, wanted %v, got %v", want, got) + } + }) + } + }) + } +} +func TestInstructions_ReturnDataCopy_ReturnsErrorOn(t *testing.T) { + + zero := *uint256.NewInt(0) + one := *uint256.NewInt(1) + maxUint64 := *uint256.NewInt(math.MaxUint64) + uint64Overflow := *new(uint256.Int).Add(&maxUint64, uint256.NewInt(1)) + returnDataSize := uint64(10) + + tests := map[string]struct { + stack []uint256.Int // memoryOffset, dataOffset, length + }{ + "length overflow": { + stack: []uint256.Int{zero, one, uint64Overflow}, + }, + "dataOffset overflow": { + stack: []uint256.Int{zero, uint64Overflow, one}, + }, + "offset + length overflow": { + stack: []uint256.Int{zero, maxUint64, one}, + }, + "offset + length greater than returnData": { + stack: []uint256.Int{zero, zero, *uint256.NewInt(returnDataSize + 1)}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.stack = fillStack(test.stack...) + ctxt.returnData = make([]byte, returnDataSize) + + err := opReturnDataCopy(&ctxt) + if err != errOverflow { + t.Fatalf("expected overflow error, got %v", err) + } + }) + } +} + +func TestOpExp_ProducesCorrectResults(t *testing.T) { + ctxt := context{gas: tosca.Gas(uint256.NewInt(8).ByteLen() * 50)} + ctxt.stack = NewStack() + ctxt.stack.push(uint256.NewInt(8)) // exponent + ctxt.stack.push(uint256.NewInt(2)) // base + err := opExp(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + expected := uint256.NewInt(256) + if got := ctxt.stack.pop(); got.Cmp(expected) != 0 { + t.Errorf("unexpected result, wanted %v, got %v", expected, got) + } +} + +func TestOpExp_ReportsOutOfGas(t *testing.T) { + ctxt := context{gas: 3} + ctxt.stack = NewStack() + ctxt.stack.push(uint256.NewInt(256)) // exponent + ctxt.stack.push(uint256.NewInt(2)) // base + err := opExp(&ctxt) + if err != errOutOfGas { + t.Errorf("expected out of gas error, got %v", err) + } +} + +func TestInstructions_Sha3_ReportsOutOfGas(t *testing.T) { + tests := map[string]struct { + size uint64 + expectedError error + }{ + "memory expansion": { + size: 64, + expectedError: errOutOfGas, + }, + "dynamic gas price": { + size: 1, + expectedError: errOutOfGas, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctxt := context{gas: 3} + ctxt.memory = NewMemory() + ctxt.stack = NewStack() + ctxt.stack.push(uint256.NewInt(test.size)) + ctxt.stack.push(uint256.NewInt(0)) + err := opSha3(&ctxt) + if err != test.expectedError { + t.Fatalf("unexpected error, wanted %v, got %v", test.expectedError, err) + } + }) + } +} + +func TestInstructions_Sha3_WritesCorrectHashInStack(t *testing.T) { + + want := Keccak256([]byte{0}) + + for _, withShaCache := range []bool{true, false} { + t.Run(fmt.Sprintf("withShaCache:%v", withShaCache), func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.withShaCache = withShaCache + ctxt.stack.push(uint256.NewInt(1)) + ctxt.stack.push(uint256.NewInt(0)) + + err := opSha3(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + got := ctxt.stack.pop() + if !bytes.Equal(got.Bytes(), want[:]) { + t.Errorf("unexpected hash wanted %x, got %x", want, got.Bytes()) + } + }) + } +} + +func TestOpExtCodeHash_WritesHashOnStackIfAccountExists(t *testing.T) { + + tests := map[string]struct { + accountEmpty bool + }{ + "account empty": {accountEmpty: true}, + "account not empty": {accountEmpty: false}, + } + + hash := tosca.Hash{0x1, 0x2, 0x3} + address := tosca.Address{0x1} + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.stack = fillStack(*new(uint256.Int).SetBytes20(address[:])) + + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + + runContext.EXPECT().GetBalance(address).AnyTimes() + runContext.EXPECT().GetNonce(address).AnyTimes() + if test.accountEmpty { + runContext.EXPECT().GetCodeSize(address).Return(0) + } else { + runContext.EXPECT().GetCodeSize(address).Return(1) + } + + if !test.accountEmpty { + runContext.EXPECT().GetCodeHash(address).Return(hash) + } + ctxt.context = runContext + + err := opExtcodehash(&ctxt) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + want := hash[:] + if test.accountEmpty { + want = []byte{} + } + if got := ctxt.stack.pop().Bytes(); !bytes.Equal(want, got) { + t.Errorf("unexpected result, wanted %v, got %v", want, got) + } + }) + } +} + +func TestInstructions_opLog(t *testing.T) { + + tests := map[string]struct { + offset, size *uint256.Int + expectedError error + reduceGas uint64 + }{ + "returns error if memory expansion fails": { + offset: uint256.NewInt(math.MaxUint64), size: uint256.NewInt(1), + expectedError: errOverflow, + }, + "returns error if word gas cost is more than available gas": { + offset: uint256.NewInt(10), size: uint256.NewInt(128), + reduceGas: 1, + expectedError: errOutOfGas, + }, + "calls emitLog with recipient, defined order of topics and memory copy": { + offset: uint256.NewInt(1), size: uint256.NewInt(2), + }, + } + for name, test := range tests { + for n := 0; n < 4; n++ { + t.Run(fmt.Sprintf("%v/LOG%d", name, n), func(t *testing.T) { + + ctxt := getEmptyContext() + for i := n - 1; i >= 0; i-- { + ctxt.stack.push(uint256.NewInt(uint64(i))) + } + ctxt.stack.push(test.size) + ctxt.stack.push(test.offset) + ctxt.gas = tosca.Gas(test.size.Uint64()*8 - test.reduceGas) + ctxt.params.Recipient = tosca.Address{1} + // ignore the expansion error to focus on log operation + // this expansion is done to remove expansion costs (if any) and + // test word count cost only. + memoryContents := []byte{0, 1, 2, 3} + _ = ctxt.memory.set(uint256.NewInt(0), memoryContents, &context{gas: math.MaxInt64}) + + if test.expectedError == nil { + runContext := tosca.NewMockRunContext(gomock.NewController(t)) + runContext.EXPECT().EmitLog(gomock.Any()).Do(func(log tosca.Log) { + if want, got := ctxt.params.Recipient, log.Address; want != got { + t.Errorf("unexpected log address, wanted %v, got %v", want, got) + } + if want, got := n, len(log.Topics); want != got { + t.Errorf("unexpected number of topics, wanted %v, got %v", want, got) + } + + for i := n; i > n; i++ { + if want, got := tosca.Hash(uint256.NewInt(uint64(i)).Bytes32()), log.Topics[i]; want != got { + t.Errorf("unexpected topic #%d, wanted %v, got %v", i, want, got) + } + } + from := test.offset.Uint64() + to := from + test.size.Uint64() + if want, got := memoryContents[from:to], log.Data; !slices.Equal(want, got) { + t.Errorf("unexpected log data, wanted %v,got %v", want, got) + } + }) + ctxt.context = runContext + } + + err := opLog(&ctxt, n) + if want, got := test.expectedError, err; want != got { + t.Fatalf("unexpected error, wanted %v, got %v", want, got) + } + }) + } + } +} + +func TestInstructions_MCopy_DoesNothingWithSizeZero(t *testing.T) { + + data := [1024]byte{} + _, _ = rand.Read(data[:]) + + ctxt := getEmptyContext() + ctxt.params.Revision = tosca.R13_Cancun + ctxt.stack = fillStack( + *uint256.NewInt(2500), // destOffset + *uint256.NewInt(137), // offset + *uint256.NewInt(0)) // size + ctxt.gas = 0 + + err := ctxt.memory.set( + uint256.NewInt(0), + data[:], + &context{gas: 1 << 32}, + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + err = opMcopy(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if want, got := data[:], ctxt.memory.store; !bytes.Equal(want, got) { + t.Errorf("unexpected memory, wanted %v, got %v", want, got) + } +} + +func TestInstructions_MCopy_ReturnsErrorOnFailure(t *testing.T) { + + tests := map[string]struct { + destOffset, offset, size uint64 + expectedError error + gasRemoved uint64 + }{ + "returns error when failed read memory expansion": { + destOffset: 0, offset: math.MaxUint64, size: 1, + expectedError: errOverflow, + }, + "returns error when failed write memory expansion": { + destOffset: math.MaxUint64, offset: 0, size: 1, + expectedError: errOverflow, + }, + "returns error if gas is not enough for size": { + destOffset: 0, offset: 0, size: 128, + expectedError: errOutOfGas, + gasRemoved: 1, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.params.Revision = tosca.R13_Cancun + ctxt.stack = fillStack( + *uint256.NewInt(test.destOffset), + *uint256.NewInt(test.offset), + *uint256.NewInt(test.size), + ) + + // ignore memory setup errors, to focus on the mcopy operation + // expansion is done to accumulate memory cost and focus on the + // word count gas cost. + _ = ctxt.memory.expandMemory(test.destOffset, test.size, &context{gas: 1 << 32}) + _ = ctxt.memory.expandMemory(test.offset, test.size, &context{gas: 1 << 32}) + ctxt.gas = tosca.Gas(3*tosca.SizeInWords(test.size) - test.gasRemoved) + + err := opMcopy(&ctxt) + if err != test.expectedError { + t.Fatalf("unexpected error, wanted %v, got %v", test.expectedError, err) + } + }) + } +} + +func TestInstructions_MCopy_CopiesOverlappingRanges(t *testing.T) { + + ctxt := getEmptyContext() + ctxt.params.Revision = tosca.R13_Cancun + ctxt.stack = fillStack( + *uint256.NewInt(5), + *uint256.NewInt(1), + *uint256.NewInt(10)) + + err := ctxt.memory.set( + uint256.NewInt(0), + []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + &context{gas: 1 << 32}, + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + ctxt.gas = tosca.Gas(3 * tosca.SizeInWords(10)) + + err = opMcopy(&ctxt) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expected := [32]byte{0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9} + if want, got := expected[:], ctxt.memory.store; !bytes.Equal(want, got) { + t.Errorf("unexpected memory, wanted %v, got %v", want, got) + } +} + +func TestInstructions_ReturnDataCopy_ReturnsOutOfGas(t *testing.T) { + zero := *uint256.NewInt(0) + ctxt := context{gas: 3, stack: fillStack(zero, zero, *uint256.NewInt(65))} + err := opReturnDataCopy(&ctxt) + if err != errOutOfGas { + t.Fatalf("expected overflow error, got %v", err) + } +} + +func TestInstructions_ParseDelegationDesignation(t *testing.T) { + exampleAddress := tosca.Address{0x42, 0x42, 0x42, 0x42, 0x42} + tests := map[string]struct { + code tosca.Code + isDelegation bool + address tosca.Address + }{ + "delegation": { + code: append(tosca.Code{0xef, 0x01, 0x00}, exampleAddress[:]...), + isDelegation: true, + address: exampleAddress, + }, + "no delegation": { + code: append(tosca.Code{0xee, 0x01, 0x00}, exampleAddress[:]...), + isDelegation: false, + address: tosca.Address{}, + }, + "short code": { + code: append(tosca.Code{0xef, 0x01, 0x00}, exampleAddress[:len(exampleAddress)-1]...), + isDelegation: false, + address: tosca.Address{}, + }, + "long code": { + code: append(tosca.Code{0xef, 0x01, 0x00, 0x42}, exampleAddress[:]...), + isDelegation: false, + address: tosca.Address{}, + }, + "empty code": { + code: tosca.Code{}, + isDelegation: false, + address: tosca.Address{}, + }, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + address, isDelegation := parseDelegationDesignation(test.code) + if want, got := test.isDelegation, isDelegation; want != got { + t.Errorf("unexpected delegation flag, wanted %v, got %v", want, got) + } + if want, got := test.address, address; want != got { + t.Errorf("unexpected address, wanted %v, got %v", want, got) + } + }) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Helper functions + +// fillStack creates a new stack and pushes the given values onto it. +// function arguments interpret top of the stack as the rightmost argument. +func fillStack(values ...uint256.Int) *stack { + s := NewStack() + for i := len(values) - 1; i >= 0; i-- { + s.push(&values[i]) + } + return s +} diff --git a/go/interpreter/sfvm/interpreter.go b/go/interpreter/sfvm/interpreter.go new file mode 100644 index 00000000..39e9c452 --- /dev/null +++ b/go/interpreter/sfvm/interpreter.go @@ -0,0 +1,593 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + + "github.com/0xsoniclabs/tosca/go/tosca" +) + +//go:generate mockgen -source interpreter.go -destination interpreter_mock.go -package lfvm + +// status is enumeration of the execution state of an interpreter run. +type status byte + +const ( + statusRunning status = iota // < all fine, ops are processed + statusStopped // < execution stopped with a STOP + statusReverted // < execution stopped with a REVERT + statusReturned // < execution stopped with a RETURN + statusSelfDestructed // < execution stopped with a SELF-DESTRUCT + statusFailed // < execution stopped with a logic error +) + +// context is the execution environment of an interpreter run. It contains all +// the necessary state to execute a contract, including input parameters, the +// contract code, and internal execution state such as the program counter, +// stack, and memory. For each contract execution, a new context is created. +type context struct { + // Inputs + params tosca.Parameters + context tosca.RunContext + code Code // the contract code in LFVM format + + // Execution state + pc int32 + gas tosca.Gas + refund tosca.Gas + stack *stack + memory *Memory + + // Intermediate data + returnData []byte // < the result of the last nested contract call + + // Configuration flags + withShaCache bool +} + +// useGas reduces the gas level by the given amount. If the gas level drops +// below zero, the caller should stop the execution with an error status. The function +// returns true if sufficient gas was available and execution can continue, +// false otherwise. +func (c *context) useGas(amount tosca.Gas) error { + if c.gas < 0 || amount < 0 || c.gas < amount { + return errOutOfGas + } + c.gas -= amount + return nil +} + +// isAtLeast returns true if the interpreter is is running at least at the given +// revision or newer, false otherwise. +func (c *context) isAtLeast(revision tosca.Revision) bool { + return c.params.Revision >= revision +} + +// --- Interpreter --- + +type runner interface { + // run executes the contract code in the given context. + // It returns the status of the execution: + // - Any logical error in the contract execution shall return statusFailed. + // - error is reserved to return runtime errors, which are not valid states + // and may not be recoverable. + run(*context) (status, error) +} + +func run( + config config, + params tosca.Parameters, + code Code, +) (tosca.Result, error) { + // Don't bother with the execution if there's no code. + if len(code) == 0 { + return tosca.Result{ + Output: nil, + GasLeft: params.Gas, + Success: true, + }, nil + } + + // Set up execution context. + var ctxt = context{ + params: params, + context: params.Context, + gas: params.Gas, + stack: NewStack(), + memory: NewMemory(), + code: code, + withShaCache: config.WithShaCache, + } + defer ReturnStack(ctxt.stack) + + if config.runner == nil { + config.runner = vanillaRunner{} + } + status, err := config.runner.run(&ctxt) + if err != nil { + return tosca.Result{}, err + } + + return generateResult(status, &ctxt) +} + +func generateResult(status status, ctxt *context) (tosca.Result, error) { + // Handle return status + switch status { + case statusStopped, statusSelfDestructed: + return tosca.Result{ + Success: true, + GasLeft: ctxt.gas, + GasRefund: ctxt.refund, + }, nil + case statusReturned: + return tosca.Result{ + Success: true, + Output: ctxt.returnData, + GasLeft: ctxt.gas, + GasRefund: ctxt.refund, + }, nil + case statusReverted: + return tosca.Result{ + Success: false, + Output: ctxt.returnData, + GasLeft: ctxt.gas, + }, nil + case statusFailed: + return tosca.Result{ + Success: false, + }, nil + default: + return tosca.Result{}, fmt.Errorf("unexpected error in interpreter, unknown status: %v", status) + } +} + +// --- Runners --- + +// vanillaRunner is the default runner that executes the contract code without +// any additional features. +type vanillaRunner struct{} + +func (r vanillaRunner) run(c *context) (status, error) { + return execute(c, false), nil +} + +// --- Execution --- + +// execute runs the contract code in the given context. If oneStepOnly is true, +// only the instruction pointed to by the program counter will be executed. +// If the contract execution yields any execution violation (i.e. out of gas, +// stack underflow, etc), the function returns statusFailed +func execute(c *context, oneStepOnly bool) status { + status, error := steps(c, oneStepOnly) + if error != nil { + return statusFailed + } + return status +} + +// steps executes the contract code in the given context, +// If oneStepOnly is true, only the instruction pointed to by the program +// counter will be executed. +// steps returns the status of the execution and an error if the contract +// execution yields any execution violation (i.e. out of gas, stack underflow, etc). +func steps(c *context, oneStepOnly bool) (status, error) { + staticGasPrices := getStaticGasPrices(c.params.Revision) + + status := statusRunning + for status == statusRunning { + if int(c.pc) >= len(c.code) { + return statusStopped, nil + } + + op := c.code[c.pc].opcode + + // Check stack boundary for every instruction + if err := checkStackLimits(c.stack.len(), op); err != nil { + return status, err + } + + // Consume static gas price for instruction before execution + if err := c.useGas(staticGasPrices.get(op)); err != nil { + return status, err + } + + var err error + + // Execute instruction + switch op { + case POP: + opPop(c) + case PUSH0: + err = opPush0(c) + case PUSH1: + opPush1(c) + case PUSH2: + opPush2(c) + case PUSH3: + opPush3(c) + case PUSH4: + opPush4(c) + case PUSH5: + opPush(c, 5) + case PUSH31: + opPush(c, 31) + case PUSH32: + opPush32(c) + case JUMP: + err = opJump(c) + case JUMPDEST: + // nothing + case SWAP1: + opSwap(c, 1) + case SWAP2: + opSwap(c, 2) + case DUP3: + opDup(c, 3) + case AND: + opAnd(c) + case SWAP3: + opSwap(c, 3) + case JUMPI: + err = opJumpi(c) + case GT: + opGt(c) + case DUP4: + opDup(c, 4) + case DUP2: + opDup(c, 2) + case ISZERO: + opIszero(c) + case ADD: + opAdd(c) + case OR: + opOr(c) + case XOR: + opXor(c) + case NOT: + opNot(c) + case SUB: + opSub(c) + case MUL: + opMul(c) + case MULMOD: + opMulMod(c) + case DIV: + opDiv(c) + case SDIV: + opSDiv(c) + case MOD: + opMod(c) + case SMOD: + opSMod(c) + case ADDMOD: + opAddMod(c) + case EXP: + err = opExp(c) + case DUP5: + opDup(c, 5) + case DUP1: + opDup(c, 1) + case EQ: + opEq(c) + case PC: + opPc(c) + case CALLER: + opCaller(c) + case CALLDATALOAD: + opCallDataload(c) + case CALLDATASIZE: + opCallDatasize(c) + case CALLDATACOPY: + err = genericDataCopy(c, c.params.Input) + case MLOAD: + err = opMload(c) + case MSTORE: + err = opMstore(c) + case MSTORE8: + err = opMstore8(c) + case MSIZE: + opMsize(c) + case MCOPY: + err = opMcopy(c) + case LT: + opLt(c) + case SLT: + opSlt(c) + case SGT: + opSgt(c) + case SHR: + opShr(c) + case SHL: + opShl(c) + case SAR: + opSar(c) + case CLZ: + err = opClz(c) + case SIGNEXTEND: + opSignExtend(c) + case BYTE: + opByte(c) + case SHA3: + err = opSha3(c) + case CALLVALUE: + opCallvalue(c) + case PUSH6: + opPush(c, 6) + case PUSH7: + opPush(c, 7) + case PUSH8: + opPush(c, 8) + case PUSH9: + opPush(c, 9) + case PUSH10: + opPush(c, 10) + case PUSH11: + opPush(c, 11) + case PUSH12: + opPush(c, 12) + case PUSH13: + opPush(c, 13) + case PUSH14: + opPush(c, 14) + case PUSH15: + opPush(c, 15) + case PUSH16: + opPush(c, 16) + case PUSH17: + opPush(c, 17) + case PUSH18: + opPush(c, 18) + case PUSH19: + opPush(c, 19) + case PUSH20: + opPush(c, 20) + case PUSH21: + opPush(c, 21) + case PUSH22: + opPush(c, 22) + case PUSH23: + opPush(c, 23) + case PUSH24: + opPush(c, 24) + case PUSH25: + opPush(c, 25) + case PUSH26: + opPush(c, 26) + case PUSH27: + opPush(c, 27) + case PUSH28: + opPush(c, 28) + case PUSH29: + opPush(c, 29) + case PUSH30: + opPush(c, 30) + case SWAP4: + opSwap(c, 4) + case SWAP5: + opSwap(c, 5) + case SWAP6: + opSwap(c, 6) + case SWAP7: + opSwap(c, 7) + case SWAP8: + opSwap(c, 8) + case SWAP9: + opSwap(c, 9) + case SWAP10: + opSwap(c, 10) + case SWAP11: + opSwap(c, 11) + case SWAP12: + opSwap(c, 12) + case SWAP13: + opSwap(c, 13) + case SWAP14: + opSwap(c, 14) + case SWAP15: + opSwap(c, 15) + case SWAP16: + opSwap(c, 16) + case DUP6: + opDup(c, 6) + case DUP7: + opDup(c, 7) + case DUP8: + opDup(c, 8) + case DUP9: + opDup(c, 9) + case DUP10: + opDup(c, 10) + case DUP11: + opDup(c, 11) + case DUP12: + opDup(c, 12) + case DUP13: + opDup(c, 13) + case DUP14: + opDup(c, 14) + case DUP15: + opDup(c, 15) + case DUP16: + opDup(c, 16) + case RETURN: + err = opEndWithResult(c) + status = statusReturned + case REVERT: + status = statusReverted + err = opEndWithResult(c) + case JUMP_TO: + opJumpTo(c) + case SLOAD: + err = opSload(c) + case SSTORE: + err = opSstore(c) + case TLOAD: + err = opTload(c) + case TSTORE: + err = opTstore(c) + case CODESIZE: + opCodeSize(c) + case CODECOPY: + err = genericDataCopy(c, c.params.Code) + case EXTCODESIZE: + err = opExtcodesize(c) + case EXTCODEHASH: + err = opExtcodehash(c) + case EXTCODECOPY: + err = opExtCodeCopy(c) + case BALANCE: + err = opBalance(c) + case SELFBALANCE: + opSelfbalance(c) + case BASEFEE: + err = opBaseFee(c) + case BLOBHASH: + err = opBlobHash(c) + case BLOBBASEFEE: + err = opBlobBaseFee(c) + case SELFDESTRUCT: + status, err = opSelfdestruct(c) + case CHAINID: + opChainId(c) + case GAS: + opGas(c) + case PREVRANDAO: + opPrevRandao(c) + case TIMESTAMP: + opTimestamp(c) + case NUMBER: + opNumber(c) + case GASLIMIT: + opGasLimit(c) + case GASPRICE: + opGasPrice(c) + case CALL: + err = opCall(c) + case CALLCODE: + err = opCallCode(c) + case STATICCALL: + err = opStaticCall(c) + case DELEGATECALL: + err = opDelegateCall(c) + case RETURNDATASIZE: + opReturnDataSize(c) + case RETURNDATACOPY: + err = opReturnDataCopy(c) + case BLOCKHASH: + opBlockhash(c) + case COINBASE: + opCoinbase(c) + case ORIGIN: + opOrigin(c) + case ADDRESS: + opAddress(c) + case STOP: + status = opStop() + case CREATE: + err = genericCreate(c, tosca.Create) + case CREATE2: + err = genericCreate(c, tosca.Create2) + case LOG0: + err = opLog(c, 0) + case LOG1: + err = opLog(c, 1) + case LOG2: + err = opLog(c, 2) + case LOG3: + err = opLog(c, 3) + case LOG4: + err = opLog(c, 4) + // --- Super Instructions --- + case SWAP2_SWAP1_POP_JUMP: + err = opSwap2_Swap1_Pop_Jump(c) + case SWAP1_POP_SWAP2_SWAP1: + opSwap1_Pop_Swap2_Swap1(c) + case POP_SWAP2_SWAP1_POP: + opPop_Swap2_Swap1_Pop(c) + case POP_POP: + opPopPop(c) + case PUSH1_SHL: + opPush1_Shl(c) + case PUSH1_ADD: + opPush1_Add(c) + case PUSH1_DUP1: + opPush1_Dup1(c) + case PUSH2_JUMP: + err = opPush2_Jump(c) + case PUSH2_JUMPI: + err = opPush2_Jumpi(c) + case PUSH1_PUSH1: + opPush1_Push1(c) + case SWAP1_POP: + opSwap1_Pop(c) + case POP_JUMP: + err = opPop_Jump(c) + case SWAP2_SWAP1: + opSwap2_Swap1(c) + case SWAP2_POP: + opSwap2_Pop(c) + case DUP2_MSTORE: + err = opDup2_Mstore(c) + case DUP2_LT: + opDup2_Lt(c) + case ISZERO_PUSH2_JUMPI: + err = opIsZero_Push2_Jumpi(c) + case PUSH1_PUSH4_DUP3: + opPush1_Push4_Dup3(c) + case AND_SWAP1_POP_SWAP2_SWAP1: + opAnd_Swap1_Pop_Swap2_Swap1(c) + case PUSH1_PUSH1_PUSH1_SHL_SUB: + opPush1_Push1_Push1_Shl_Sub(c) + default: + err = errInvalidOpCode + } + + if err != nil { + return status, err + } + + c.pc++ + + if oneStepOnly { + return status, nil + } + } + return status, nil +} + +// checkStackLimits checks that the opCode will not make an out of bounds access +// with the current stack size. +func checkStackLimits(stackLen int, op OpCode) error { + limits := _precomputedStackLimits.get(op) + if stackLen < limits.min { + return errStackUnderflow + } + if stackLen > limits.max { + return errStackOverflow + } + return nil +} + +// stackLimits defines the stack usage of a single OpCode. +type stackLimits struct { + min int // The minimum stack size required by an OpCode. + max int // The maximum stack size allowed before running an OpCode. +} + +var _precomputedStackLimits = newOpCodePropertyMap(func(op OpCode) stackLimits { + usage := computeStackUsage(op) + return stackLimits{ + min: -usage.from, + max: maxStackSize - usage.to, + } +}) diff --git a/go/interpreter/sfvm/interpreter_mock.go b/go/interpreter/sfvm/interpreter_mock.go new file mode 100644 index 00000000..944ff591 --- /dev/null +++ b/go/interpreter/sfvm/interpreter_mock.go @@ -0,0 +1,64 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +// Code generated by MockGen. DO NOT EDIT. +// Source: interpreter.go +// +// Generated by this command: +// +// mockgen -source interpreter.go -destination interpreter_mock.go -package lfvm +// + +// Package lfvm is a generated GoMock package. +package lfvm + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// Mockrunner is a mock of runner interface. +type Mockrunner struct { + ctrl *gomock.Controller + recorder *MockrunnerMockRecorder +} + +// MockrunnerMockRecorder is the mock recorder for Mockrunner. +type MockrunnerMockRecorder struct { + mock *Mockrunner +} + +// NewMockrunner creates a new mock instance. +func NewMockrunner(ctrl *gomock.Controller) *Mockrunner { + mock := &Mockrunner{ctrl: ctrl} + mock.recorder = &MockrunnerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *Mockrunner) EXPECT() *MockrunnerMockRecorder { + return m.recorder +} + +// run mocks base method. +func (m *Mockrunner) run(arg0 *context) (status, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "run", arg0) + ret0, _ := ret[0].(status) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// run indicates an expected call of run. +func (mr *MockrunnerMockRecorder) run(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "run", reflect.TypeOf((*Mockrunner)(nil).run), arg0) +} diff --git a/go/interpreter/sfvm/interpreter_test.go b/go/interpreter/sfvm/interpreter_test.go new file mode 100644 index 00000000..73cf080a --- /dev/null +++ b/go/interpreter/sfvm/interpreter_test.go @@ -0,0 +1,839 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "encoding/hex" + "errors" + "fmt" + "io" + "log" + "os" + "reflect" + "regexp" + "slices" + "strings" + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/holiman/uint256" + "go.uber.org/mock/gomock" +) + +func TestContext_useGas_ReturnsErrorIfOutOfGasOrNegativeCost(t *testing.T) { + tests := map[string]struct { + available tosca.Gas + required tosca.Gas + }{ + "no available gas and no gas required": {0, 0}, + "no available gas": {0, 100}, + "no available gas and infinite need": {0, -1}, + "gas available and infinite need": {100, -100}, + "gas available with no need": {100, 0}, + "sufficient gas": {100, 10}, + "insufficient gas": {10, 100}, + "all gas": {100, 100}, + "almost all gas": {100, 99}, + "one unit too much": {100, 101}, + "negative available gas": {-100, 100}, + "negative available gas with infinite need": {-100, -100}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctx := context{ + gas: test.available, + } + err := ctx.useGas(test.required) + + // Check that the result of UseGas indicates whether there was + // enough gas. + want := test.required >= 0 && test.available >= test.required + success := err == nil + if want != success { + t.Errorf("expected UseGas to return %v, got %v", want, success) + } + }) + } +} + +func TestContext_isAtLeast_RespectsOrderOfRevisions(t *testing.T) { + revisions := []tosca.Revision{ + tosca.R07_Istanbul, + tosca.R09_Berlin, + tosca.R10_London, + tosca.R11_Paris, + tosca.R12_Shanghai, + tosca.R13_Cancun, + tosca.R14_Prague, + tosca.R15_Osaka, + } + + for _, is := range revisions { + context := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: is, + }, + }, + } + + for _, trg := range revisions { + if want, got := is >= trg, context.isAtLeast(trg); want != got { + t.Errorf("revision %v should be at least %v: %t, got %t", is, trg, want, got) + } + } + } + +} + +type example struct { + code []byte // Some contract code + function uint32 // The identifier of the function in the contract to be called +} + +const MAX_STACK_SIZE int = 1024 +const GAS_START tosca.Gas = 1 << 32 + +func getEmptyContext() context { + code := make([]Instruction, 0) + data := make([]byte, 0) + return getContext(code, data, nil, 0, GAS_START, tosca.R07_Istanbul) +} + +func getContext(code Code, data []byte, runContext tosca.RunContext, stackPtr int, gas tosca.Gas, revision tosca.Revision) context { + + // Create execution context. + ctxt := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: revision, + }, + Gas: gas, + Input: data, + }, + context: runContext, + gas: gas, + stack: NewStack(), + memory: NewMemory(), + code: code, + } + + // Move the stack pointer to the required hight. + // For the tests using the resulting context the actual + // stack content is not relevant. It is merely used for + // checking stack over- or under-flows. + ctxt.stack.stackPointer = stackPtr + + return ctxt +} + +func TestInterpreter_step_DetectsLowerStackLimitViolation(t *testing.T) { + // Add tests for execution + + for _, op := range allOpCodes() { + + usage := computeStackUsage(op) + if usage.from >= 0 { + continue + } + + ctxt := getEmptyContext() + ctxt.code = []Instruction{{op, 0}} + + _, err := steps(&ctxt, false) + if want, got := errStackUnderflow, err; want != got { + t.Errorf("expected stack-underflow for %v to be detected, got %v", op, got) + } + } +} + +func TestInterpreter_step_DetectsUpperStackLimitViolation(t *testing.T) { + // Add tests for execution + for _, op := range allOpCodes() { + // Ignore operations that do not need any data on the stack. + usage := computeStackUsage(op) + if usage.to <= 0 { + continue + } + + ctxt := getEmptyContext() + ctxt.code = []Instruction{{op, 0}} + ctxt.stack.stackPointer = maxStackSize + + _, err := steps(&ctxt, false) + if want, got := errStackOverflow, err; want != got { + t.Errorf("expected stack-underflow for %v to be detected, got %v", op, got) + } + } +} + +func TestInterpreter_CanDispatchExecutableInstructions(t *testing.T) { + + for _, op := range allOpCodesWhere(isExecutable) { + t.Run(op.String(), func(t *testing.T) { + forEachRevision(t, op, func(t *testing.T, revision tosca.Revision) { + + ctrl := gomock.NewController(t) + mock := tosca.NewMockRunContext(ctrl) + // mock all to satisfy any instruction + mock.EXPECT().AccessAccount(gomock.Any()).Return(tosca.WarmAccess).AnyTimes() + mock.EXPECT().GetBalance(gomock.Any()).AnyTimes() + mock.EXPECT().GetNonce(gomock.Any()).AnyTimes() + mock.EXPECT().GetCodeSize(gomock.Any()).AnyTimes() + mock.EXPECT().GetCode(gomock.Any()).AnyTimes() + mock.EXPECT().AccountExists(gomock.Any()).AnyTimes() + mock.EXPECT().AccessStorage(gomock.Any(), gomock.Any()).AnyTimes() + mock.EXPECT().GetStorage(gomock.Any(), gomock.Any()).AnyTimes() + mock.EXPECT().SetStorage(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + mock.EXPECT().Call(gomock.Any(), gomock.Any()).AnyTimes() + mock.EXPECT().EmitLog(gomock.Any()).AnyTimes() + mock.EXPECT().SelfDestruct(gomock.Any(), gomock.Any()).AnyTimes() + mock.EXPECT().GetTransientStorage(gomock.Any(), gomock.Any()).AnyTimes() + mock.EXPECT().SetTransientStorage(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + ctx := context{ + params: tosca.Parameters{ + BlockParameters: tosca.BlockParameters{ + Revision: revision, + }, + }, + context: mock, + // enough gas to satisfy any instruction + gas: 1 << 32, + stack: NewStack(), + memory: NewMemory(), + code: generateCodeFor(op), + } + err := fillStackFor(op, ctx.stack, ctx.code) + if err != nil { + t.Fatalf("unexpected creating stack: %v", err) + } + + _, err = vanillaRunner{}.run(&ctx) + if err != nil { + t.Errorf("execution failed: %v", err) + } + }) + }) + } +} + +func TestInterpreter_ExecutionTerminates(t *testing.T) { + + tests := map[string]struct { + code []Instruction + }{ + "empty code": {code: []Instruction{}}, + "single stop": {code: []Instruction{{STOP, 0}}}, + "pc bigger than code": {code: []Instruction{{PUSH1, 0}}}, + "revert": {code: []Instruction{{REVERT, 0}}}, + "return": {code: []Instruction{{RETURN, 0}}}, + "selfdestruct": {code: []Instruction{{SELFDESTRUCT, 0}}}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + + ctxt := getEmptyContext() + ctxt.code = test.code + ctxt.stack.push(uint256.NewInt(1)) + ctxt.stack.push(uint256.NewInt(2)) + ctxt.stack.push(uint256.NewInt(3)) + // runcontext is needed for selfdestruct + mockContext := tosca.NewMockRunContext(gomock.NewController(t)) + mockContext.EXPECT().GetBalance(gomock.Any()).Return(tosca.Value{1}).AnyTimes() + mockContext.EXPECT().GetNonce(gomock.Any()).Return(uint64(1)).AnyTimes() + mockContext.EXPECT().SelfDestruct(gomock.Any(), gomock.Any()).Return(true).AnyTimes() + ctxt.context = mockContext + + status, err := steps(&ctxt, false) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if status == statusRunning { + t.Errorf("failed to terminate execution, status is %v", status) + } + }) + } +} + +func TestInterpreter_Vanilla_RunsWithoutOutput(t *testing.T) { + + code := []Instruction{ + {PUSH1, 1}, + {STOP, 0}, + } + + params := tosca.Parameters{ + Input: []byte{}, + Static: true, + Gas: 10, + Code: []byte{0x0}, + } + + // redirect stdout + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + // Run testing code + _, err := run(config{}, params, code) + // read the output + _ = w.Close() // ignore error in test + out, _ := io.ReadAll(r) + os.Stdout = old + + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if len(string(out)) != 0 { + t.Errorf("unexpected output: want \"\", got %v", string(out)) + } +} + +func TestInterpreter_EmptyCodeBypassesRunnerAndSucceeds(t *testing.T) { + code := []Instruction{} + params := tosca.Parameters{} + config := config{ + runner: NewMockrunner(gomock.NewController(t)), + } + + result, err := run(config, params, code) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if !result.Success { + t.Errorf("unexpected result: want success, got %v", result.Success) + } +} + +func TestInterpreter_run_ReturnsErrorOnRuntimeError(t *testing.T) { + + runner := NewMockrunner(gomock.NewController(t)) + code := []Instruction{{JUMPDEST, 0}} + params := tosca.Parameters{Gas: 20} + config := config{runner: runner} + + expectedError := fmt.Errorf("runtime error") + + runner.EXPECT().run(gomock.Any()).Return(statusFailed, expectedError) + + _, err := run(config, params, code) + if !errors.Is(err, expectedError) { + t.Errorf("unexpected error: %v", err) + } +} + +func TestRun_GenerateResult(t *testing.T) { + + baseOutput := []byte{0x1, 0x2, 0x3} + baseGas := tosca.Gas(2) + baseRefund := tosca.Gas(3) + + tests := map[string]struct { + status status + expectedErr error + expectedResult tosca.Result + }{ + "returned": { + status: statusReturned, + expectedResult: tosca.Result{ + Success: true, + Output: baseOutput, + GasLeft: baseGas, + GasRefund: baseRefund, + }, + }, + "reverted": { + status: statusReverted, + expectedResult: tosca.Result{ + Success: false, + Output: baseOutput, + GasLeft: baseGas, + GasRefund: 0, + }, + }, + "stopped": { + status: statusStopped, + expectedResult: tosca.Result{ + Success: true, + Output: nil, + GasLeft: baseGas, + GasRefund: baseRefund, + }, + }, + "suicide": { + status: statusSelfDestructed, + expectedResult: tosca.Result{ + Success: true, + Output: nil, + GasLeft: baseGas, + GasRefund: baseRefund, + }, + }, + "failure": { + status: statusFailed, + expectedResult: tosca.Result{ + Success: false, + }, + }, + "unknown status": { + status: statusFailed + 1, + expectedErr: fmt.Errorf("unexpected error in interpreter, unknown status: %v", statusFailed+1), + expectedResult: tosca.Result{}, + }, + } + + for name, test := range tests { + t.Run(fmt.Sprintf("%v", name), func(t *testing.T) { + + ctxt := context{} + ctxt.refund = baseRefund + ctxt.gas = baseGas + ctxt.returnData = bytes.Clone(baseOutput) + + res, err := generateResult(test.status, &ctxt) + + if test.expectedErr != nil && strings.Compare(err.Error(), test.expectedErr.Error()) != 0 { + t.Errorf("unexpected error: want \"%v\", got \"%v\"", test.expectedErr, err) + } + if !reflect.DeepEqual(res, test.expectedResult) { + t.Errorf("unexpected result: want %v, got %v", test.expectedResult, res) + } + }) + } +} + +func TestStepsProperlyHandlesJUMP_TO(t *testing.T) { + ctxt := getEmptyContext() + instructions := []Instruction{ + {JUMP_TO, 0x02}, + {RETURN, 0}, + {STOP, 0}, + } + + ctxt.params = tosca.Parameters{ + Input: []byte{}, + Static: false, + Gas: 10, + Code: []byte{0x0}, + } + ctxt.code = instructions + + status, err := steps(&ctxt, false) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if status != statusStopped { + t.Errorf("unexpected status: want STOPPED, got %v", status) + } +} + +func TestSteps_DetectsNonExecutableCode(t *testing.T) { + + nonExecutableOpCodes := []OpCode{ + INVALID, + NOOP, + DATA, + } + undefinedOpCodeRegex := regexp.MustCompile(`^op\(0x[0-9a-fA-F]+\)`) + isUndefined := + func(op OpCode) bool { + return undefinedOpCodeRegex.MatchString(op.String()) + } + nonExecutableOpCodes = append(nonExecutableOpCodes, allOpCodesWhere(isUndefined)...) + + for _, opCode := range nonExecutableOpCodes { + t.Run(opCode.String(), func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.code = []Instruction{{opCode, 0}} + + _, err := steps(&ctxt, false) + if want, got := errInvalidOpCode, err; want != got { + t.Errorf("unexpected error: want %v, got %v", want, got) + } + }) + } +} + +func TestSteps_StaticContextViolation(t *testing.T) { + tests := []struct { + op OpCode + stack []uint256.Int + minRevision tosca.Revision + }{ + {op: SSTORE}, + {op: LOG0}, + {op: LOG1}, + {op: LOG2}, + {op: LOG3}, + {op: LOG4}, + {op: CREATE}, + {op: CREATE2}, + {op: SELFDESTRUCT}, + { + op: TSTORE, + minRevision: tosca.R13_Cancun, + }, + { + op: CALL, + stack: []uint256.Int{ + {}, {}, {}, {}, + *uint256.NewInt(1), // value != 0: static violation + {}, {}, + }, + }, + } + + for _, test := range tests { + t.Run(test.op.String(), func(t *testing.T) { + ctxt := getEmptyContext() + // Get tosca.Parameters + ctxt.params = tosca.Parameters{ + Input: []byte{}, + Static: true, + Gas: 10, + Code: []byte{0x0}, + } + ctxt.code = []Instruction{{test.op, 0}} + ctxt.params.Revision = test.minRevision + + if len(test.stack) == 0 { + // add enough stack elements to pass stack bounds check + ctxt.stack.stackPointer = 50 + } else { + // otherwise prefill the stack with provided data + copy(ctxt.stack.data[:len(test.stack)], test.stack) + ctxt.stack.stackPointer = len(test.stack) + } + + _, err := steps(&ctxt, false) + if want, got := errStaticContextViolation, err; want != got { + t.Errorf("unexpected error: want %v, got %v", want, got) + } + }) + } +} + +func TestSteps_FailsWithLessGasThanStaticCost(t *testing.T) { + + for _, op := range allOpCodes() { + t.Run(op.String(), func(t *testing.T) { + forEachRevision(t, op, func(t *testing.T, revision tosca.Revision) { + + expectedGas := getStaticGasPrices(revision).get(op) + if expectedGas == 0 { + t.Skip("operation has static cost zero") + } + + ctxt := getEmptyContext() + ctxt.code = []Instruction{{op, 0}} + ctxt.stack.stackPointer = 20 + ctxt.gas = expectedGas - 1 + + _, err := steps(&ctxt, false) + if want, got := errOutOfGas, err; want != got { + t.Errorf("unexpected error: want %v, got %v", want, got) + } + }) + }) + } +} + +func TestInterpreter_InstructionsFailWhenExecutedInRevisionsEarlierThanIntroducedIn(t *testing.T) { + for _, op := range allOpCodes() { + introducedIn := _introducedIn.get(op) + for revision := tosca.R07_Istanbul; revision < introducedIn; revision++ { + t.Run(fmt.Sprintf("%v/%v", op, revision), func(t *testing.T) { + ctxt := getEmptyContext() + ctxt.code = []Instruction{{op, 0}} + ctxt.params.Revision = revision + ctxt.stack.stackPointer = 20 + + _, err := steps(&ctxt, false) + if want, got := errInvalidRevision, err; want != got { + t.Errorf("unexpected error: want %v, got %v", want, got) + } + }) + } + } +} + +func TestInterpreter_ExecuteReturnsFailureOnExecutionError(t *testing.T) { + + ctxt := context{ + code: generateCodeFor(INVALID), + stack: NewStack(), + } + + status := execute(&ctxt, false) + if want, got := statusFailed, status; want != got { + t.Errorf("unexpected status: want %v, got %v", want, got) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Benchmarks + +func BenchmarkFib10(b *testing.B) { + benchmarkFib(b, 10, false) +} + +func BenchmarkFib10_SI(b *testing.B) { + benchmarkFib(b, 10, true) +} + +func BenchmarkSatisfiesStackRequirements(b *testing.B) { + context := &context{ + stack: NewStack(), + } + + opCodes := allOpCodes() + for i := 0; i < b.N; i++ { + _ = checkStackLimits(context.stack.len(), opCodes[i%len(opCodes)]) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// test utilities + +func Test_generateCodeForOps(t *testing.T) { + tests := map[OpCode]int{ + PUSH1: 1, + PUSH2: 1, + PUSH3: 2, + PUSH4: 2, + PUSH5: 3, + PUSH6: 3, + PUSH31: 16, + PUSH32: 16, + PUSH1_PUSH1: 1, + PUSH1_PUSH4_DUP3: 3, + PUSH1_PUSH1_PUSH1_SHL_SUB: 2, + } + for op, test := range tests { + t.Run(op.String(), func(t *testing.T) { + code := generateCodeFor(op) + if want, got := test, len(code); want != got { + t.Errorf("expected %d instructions, got %d", want, got) + } + }) + } +} + +// generateCodeFor generates valid LFVM code for one instruction. +// Appends necessary DATA instructions to the code to satisfy stack requirements. +// Adds JUMPDEST instruction after JUMP instructions. +func generateCodeFor(op OpCode) Code { + + var code []Instruction + + switch op { + case PUSH1_PUSH4_DUP3: + code = append(code, Instruction{op, 0}, Instruction{DATA, 0}) + case PUSH1_PUSH1_PUSH1_SHL_SUB: + code = append(code, Instruction{op, 0}, Instruction{DATA, 0}) + case PUSH2_JUMP: + code = append(code, Instruction{op, 1}) // hardcoded jump destination + case PUSH2_JUMPI: + code = append(code, Instruction{op, 1}) // hardcoded jump destination + default: + code = append(code, Instruction{op, 0}) + } + + for _, op := range append(op.decompose(), op) { + if PUSH3 <= op && op <= PUSH32 { + n := int(op) - int(PUSH3) + 3 + numInstructions := n/2 + n%2 + for i := 0; i < numInstructions-1; i++ { + code = append(code, Instruction{DATA, 0}) + } + } + } + + if isJump(op) { + code = append(code, Instruction{JUMPDEST, 0}) + } + + if op == JUMP_TO { + // prevent endless loop by having jump to itself + code[0].arg = uint16(len(code)) + code = append(code, Instruction{JUMPDEST, 0}) + } + + return code +} + +// fillStackFor fills the stack with the required number of elements for the given opcode. +// For Jump instructions, it also encodes the PC for the the first jump destination found in code +func fillStackFor(op OpCode, stack *stack, code Code) error { + limits := _precomputedStackLimits.get(op) + stack.stackPointer = limits.min + + // jump instructions need a valid jump destination + if isJump(op) { + counter := slices.IndexFunc(code, func(v Instruction) bool { + return v.opcode == JUMPDEST + }) + if counter == -1 { + return fmt.Errorf("missing JUMPDEST instruction") + } + + for i := 0; i < limits.min; i++ { + stack.data[i] = *uint256.NewInt(uint64(counter)) + } + } + + return nil +} + +var _isUndefinedOpCodeRegex = regexp.MustCompile(`^op\(0x[0-9A-Fa-f]+\)$`) + +func isExecutable(op OpCode) bool { + if slices.Contains([]OpCode{INVALID, NOOP, DATA}, op) { + return false + } + return !_isUndefinedOpCodeRegex.MatchString(op.String()) +} + +func isJump(op OpCode) bool { + ops := append(op.decompose(), op) + return slices.ContainsFunc(ops, func(op OpCode) bool { + return op == JUMP || op == JUMPI + }) +} + +func benchmarkFib(b *testing.B, arg int, with_super_instructions bool) { + example := getFibExample() + + // Convert example to LFVM format. + converted := convert(example.code, ConversionConfig{WithSuperInstructions: with_super_instructions}) + + // Create input data. + + // See details of argument encoding: t.ly/kBl6 + data := make([]byte, 4+32) // < the parameter is padded up to 32 bytes + + // Encode function selector in big-endian format. + data[0] = byte(example.function >> 24) + data[1] = byte(example.function >> 16) + data[2] = byte(example.function >> 8) + data[3] = byte(example.function) + + // Encode argument as a big-endian value. + data[4+28] = byte(arg >> 24) + data[5+28] = byte(arg >> 16) + data[6+28] = byte(arg >> 8) + data[7+28] = byte(arg) + + // Create execution context. + ctxt := context{ + params: tosca.Parameters{ + Input: data, + Static: true, + }, + gas: 1 << 62, + code: converted, + stack: NewStack(), + memory: NewMemory(), + } + + // Compute expected value. + wanted := fib(arg) + + for i := 0; i < b.N; i++ { + // Reset the context. + ctxt.pc = 0 + ctxt.gas = 1 << 31 + ctxt.stack.stackPointer = 0 + + // Run the code (actual benchmark). + status, err := vanillaRunner{}.run(&ctxt) + if err != nil { + b.Fatalf("execution failed: %v", err) + } + + if status != statusReturned { + b.Fatalf("execution failed: status is %v", status) + } + + res := ctxt.returnData + copy(res, data) + + got := (int(res[28]) << 24) | (int(res[29]) << 16) | (int(res[30]) << 8) | (int(res[31]) << 0) + if wanted != got { + b.Fatalf("unexpected result, wanted %d, got %d", wanted, got) + } + } +} + +func getFibExample() example { + // An implementation of the fib function in EVM byte code. + code, err := hex.DecodeString("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f9b7c7e514610030575b600080fd5b61004a600480360381019061004591906100f6565b610060565b6040516100579190610132565b60405180910390f35b600060018263ffffffff161161007957600190506100b0565b61008e600283610089919061017c565b610060565b6100a360018461009e919061017c565b610060565b6100ad91906101b4565b90505b919050565b600080fd5b600063ffffffff82169050919050565b6100d3816100ba565b81146100de57600080fd5b50565b6000813590506100f0816100ca565b92915050565b60006020828403121561010c5761010b6100b5565b5b600061011a848285016100e1565b91505092915050565b61012c816100ba565b82525050565b60006020820190506101476000830184610123565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610187826100ba565b9150610192836100ba565b9250828203905063ffffffff8111156101ae576101ad61014d565b5b92915050565b60006101bf826100ba565b91506101ca836100ba565b9250828201905063ffffffff8111156101e6576101e561014d565b5b9291505056fea26469706673582212207fd33e47e97ce5871bb05401e6710238af535ae8aeaab013ca9a9c29152b8a1b64736f6c637827302e382e31372d646576656c6f702e323032322e382e392b636f6d6d69742e62623161386466390058") + if err != nil { + log.Fatalf("Unable to decode fib-code: %v", err) + } + + return example{ + code: code, + function: 0xF9B7C7E5, // The function selector for the fib function. + } +} + +func fib(x int) int { + if x <= 1 { + return 1 + } + return fib(x-1) + fib(x-2) +} + +var _introducedIn = newOpCodePropertyMap(func(op OpCode) tosca.Revision { + switch op { + case CLZ: + return tosca.R15_Osaka + case BASEFEE: + return tosca.R10_London + case PUSH0: + return tosca.R12_Shanghai + case BLOBHASH: + return tosca.R13_Cancun + case BLOBBASEFEE: + return tosca.R13_Cancun + case TLOAD: + return tosca.R13_Cancun + case TSTORE: + return tosca.R13_Cancun + case MCOPY: + return tosca.R13_Cancun + } + return tosca.R07_Istanbul +}) + +// forEachRevision runs a test for each revision starting from the revision +// where the operation was introduced. +// It creates a new testing scope to name the test after the revision. +func forEachRevision( + t *testing.T, op OpCode, + f func(t *testing.T, revision tosca.Revision)) { + + for revision := tosca.R07_Istanbul; revision <= newestSupportedRevision; revision++ { + if revision < _introducedIn.get(op) { + continue + } + t.Run(revision.String(), func(t *testing.T) { + f(t, revision) + }) + } +} diff --git a/go/interpreter/sfvm/keccak.go b/go/interpreter/sfvm/keccak.go new file mode 100644 index 00000000..669adac6 --- /dev/null +++ b/go/interpreter/sfvm/keccak.go @@ -0,0 +1,79 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +/* +#include "keccak.h" +*/ +import "C" + +import ( + "sync" + "unsafe" + + "github.com/0xsoniclabs/tosca/go/tosca" + "golang.org/x/crypto/sha3" +) + +func Keccak256(data []byte) tosca.Hash { + return keccak256_C(data) +} + +func Keccak256For32byte(data [32]byte) tosca.Hash { + return keccak256_C_32byte(data) +} + +var keccakHasherPool = sync.Pool{New: func() any { return sha3.NewLegacyKeccak256() }} + +func keccak256_Go(data []byte) tosca.Hash { + hasher := keccakHasherPool.Get().(keccakHasher) + hasher.Reset() + _, _ = hasher.Write(data) // keccak256 never returns an error + var res tosca.Hash + _, _ = hasher.Read(res[:]) // keccak256 never returns an error + keccakHasherPool.Put(hasher) + return res +} + +type keccakHasher interface { + Reset() + Write(in []byte) (int, error) + Read(out []byte) (int, error) +} + +var emptyKeccak256Hash = keccak256_Go([]byte{}) + +func keccak256_C(data []byte) tosca.Hash { + if len(data) == 0 { + return emptyKeccak256Hash + } + res := C.tosca_lfvm_keccak256(unsafe.Pointer(&data[0]), C.size_t(len(data))) + return tosca.Hash(res) +} + +func keccak256_C_32byte(data [32]byte) tosca.Hash { + // The address is passed as 4x 64-bit integer values through the stack to + // avoid the need of allocating heap memory for the key. + return tosca.Hash(C.tosca_lfvm_keccak256_32byte( + C.uint64_t( + uint64(data[7])<<56|uint64(data[6])<<48|uint64(data[5])<<40|uint64(data[4])<<32| + uint64(data[3])<<24|uint64(data[2])<<16|uint64(data[1])<<8|uint64(data[0])<<0), + C.uint64_t( + uint64(data[15])<<56|uint64(data[14])<<48|uint64(data[13])<<40|uint64(data[12])<<32| + uint64(data[11])<<24|uint64(data[10])<<16|uint64(data[9])<<8|uint64(data[8])<<0), + C.uint64_t( + uint64(data[23])<<56|uint64(data[22])<<48|uint64(data[21])<<40|uint64(data[20])<<32| + uint64(data[19])<<24|uint64(data[18])<<16|uint64(data[17])<<8|uint64(data[16])<<0), + C.uint64_t( + uint64(data[31])<<56|uint64(data[30])<<48|uint64(data[29])<<40|uint64(data[28])<<32| + uint64(data[27])<<24|uint64(data[26])<<16|uint64(data[25])<<8|uint64(data[24])<<0), + )) +} diff --git a/go/interpreter/sfvm/keccak.h b/go/interpreter/sfvm/keccak.h new file mode 100644 index 00000000..649e261c --- /dev/null +++ b/go/interpreter/sfvm/keccak.h @@ -0,0 +1,451 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +// This header file is based on the chfast/ethash project: +// +// source: +// https://github.com/chfast/ethash/tree/8e18c3d715d3d759f44ac10f70f9c93b227e18c2 +// License: Apache-2.0 license +// (https://github.com/chfast/ethash/blob/8e18c3d715d3d759f44ac10f70f9c93b227e18c2/LICENSE) +// +// This file merges extracts of codes from the project and adds Tosca specific +// modifications to facilitate the adaption in the Tosca project. +// +// ethash: C/C++ implementation of Ethash, the Ethereum Proof of Work algorithm. +// Copyright 2018 Pawel Bylica. +// SPDX-License-Identifier: Apache-2.0 + +// Provide __has_attribute macro if not defined. +#ifndef __has_attribute +#define __has_attribute(name) 0 +#endif + +// [[always_inline]] +#if defined(_MSC_VER) +#define ALWAYS_INLINE __forceinline +#elif __has_attribute(always_inline) +#define ALWAYS_INLINE __attribute__((always_inline)) +#else +#define ALWAYS_INLINE +#endif + +#include +#include + +#ifndef __cplusplus +#define noexcept // Ignore noexcept in C code. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +union ethash_hash256 { + uint64_t word64s[4]; + uint32_t word32s[8]; + uint8_t bytes[32]; + char str[32]; +}; + +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define to_le64(X) __builtin_bswap64(X) +#else +#define to_le64(X) X +#endif + +/// Loads 64-bit integer from given memory location as little-endian number. +static inline ALWAYS_INLINE uint64_t load_le(const uint8_t *data) { + /* memcpy is the best way of expressing the intention. Every compiler will + optimize is to single load instruction if the target architecture + supports unaligned memory access (GCC and clang even in O0). + This is great trick because we are violating C/C++ memory alignment + restrictions with no performance penalty. */ + uint64_t word; + __builtin_memcpy(&word, data, sizeof(word)); + return to_le64(word); +} + +/// Rotates the bits of x left by the count value specified by s. +/// The s must be in range <0, 64> exclusively, otherwise the result is +/// undefined. +static inline uint64_t rol(uint64_t x, unsigned s) { + return (x << s) | (x >> (64 - s)); +} + +static const uint64_t round_constants[24] = { // + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008}; + +/// The Keccak-f[1600] function. +/// +/// The implementation of the Keccak-f function with 1600-bit width of the +/// permutation (b). The size of the state is also 1600 bit what gives 25 64-bit +/// words. +/// +/// @param state The state of 25 64-bit words on which the permutation is to be +/// performed. +/// +/// The implementation based on: +/// - "simple" implementation by Ronny Van Keer, included in "Reference and +/// optimized code in C", +/// https://keccak.team/archives.html, CC0-1.0 / Public Domain. +static inline ALWAYS_INLINE void +keccakf1600_implementation(uint64_t state[25]) { + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + uint64_t Ba, Be, Bi, Bo, Bu; + + uint64_t Da, De, Di, Do, Du; + + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (size_t n = 0; n < 24; n += 2) { + // Round (n + 0): Axx -> Exx + + Ba = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + Be = Abe ^ Age ^ Ake ^ Ame ^ Ase; + Bi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + Bo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + Bu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + Da = Bu ^ rol(Be, 1); + De = Ba ^ rol(Bi, 1); + Di = Be ^ rol(Bo, 1); + Do = Bi ^ rol(Bu, 1); + Du = Bo ^ rol(Ba, 1); + + Ba = Aba ^ Da; + Be = rol(Age ^ De, 44); + Bi = rol(Aki ^ Di, 43); + Bo = rol(Amo ^ Do, 21); + Bu = rol(Asu ^ Du, 14); + Eba = Ba ^ (~Be & Bi) ^ round_constants[n]; + Ebe = Be ^ (~Bi & Bo); + Ebi = Bi ^ (~Bo & Bu); + Ebo = Bo ^ (~Bu & Ba); + Ebu = Bu ^ (~Ba & Be); + + Ba = rol(Abo ^ Do, 28); + Be = rol(Agu ^ Du, 20); + Bi = rol(Aka ^ Da, 3); + Bo = rol(Ame ^ De, 45); + Bu = rol(Asi ^ Di, 61); + Ega = Ba ^ (~Be & Bi); + Ege = Be ^ (~Bi & Bo); + Egi = Bi ^ (~Bo & Bu); + Ego = Bo ^ (~Bu & Ba); + Egu = Bu ^ (~Ba & Be); + + Ba = rol(Abe ^ De, 1); + Be = rol(Agi ^ Di, 6); + Bi = rol(Ako ^ Do, 25); + Bo = rol(Amu ^ Du, 8); + Bu = rol(Asa ^ Da, 18); + Eka = Ba ^ (~Be & Bi); + Eke = Be ^ (~Bi & Bo); + Eki = Bi ^ (~Bo & Bu); + Eko = Bo ^ (~Bu & Ba); + Eku = Bu ^ (~Ba & Be); + + Ba = rol(Abu ^ Du, 27); + Be = rol(Aga ^ Da, 36); + Bi = rol(Ake ^ De, 10); + Bo = rol(Ami ^ Di, 15); + Bu = rol(Aso ^ Do, 56); + Ema = Ba ^ (~Be & Bi); + Eme = Be ^ (~Bi & Bo); + Emi = Bi ^ (~Bo & Bu); + Emo = Bo ^ (~Bu & Ba); + Emu = Bu ^ (~Ba & Be); + + Ba = rol(Abi ^ Di, 62); + Be = rol(Ago ^ Do, 55); + Bi = rol(Aku ^ Du, 39); + Bo = rol(Ama ^ Da, 41); + Bu = rol(Ase ^ De, 2); + Esa = Ba ^ (~Be & Bi); + Ese = Be ^ (~Bi & Bo); + Esi = Bi ^ (~Bo & Bu); + Eso = Bo ^ (~Bu & Ba); + Esu = Bu ^ (~Ba & Be); + + // Round (n + 1): Exx -> Axx + + Ba = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + Be = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + Bi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + Bo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + Bu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + Da = Bu ^ rol(Be, 1); + De = Ba ^ rol(Bi, 1); + Di = Be ^ rol(Bo, 1); + Do = Bi ^ rol(Bu, 1); + Du = Bo ^ rol(Ba, 1); + + Ba = Eba ^ Da; + Be = rol(Ege ^ De, 44); + Bi = rol(Eki ^ Di, 43); + Bo = rol(Emo ^ Do, 21); + Bu = rol(Esu ^ Du, 14); + Aba = Ba ^ (~Be & Bi) ^ round_constants[n + 1]; + Abe = Be ^ (~Bi & Bo); + Abi = Bi ^ (~Bo & Bu); + Abo = Bo ^ (~Bu & Ba); + Abu = Bu ^ (~Ba & Be); + + Ba = rol(Ebo ^ Do, 28); + Be = rol(Egu ^ Du, 20); + Bi = rol(Eka ^ Da, 3); + Bo = rol(Eme ^ De, 45); + Bu = rol(Esi ^ Di, 61); + Aga = Ba ^ (~Be & Bi); + Age = Be ^ (~Bi & Bo); + Agi = Bi ^ (~Bo & Bu); + Ago = Bo ^ (~Bu & Ba); + Agu = Bu ^ (~Ba & Be); + + Ba = rol(Ebe ^ De, 1); + Be = rol(Egi ^ Di, 6); + Bi = rol(Eko ^ Do, 25); + Bo = rol(Emu ^ Du, 8); + Bu = rol(Esa ^ Da, 18); + Aka = Ba ^ (~Be & Bi); + Ake = Be ^ (~Bi & Bo); + Aki = Bi ^ (~Bo & Bu); + Ako = Bo ^ (~Bu & Ba); + Aku = Bu ^ (~Ba & Be); + + Ba = rol(Ebu ^ Du, 27); + Be = rol(Ega ^ Da, 36); + Bi = rol(Eke ^ De, 10); + Bo = rol(Emi ^ Di, 15); + Bu = rol(Eso ^ Do, 56); + Ama = Ba ^ (~Be & Bi); + Ame = Be ^ (~Bi & Bo); + Ami = Bi ^ (~Bo & Bu); + Amo = Bo ^ (~Bu & Ba); + Amu = Bu ^ (~Ba & Be); + + Ba = rol(Ebi ^ Di, 62); + Be = rol(Ego ^ Do, 55); + Bi = rol(Eku ^ Du, 39); + Bo = rol(Ema ^ Da, 41); + Bu = rol(Ese ^ De, 2); + Asa = Ba ^ (~Be & Bi); + Ase = Be ^ (~Bi & Bo); + Asi = Bi ^ (~Bo & Bu); + Aso = Bo ^ (~Bu & Ba); + Asu = Bu ^ (~Ba & Be); + } + + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} + +static void keccakf1600_generic(uint64_t state[25]) { + keccakf1600_implementation(state); +} + +/// The pointer to the best Keccak-f[1600] function implementation, +/// selected during runtime initialization. +static void (*keccakf1600_best)(uint64_t[25]) = keccakf1600_generic; + +#if !defined(_MSC_VER) && defined(__x86_64__) && __has_attribute(target) +__attribute__((target("bmi,bmi2"))) static void +keccakf1600_bmi(uint64_t state[25]) { + keccakf1600_implementation(state); +} + +__attribute__((constructor)) static void +select_keccakf1600_implementation(void) { + // Init CPU information. + // This is needed on macOS because of the bug: + // https://bugs.llvm.org/show_bug.cgi?id=48459. + __builtin_cpu_init(); + + // Check if both BMI and BMI2 are supported. Some CPUs like Intel E5-2697 v2 + // incorrectly report BMI2 but not BMI being available. + if (__builtin_cpu_supports("bmi") && __builtin_cpu_supports("bmi2")) + keccakf1600_best = keccakf1600_bmi; +} +#endif + +static inline ALWAYS_INLINE void keccak(uint64_t *out, size_t bits, + const uint8_t *data, size_t size) { + static const size_t word_size = sizeof(uint64_t); + const size_t hash_size = bits / 8; + const size_t block_size = (1600 - bits * 2) / 8; + + size_t i; + uint64_t *state_iter; + uint64_t last_word = 0; + uint8_t *last_word_iter = (uint8_t *)&last_word; + + uint64_t state[25] = {0}; + + while (size >= block_size) { + for (i = 0; i < (block_size / word_size); ++i) { + state[i] ^= load_le(data); + data += word_size; + } + + keccakf1600_best(state); + + size -= block_size; + } + + state_iter = state; + + while (size >= word_size) { + *state_iter ^= load_le(data); + ++state_iter; + data += word_size; + size -= word_size; + } + + while (size > 0) { + *last_word_iter = *data; + ++last_word_iter; + ++data; + --size; + } + *last_word_iter = 0x01; + *state_iter ^= to_le64(last_word); + + state[(block_size / word_size) - 1] ^= 0x8000000000000000; + + keccakf1600_best(state); + + for (i = 0; i < (hash_size / word_size); ++i) + out[i] = to_le64(state[i]); +} + +union ethash_hash256 tosca_lfvm_keccak256(const void *in, + size_t size) noexcept { + union ethash_hash256 hash; + keccak(hash.word64s, 256, (const uint8_t *)in, size); + return hash; +} + +static inline ALWAYS_INLINE union ethash_hash256 keccak_32(const uint64_t a, + const uint64_t b, + const uint64_t c, + const uint64_t d) { + static const size_t word_size = sizeof(uint64_t); + const size_t hash_size = 256 / 8; + const size_t block_size = (1600 - 256 * 2) / 8; + + size_t i; + uint64_t *state_iter; + uint64_t last_word = 0; + uint8_t *last_word_iter = (uint8_t *)&last_word; + + uint64_t state[25] = {0}; + + state_iter = state; + + *state_iter = a; + ++state_iter; + *state_iter = b; + ++state_iter; + *state_iter = c; + ++state_iter; + *state_iter = d; + ++state_iter; + + last_word = 0x01; + *state_iter ^= to_le64(last_word); + + state[(block_size / word_size) - 1] ^= 0x8000000000000000; + + keccakf1600_best(state); + + union ethash_hash256 res; + res.word64s[0] = state[0]; + res.word64s[1] = state[1]; + res.word64s[2] = state[2]; + res.word64s[3] = state[3]; + return res; +} + +union ethash_hash256 tosca_lfvm_keccak256_32byte(const uint64_t a, + const uint64_t b, + const uint64_t c, + const uint64_t d) noexcept { + return keccak_32(a, b, c, d); +} + +#ifdef __cplusplus +} +#endif diff --git a/go/interpreter/sfvm/keccak_test.go b/go/interpreter/sfvm/keccak_test.go new file mode 100644 index 00000000..da218259 --- /dev/null +++ b/go/interpreter/sfvm/keccak_test.go @@ -0,0 +1,127 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "math/rand" + "testing" +) + +func TestKeccakC_ProducesSameHashAsGo(t *testing.T) { + tests := [][]byte{ + nil, + {}, + {1, 2, 3}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + make([]byte, 128), + make([]byte, 1024), + } + for _, test := range tests { + want := keccak256_Go(test) + got := keccak256_C(test) + if want != got { + t.Errorf("unexpected hash for %v, wanted %v, got %v", test, want, got) + } + } +} + +func TestKeccakC_32ByteSpecializationProducesSameHashAsGenericVersion(t *testing.T) { + tests := [][32]byte{ + {}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2}, + } + + // Test each individual bit. + for i := 0; i < 32*8; i++ { + data := [32]byte{} + data[i/8] = 1 << i % 8 + tests = append(tests, data) + } + + // Add some random inputs as well. + r := rand.New(rand.NewSource(99)) + for i := 0; i < 10; i++ { + data := [32]byte{} + r.Read(data[:]) + tests = append(tests, data) + } + + t.Run("keccak256_C_32byte", func(t *testing.T) { + t.Parallel() + for _, test := range tests { + want := keccak256_Go(test[:]) + got := keccak256_C_32byte(test) + if want != got { + t.Errorf("unexpected hash for %v, wanted %v, got %v", test, want, got) + } + } + }) + + t.Run("Keccak256For32byte", func(t *testing.T) { + t.Parallel() + for _, test := range tests { + want := keccak256_Go(test[:]) + got := Keccak256For32byte(test) + if want != got { + t.Errorf("unexpected hash for %v, wanted %v, got %v", test, want, got) + } + } + }) +} + +func benchmark(b *testing.B, hasher func([]byte)) { + lengths := []int{1, 8, 32} + for i := 64; i < 1<<19; i <<= 2 { + lengths = append(lengths, i) + } + for _, i := range lengths { + b.Run(fmt.Sprintf("size=%d", i), func(b *testing.B) { + data := make([]byte, i) + for i := 0; i < b.N; i++ { + hasher(data) + } + }) + } +} + +func BenchmarkKeccakGo(b *testing.B) { + benchmark(b, func(data []byte) { + keccak256_Go(data) + }) +} + +func BenchmarkKeccakC(b *testing.B) { + benchmark(b, func(data []byte) { + keccak256_C(data) + }) +} + +func BenchmarkKeccakGo32ByteGeneric(b *testing.B) { + data := [32]byte{} + for i := 0; i < b.N; i++ { + keccak256_Go(data[:]) + } +} + +func BenchmarkKeccakC32ByteGeneric(b *testing.B) { + data := [32]byte{} + for i := 0; i < b.N; i++ { + keccak256_C(data[:]) + } +} + +func BenchmarkKeccakC32ByteSpecialized(b *testing.B) { + data := [32]byte{} + for i := 0; i < b.N; i++ { + keccak256_C_32byte(data) + } +} diff --git a/go/interpreter/sfvm/lfvm.go b/go/interpreter/sfvm/lfvm.go new file mode 100644 index 00000000..55335da7 --- /dev/null +++ b/go/interpreter/sfvm/lfvm.go @@ -0,0 +1,149 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "os" + + "github.com/0xsoniclabs/tosca/go/tosca" +) + +// Config provides a set of user-definable options for the LFVM interpreter. +type Config struct { +} + +// NewInterpreter creates a new LFVM interpreter instance with the official +// configuration for production purposes. +func NewInterpreter(Config) (*lfvm, error) { + return newVm(config{ + ConversionConfig: ConversionConfig{ + WithSuperInstructions: false, + }, + WithShaCache: true, + }) +} + +// Registers the long-form EVM as a possible interpreter implementation. +func init() { + tosca.MustRegisterInterpreterFactory("lfvm", func(any) (tosca.Interpreter, error) { + return NewInterpreter(Config{}) + }) +} + +// RegisterExperimentalInterpreterConfigurations registers all experimental +// LFVM interpreter configurations to Tosca's interpreter registry. This +// function should not be called in production code, as the resulting VMs are +// not officially supported. +func RegisterExperimentalInterpreterConfigurations() error { + + configs := map[string]config{} + + for _, si := range []string{"", "-si"} { + for _, shaCache := range []string{"", "-no-sha-cache"} { + for _, mode := range []string{"", "-stats", "-logging"} { + + config := config{ + ConversionConfig: ConversionConfig{ + WithSuperInstructions: si == "-si", + }, + WithShaCache: shaCache != "-no-sha-cache", + } + + switch mode { + case "-stats": + config.runner = &statisticRunner{ + stats: newStatistics(), + } + case "-logging": + config.runner = loggingRunner{ + log: os.Stdout, + } + } + + name := "lfvm" + si + shaCache + mode + if name == "lfvm" { + continue + } + + configs[name] = config + } + } + } + + configs["lfvm-no-code-cache"] = config{ + ConversionConfig: ConversionConfig{CacheSize: -1}, + } + + for name, config := range configs { + err := tosca.RegisterInterpreterFactory( + name, + func(any) (tosca.Interpreter, error) { + return newVm(config) + }, + ) + if err != nil { + return fmt.Errorf("failed to register interpreter %q: %v", name, err) + } + } + + return nil +} + +type config struct { + ConversionConfig + WithShaCache bool + runner runner +} + +type lfvm struct { + config config + converter *Converter +} + +func newVm(config config) (*lfvm, error) { + converter, err := NewConverter(config.ConversionConfig) + if err != nil { + return nil, fmt.Errorf("failed to create converter: %v", err) + } + return &lfvm{config: config, converter: converter}, nil +} + +// Defines the newest supported revision for this interpreter implementation +const newestSupportedRevision = tosca.R15_Osaka + +func (e *lfvm) Run(params tosca.Parameters) (tosca.Result, error) { + if params.Revision > newestSupportedRevision { + return tosca.Result{}, &tosca.ErrUnsupportedRevision{Revision: params.Revision} + } + + converted, err := e.converter.Convert( + params.Code, + params.CodeHash, + ) + if err != nil { + return tosca.Result{}, fmt.Errorf("failed to convert code: %w", err) + } + + return run(e.config, params, converted) +} + +func (e *lfvm) DumpProfile() { + if statsRunner, ok := e.config.runner.(*statisticRunner); ok { + fmt.Print(statsRunner.getSummary()) + } +} + +func (e *lfvm) ResetProfile() { + if statsRunner, ok := e.config.runner.(*statisticRunner); ok { + statsRunner.reset() + } +} diff --git a/go/interpreter/sfvm/lfvm_interface_test.go b/go/interpreter/sfvm/lfvm_interface_test.go new file mode 100644 index 00000000..c2d4e1e4 --- /dev/null +++ b/go/interpreter/sfvm/lfvm_interface_test.go @@ -0,0 +1,72 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm_test + +import ( + "testing" + + "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" + "github.com/0xsoniclabs/tosca/go/tosca" +) + +// TestLfvm_RegisterExperimentalConfigurations tests the registration of +// experimental configurations. +// This test is slightly different from other tests because of dealing with the +// global registry: +// - It is declared in it's own package to avoid leaking the registration to other tests. +// - It tests different properties, in one single function. The reason is that the +// order of different functions may change, invalidating the test. +func TestLfvm_RegisterExperimentalConfigurations(t *testing.T) { + + // Fist registration must succeed. + err := lfvm.RegisterExperimentalInterpreterConfigurations() + if err != nil { + t.Fatalf("failed to register experimental configurations: %v", err) + } + + // Registering a second time must fail. + err = lfvm.RegisterExperimentalInterpreterConfigurations() + if err == nil { + t.Fatalf("expected error when registering experimental configurations twice") + } + + // Check that lfvm is registered by default, in addition to experimental configurations + if _, ok := tosca.GetAllRegisteredInterpreters()["lfvm"]; !ok { + t.Fatalf("lfvm is not registered") + } + + // Construct all registered interpreter configurations + for name, factory := range tosca.GetAllRegisteredInterpreters() { + t.Run(name, func(t *testing.T) { + vm, err := factory(lfvm.Config{}) + if err != nil { + t.Fatalf("failed to create interpreter: %v", err) + } + + // Vms are opaque, we can't check their configuration directly. + // We can only check that they do execute some basic code. + params := tosca.Parameters{} + params.Code = []byte{byte(lfvm.PUSH1), 0xff, byte(lfvm.POP), byte(lfvm.STOP)} + params.Gas = 5 + res, err := vm.Run(params) + if err != nil { + t.Fatalf("failed to run interpreter: %v", err) + } + + if want, got := true, res.Success; want != got { + t.Fatalf("unexpected success result: want %v, got %v", want, got) + } + if want, got := tosca.Gas(0), res.GasLeft; want != got { + t.Fatalf("unexpected gas used: want %v, got %v", want, got) + } + }) + } +} diff --git a/go/interpreter/sfvm/lfvm_test.go b/go/interpreter/sfvm/lfvm_test.go new file mode 100644 index 00000000..d4d7f8b9 --- /dev/null +++ b/go/interpreter/sfvm/lfvm_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" +) + +func TestNewInterpreter_ProducesInstanceWithSanctionedProperties(t *testing.T) { + lfvm, err := NewInterpreter(Config{}) + if err != nil { + t.Fatalf("failed to create LFVM instance: %v", err) + } + if lfvm.config.WithShaCache != true { + t.Fatalf("LFVM is not configured with sha cache") + } + if lfvm.config.WithSuperInstructions != false { + t.Fatalf("LFVM is configured with super instructions") + } +} + +func TestLfvm_OfficialConfigurationHasSanctionedProperties(t *testing.T) { + vm, err := tosca.NewInterpreter("lfvm") + if err != nil { + t.Fatalf("lfvm is not registered: %v", err) + } + lfvm, ok := vm.(*lfvm) + if !ok { + t.Fatalf("unexpected interpreter implementation, got %T", vm) + } + if lfvm.config.WithShaCache != true { + t.Fatalf("lfvm is not configured with sha cache") + } + if lfvm.config.WithSuperInstructions != false { + t.Fatalf("lfvm is configured with super instructions") + } +} + +func TestLfvm_InterpreterReturnsErrorWhenExecutingUnsupportedRevision(t *testing.T) { + vm, err := tosca.NewInterpreter("lfvm") + if err != nil { + t.Fatalf("lfvm is not registered: %v", err) + } + + params := tosca.Parameters{} + params.Revision = newestSupportedRevision + 1 + + _, err = vm.Run(params) + if want, got := fmt.Sprintf("unsupported revision %d", params.Revision), err.Error(); want != got { + t.Fatalf("unexpected error: want %q, got %q", want, got) + } +} + +func TestLfvm_newVm_returnsErrorWithWrongConfiguration(t *testing.T) { + config := config{ + ConversionConfig: ConversionConfig{CacheSize: maxCachedCodeLength / 2}, + } + _, err := newVm(config) + if err == nil { + t.Fatalf("expected error, got nil") + } +} diff --git a/go/interpreter/sfvm/memory.go b/go/interpreter/sfvm/memory.go new file mode 100644 index 00000000..c4a445e4 --- /dev/null +++ b/go/interpreter/sfvm/memory.go @@ -0,0 +1,144 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/holiman/uint256" +) + +type Memory struct { + store []byte + currentMemoryCost tosca.Gas +} + +func NewMemory() *Memory { + return &Memory{} +} + +const ( + // Maximum memory size allowed + // This magic number comes from 'core/vm/gas_table.go' 'memoryGasCost' in geth + maxMemoryExpansionSize = 0x1FFFFFFFE0 +) + +// getExpansionCostsAndSize returns the gas cost and the new memory size after +// the expansion. +// The function returns an error if the new size is greater than the maximum +// memory size allowed, or an overflow happens when computing the costs. +func (m *Memory) getExpansionCostsAndSize(size uint64) (tosca.Gas, uint64, error) { + + // static assert + const ( + // Memory expansion cost is done using unsigned arithmetic, + // check for the maximum memory expansion size, not overflowing int64 after computing costs + maxInWords uint64 = (uint64(maxMemoryExpansionSize) + 31) / 32 + _ = int64(maxInWords*maxInWords/512 + 3*maxInWords) + ) + + if m.length() >= size { + return 0, m.length(), nil + } + + words := tosca.SizeInWords(size) + validSize := words * 32 + if validSize < size { + return 0, 0, errOverflow + } + if validSize > maxMemoryExpansionSize { + return 0, 0, errMaxMemoryExpansionSize + } + + new_costs := tosca.Gas((words*words)/512 + (3 * words)) + fee := new_costs - m.currentMemoryCost + return fee, validSize, nil +} + +// expandMemory tries to expand memory to the given size. +// If the memory is already large enough or size is 0, it does nothing. +// If there is not enough gas in the context or an overflow occurs when adding +// offset and size, it returns an error. +func (m *Memory) expandMemory(offset, size uint64, c *context) error { + if size == 0 { + return nil + } + needed := offset + size + // check overflow + if needed < offset { + return errOverflow + } + if m.length() < needed { + fee, expandedSize, err := m.getExpansionCostsAndSize(needed) + if err != nil { + return err + } + if err := c.useGas(fee); err != nil { + return err + } + + currentSize := m.length() + m.currentMemoryCost += fee + m.store = append(m.store, make([]byte, expandedSize-currentSize)...) + } + + return nil +} + +func (m *Memory) length() uint64 { + return uint64(len(m.store)) +} + +// getSlice obtains a slice of size bytes from the memory at the given offset. +// The returned slice is backed by the memory's internal data. Updates to the +// slice will thus effect the memory states. This connection is invalidated by any +// subsequent memory operation that may change the size of the memory. +func (m *Memory) getSlice(offset, size *uint256.Int, c *context) ([]byte, error) { + if size.IsZero() { + return nil, nil + } + if !offset.IsUint64() || !size.IsUint64() { + return nil, errOverflow + } + + offset64 := offset.Uint64() + size64 := size.Uint64() + + err := m.expandMemory(offset64, size64, c) + if err != nil { + return nil, err + } + return m.store[offset64 : offset64+size64], nil +} + +// readWord reads a Word (32 byte) from the memory at the given offset and stores +// that word in the provided target. +// Expands memory as needed and charges for it. +// Returns an error in case of not enough gas or offset+32 overflow. +func (m *Memory) readWord(offset *uint256.Int, target *uint256.Int, c *context) error { + data, err := m.getSlice(offset, uint256.NewInt(32), c) + if err != nil { + return err + } + target.SetBytes32(data) + return nil +} + +// set copies the given value into memory at the given offset. +// Expands the memory size as needed and charges for it. +// Returns an error if there is not enough gas or offset+len(value) overflows. +func (m *Memory) set(offset *uint256.Int, value []byte, c *context) error { + data, err := m.getSlice(offset, uint256.NewInt(uint64(len(value))), c) + if err != nil { + return err + } + copy(data, value) + return nil +} diff --git a/go/interpreter/sfvm/memory_test.go b/go/interpreter/sfvm/memory_test.go new file mode 100644 index 00000000..9c5701b8 --- /dev/null +++ b/go/interpreter/sfvm/memory_test.go @@ -0,0 +1,477 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "bytes" + "crypto/rand" + "errors" + "math" + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/holiman/uint256" +) + +func TestMemory_NewMemoryIsEmpty(t *testing.T) { + m := NewMemory() + if m.length() != 0 { + t.Errorf("memory should be empty, instead has length: %d", m.length()) + } +} + +func TestGetExpansionCostsAndSize(t *testing.T) { + type testDef struct { + size uint64 + cost tosca.Gas + } + + tests := map[string]struct { + tests []testDef + err error + }{ + "zero size does not expand": { + tests: []testDef{ + {size: 0}, + }, + }, + "in-rage size can be expanded": { + tests: []testDef{ + {size: 1, cost: 3}, + {size: 32, cost: 3}, + {size: 33, cost: 6}, + {size: 64, cost: 6}, + {size: 65, cost: 9}, + {size: 22 * 32, cost: 3 * 22}, + {size: 23 * 32, cost: (23*23)/512 + 3*23}, + {size: maxMemoryExpansionSize - 33, cost: 36028809870311418}, + {size: maxMemoryExpansionSize - 1, cost: 36028809887088637}, + {size: maxMemoryExpansionSize, cost: 36028809887088637}, + }, + }, + "larger size than memory size limit yields an error": { + tests: []testDef{ + {size: maxMemoryExpansionSize + 1}, + {size: math.MaxInt64}, + }, + err: errMaxMemoryExpansionSize, + }, + "size overflowing word count computation yields an error": { + tests: []testDef{ + {size: math.MaxUint64}, + }, + err: errOverflow, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + expectedError := test.err + + for _, test := range test.tests { + + m := NewMemory() + cost, size, err := m.getExpansionCostsAndSize(test.size) + if !errors.Is(err, expectedError) { + t.Errorf("unexpected error: want: %v but got: %v", expectedError, err) + } + + if err != nil { + continue + } + + // all expansions must be done in 32-byte chunks + expectedSize := (test.size + 31) / 32 * 32 + if want, got := expectedSize, size; want != got { + t.Errorf("unexpected size: want: %d but got: %d", want, got) + } + + // cost must be calculated by the formula + words := tosca.SizeInWords(test.size) + expectedCost := tosca.Gas((words*words)/512 + 3*words) + if want, got := expectedCost, cost; want != got { + t.Errorf("unexpected cost: want: %d but got: %d", want, got) + } + } + }) + } +} + +func TestMemory_ExpansionsAndCostsAreIncremental(t *testing.T) { + for a := uint64(0); a < 128; a += 32 { + for b := uint64(0); b < 128; b += 32 { + + m := NewMemory() + + // Compute costs from 0 to target size. + costA, sizeA, errA := m.getExpansionCostsAndSize(a) + costB, sizeB, errB := m.getExpansionCostsAndSize(b) + if errA != nil || errB != nil { + t.Fatalf("unexpected error: %v, %v", errA, errB) + } + + // Compute costs for increasing from size a to size b. + ctxt := &context{gas: math.MaxInt} + if err := m.expandMemory(0, a, ctxt); err != nil { + t.Fatalf("unexpected error: %v", err) + } + delta, sizeAB, err := m.getExpansionCostsAndSize(b) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + max := sizeA + if sizeB > sizeA { + max = sizeB + } + if sizeAB != max { + t.Fatalf("size must increase monotonically, got: %d, %d, %d", sizeA, sizeB, sizeAB) + } + + wantDelta := tosca.Gas(0) + if a <= b { + wantDelta = costB - costA + } + + if wantDelta != delta { + t.Fatalf("unexpected delta for expansions to %d and %d: want: %d, got: %d", a, b, wantDelta, delta) + } + } + } +} + +func TestMemory_expandMemory_ErrorCases(t *testing.T) { + + tests := map[string]struct { + size uint64 + offset uint64 + gas tosca.Gas + expected error + }{ + "not enough gas": { + size: 32, + offset: 0, + gas: 0, + expected: errOutOfGas, + }, + "offset overflow": { + size: 1, + offset: math.MaxUint64, + gas: 100, + expected: errOverflow, + }, + "size overflow": { + size: math.MaxUint64, + offset: 1, + gas: 100, + expected: errOverflow, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctxt := context{gas: test.gas} + m := NewMemory() + + err := m.expandMemory(test.offset, test.size, &ctxt) + if !errors.Is(err, test.expected) { + t.Errorf("unexpected error, want: %v, got: %v", test.expected, err) + } + if m.length() != 0 { + t.Errorf("should have remained empty, got length: %d", m.length()) + } + }) + } +} + +func TestMemory_expandMemory_expandsMemoryOnlyWhenNeeded(t *testing.T) { + + tests := map[string]struct { + size uint64 + offset uint64 + initialMemorySize uint64 + expectedMemorySize uint64 + }{ + "empty memory with zero offset and size does not expand": {}, + "size zero with offset does not expand": { + size: 0, + offset: 32, + expectedMemorySize: 0, + }, + "expand memory in words length": { + size: 13, + offset: 0, + expectedMemorySize: 32, + }, + "memory bigger than size+offset does not expand": { + size: 41, + offset: 41, + initialMemorySize: 128, + expectedMemorySize: 128, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctxt := context{gas: 3} + m := NewMemory() + m.store = make([]byte, test.initialMemorySize) + + err := m.expandMemory(test.offset, test.size, &ctxt) + if err != nil { + t.Fatalf("unexpected error, want: %v, got: %v", nil, err) + } + if m.length() != test.expectedMemorySize { + t.Errorf("unexpected memory size, want: %d, got: %d", test.expectedMemorySize, m.length()) + } + }) + } +} + +func TestMemory_getSlice_ErrorCases(t *testing.T) { + c := context{gas: 0} + m := NewMemory() + _, err := m.getSlice(uint256.NewInt(0), uint256.NewInt(1), &c) + if !errors.Is(err, errOutOfGas) { + t.Errorf("error should be errOutOfGas, instead is: %v", err) + } + + _, err = m.getSlice(uint256.NewInt(math.MaxUint64-31), uint256.NewInt(32), &c) + if !errors.Is(err, errOverflow) { + t.Errorf("error should be errOverflow, instead is: %v", err) + } +} + +func TestMemory_getSlice_ReturnsSliceOfRequestedSize(t *testing.T) { + + tests := map[string]struct { + offset *uint256.Int + size *uint256.Int + expected []byte + }{ + "size zero returns empty slice": { + offset: uint256.NewInt(64), + size: uint256.NewInt(0), + expected: []byte{}, + }, + "memory does not expand when not needed": { + offset: uint256.NewInt(0), + size: uint256.NewInt(4), + expected: []byte{0x0, 0x01, 0x02, 0x03}, + }, + "memory expands when needed": { + offset: uint256.NewInt(4), + size: uint256.NewInt(5), + expected: []byte{0x04, 4: 0x0}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + c := context{gas: 3} + m := NewMemory() + m.store = []byte{0x0, 0x01, 0x02, 0x03, 0x04} + slice, err := m.getSlice(test.offset, test.size, &c) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !bytes.Equal(slice, test.expected) { + t.Errorf("unexpected slice: %x, want: %x", slice, test.expected) + } + }) + } +} + +func TestMemory_getSlice_ExpandsMemoryIn32ByteChunks(t *testing.T) { + for memSize := uint64(0); memSize < 128; memSize += 32 { + for offset := uint64(0); offset < 128; offset++ { + for size := uint64(1); size < 32; size++ { + offset256 := uint256.NewInt(offset) + size256 := uint256.NewInt(size) + c := &context{gas: 15} + m := NewMemory() + m.store = make([]byte, memSize) + _, err := m.getSlice(offset256, size256, c) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + want := (offset + size + 31) / 32 * 32 + if want < memSize { + want = memSize + } + if got, want := m.length(), want; got != want { + t.Errorf("unexpected memory length: %d, want: %d", got, want) + } + } + } + } +} + +func TestMemory_getSlice_DoesNotExpandWithSizeZero(t *testing.T) { + for memSize := 0; memSize < 128; memSize += 32 { + for offset := uint64(0); offset < 128; offset++ { + c := &context{gas: 1} + m := NewMemory() + m.store = make([]byte, memSize) + _, err := m.getSlice(uint256.NewInt(offset), uint256.NewInt(0), c) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if c.gas != 1 { + t.Error("no gas should have been consumed when size is zero.") + } + if got, want := m.length(), uint64(memSize); got != want { + t.Errorf("unexpected memory length: %d, want: %d", got, want) + } + } + } +} + +func TestMemory_getSlice_MemoryExpansionDoesNotOverwriteExistingMemory(t *testing.T) { + c := context{gas: 6} + m := NewMemory() + m.store = []byte{0x0, 0x01, 0x02, 0x03, 0x04} + _, err := m.getSlice(uint256.NewInt(4), uint256.NewInt(29), &c) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if m.length() != 64 { + t.Errorf("memory should have been expanded to 64 bytes, instead is: %d", m.length()) + } + if !bytes.Equal(m.store[:5], []byte{0x0, 0x01, 0x02, 0x03, 0x04}) { + t.Errorf("unexpected memory value: %x", m.store) + } +} + +func TestMemory_getSlice_ExpandsWithZeros(t *testing.T) { + c := context{gas: 6} + m := NewMemory() + baseMemory := []byte{0x0, 0x01, 0x02, 0x03, 0x04} + m.store = bytes.Clone(baseMemory) + _, err := m.getSlice(uint256.NewInt(28), uint256.NewInt(8), &c) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if m.length() != 64 { + t.Errorf("memory should have been expanded to 64 bytes, instead is: %d", m.length()) + } + if !bytes.Equal(m.store, append(baseMemory, []byte{58: 0x0}...)) { + t.Errorf("unexpected memory value: %x", m.store) + } +} + +func TestMemory_readWord_ErrorCases(t *testing.T) { + c := context{gas: 0} + m := NewMemory() + originalTarget := uint256.NewInt(1) + target := originalTarget.Clone() + err := m.readWord(uint256.NewInt(math.MaxUint64-31), target, &c) + if !errors.Is(err, errOverflow) { + t.Errorf("error should be errOverflow, instead is: %v", err) + } + if target.Cmp(originalTarget) != 0 { + t.Errorf("target should not have been modified, want %v but got %v", originalTarget, target) + } + err = m.readWord(uint256.NewInt(0), target, &c) + if !errors.Is(err, errOutOfGas) { + t.Errorf("error should be errOutOfGas, instead is: %v", err) + } + if target.Cmp(originalTarget) != 0 { + t.Errorf("target should not have been modified, want %v but got %v", originalTarget, target) + } +} + +func TestMemory_set_UpdatesDataInMemoryAtGivenOffset(t *testing.T) { + before := generateRandomBytes(128) + for offset := uint64(0); offset < 128; offset++ { + for size := 0; size < int(128-offset); size++ { + data := generateRandomBytes(size) + + // test the memory update + m := &Memory{store: bytes.Clone(before)} + if err := m.set(uint256.NewInt(offset), data, nil); err != nil { + t.Errorf("unexpected error: %v", err) + } + + // check if only the data at the given offset has changed + want := bytes.Clone(before) + copy(want[offset:], data) + if !bytes.Equal(m.store, want) { + t.Errorf("unexpected memory value after set, want: %x, got: %x", want, m.store) + } + } + } +} + +func TestMemory_set_ExpansionPreservesMemoryContentAndPadsWithZeroesToTheRight(t *testing.T) { + before := generateRandomBytes(32) + m := &Memory{store: bytes.Clone(before)} + if err := m.set(uint256.NewInt(64), []byte{0x1, 0x2}, &context{gas: 100}); err != nil { + t.Errorf("unexpected error: %v", err) + } + + want := bytes.Clone(before) // < preserved data + want = append(want, make([]byte, 32)...) // < zero-padding before the new data + want = append(want, []byte{0x1, 0x2}...) // < the new data + want = append(want, make([]byte, 30)...) // < zero-padding after the new data + + if !bytes.Equal(m.store, want) { + t.Errorf("unexpected memory value after set, want: %x, got: %x", want, m.store) + } +} + +func TestMemory_set_IgnoresEmptyData(t *testing.T) { + before := generateRandomBytes(32) + for _, offset := range []uint64{0, 32, 64} { + for _, data := range [][]byte{nil, {}} { + m := &Memory{store: bytes.Clone(before)} + if err := m.set(uint256.NewInt(offset), data, nil); err != nil { + t.Errorf("unexpected error: %v", err) + } + if !bytes.Equal(m.store, before) { + t.Errorf("unexpected no change in data, want: %x, got: %x", before, m.store) + } + } + } +} + +func TestMemory_set_FailsIfThereIsNotEnoughGasToGrow(t *testing.T) { + c := &context{gas: 2} + m := &Memory{store: make([]byte, 32)} + if err := m.set(uint256.NewInt(64), []byte{0x1}, c); !errors.Is(err, errOutOfGas) { + t.Errorf("unexpected error %v, got %v", errOutOfGas, err) + } + if len(m.store) != 32 { + t.Errorf("memory should not have been expanded, instead is: %d", len(m.store)) + } +} + +func TestMemory_set_FailsIfOffsetLeadsToOverflow(t *testing.T) { + m := &Memory{store: make([]byte, 32)} + data := []byte{0x1, 0x2, 0x3} + offset := math.MaxUint64 - uint64(len(data)) + 1 + if err := m.set(uint256.NewInt(offset), data, nil); !errors.Is(err, errOverflow) { + t.Errorf("unexpected error %v, got %v", errOutOfGas, err) + } + if len(m.store) != 32 { + t.Errorf("memory should not have been expanded, instead is: %d", len(m.store)) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Helper functions + +func generateRandomBytes(size int) []byte { + data := make([]byte, size) + _, _ = rand.Read(data) + return data +} diff --git a/go/interpreter/sfvm/opcode.go b/go/interpreter/sfvm/opcode.go new file mode 100644 index 00000000..74351259 --- /dev/null +++ b/go/interpreter/sfvm/opcode.go @@ -0,0 +1,429 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + + "github.com/0xsoniclabs/tosca/go/tosca/vm" +) + +type OpCode uint16 + +// opCodeMask defines the relevant trailing bits of an OpCode. Any two OpCodes +// with the same value when masked with opCodeMask are considered equal. +// +// The motivation for this is that the long-form EVM has a number of OpCodes +// that are not part of the original EVM. For those, values beyond the range +// [0-255] of the EVM's single-byte OpCodes are used. To that end, the OpCode +// data type in the LFVM is increased to 16 bits. However, in several places +// maps from LFVM OpCodes to properties are required to provide efficient +// lookup tables for properties. To avoid the need to maintain tables of +// 2^16 entries, the number of relevant bits is reduced to 9. Any leading bits +// are ignored when comparing OpCodes. +const opCodeMask = 0x1ff + +// numOpCodes is the maximum number of OpCodes that can be defined. +const numOpCodes = opCodeMask + 1 + +// The following constants define the original EVM OpCodes, in the lfvm OpCode space. +const ( + // Stack operations + POP = OpCode(vm.POP) + PUSH0 = OpCode(vm.PUSH0) + PUSH1 = OpCode(vm.PUSH1) + PUSH2 = OpCode(vm.PUSH2) + PUSH3 = OpCode(vm.PUSH3) + PUSH4 = OpCode(vm.PUSH4) + PUSH5 = OpCode(vm.PUSH5) + PUSH6 = OpCode(vm.PUSH6) + PUSH7 = OpCode(vm.PUSH7) + PUSH8 = OpCode(vm.PUSH8) + PUSH9 = OpCode(vm.PUSH9) + PUSH10 = OpCode(vm.PUSH10) + PUSH11 = OpCode(vm.PUSH11) + PUSH12 = OpCode(vm.PUSH12) + PUSH13 = OpCode(vm.PUSH13) + PUSH14 = OpCode(vm.PUSH14) + PUSH15 = OpCode(vm.PUSH15) + PUSH16 = OpCode(vm.PUSH16) + PUSH17 = OpCode(vm.PUSH17) + PUSH18 = OpCode(vm.PUSH18) + PUSH19 = OpCode(vm.PUSH19) + PUSH20 = OpCode(vm.PUSH20) + PUSH21 = OpCode(vm.PUSH21) + PUSH22 = OpCode(vm.PUSH22) + PUSH23 = OpCode(vm.PUSH23) + PUSH24 = OpCode(vm.PUSH24) + PUSH25 = OpCode(vm.PUSH25) + PUSH26 = OpCode(vm.PUSH26) + PUSH27 = OpCode(vm.PUSH27) + PUSH28 = OpCode(vm.PUSH28) + PUSH29 = OpCode(vm.PUSH29) + PUSH30 = OpCode(vm.PUSH30) + PUSH31 = OpCode(vm.PUSH31) + PUSH32 = OpCode(vm.PUSH32) + + DUP1 = OpCode(vm.DUP1) + DUP2 = OpCode(vm.DUP2) + DUP3 = OpCode(vm.DUP3) + DUP4 = OpCode(vm.DUP4) + DUP5 = OpCode(vm.DUP5) + DUP6 = OpCode(vm.DUP6) + DUP7 = OpCode(vm.DUP7) + DUP8 = OpCode(vm.DUP8) + DUP9 = OpCode(vm.DUP9) + DUP10 = OpCode(vm.DUP10) + DUP11 = OpCode(vm.DUP11) + DUP12 = OpCode(vm.DUP12) + DUP13 = OpCode(vm.DUP13) + DUP14 = OpCode(vm.DUP14) + DUP15 = OpCode(vm.DUP15) + DUP16 = OpCode(vm.DUP16) + + SWAP1 = OpCode(vm.SWAP1) + SWAP2 = OpCode(vm.SWAP2) + SWAP3 = OpCode(vm.SWAP3) + SWAP4 = OpCode(vm.SWAP4) + SWAP5 = OpCode(vm.SWAP5) + SWAP6 = OpCode(vm.SWAP6) + SWAP7 = OpCode(vm.SWAP7) + SWAP8 = OpCode(vm.SWAP8) + SWAP9 = OpCode(vm.SWAP9) + SWAP10 = OpCode(vm.SWAP10) + SWAP11 = OpCode(vm.SWAP11) + SWAP12 = OpCode(vm.SWAP12) + SWAP13 = OpCode(vm.SWAP13) + SWAP14 = OpCode(vm.SWAP14) + SWAP15 = OpCode(vm.SWAP15) + SWAP16 = OpCode(vm.SWAP16) + + // Control flow + JUMP = OpCode(vm.JUMP) + JUMPI = OpCode(vm.JUMPI) + JUMPDEST = OpCode(vm.JUMPDEST) + RETURN = OpCode(vm.RETURN) + REVERT = OpCode(vm.REVERT) + PC = OpCode(vm.PC) + STOP = OpCode(vm.STOP) + + // Arithmetic + ADD = OpCode(vm.ADD) + SUB = OpCode(vm.SUB) + MUL = OpCode(vm.MUL) + DIV = OpCode(vm.DIV) + SDIV = OpCode(vm.SDIV) + MOD = OpCode(vm.MOD) + SMOD = OpCode(vm.SMOD) + ADDMOD = OpCode(vm.ADDMOD) + MULMOD = OpCode(vm.MULMOD) + EXP = OpCode(vm.EXP) + SIGNEXTEND = OpCode(vm.SIGNEXTEND) + + // Complex function + SHA3 = OpCode(vm.SHA3) + + // Comparison operations + LT = OpCode(vm.LT) + GT = OpCode(vm.GT) + SLT = OpCode(vm.SLT) + SGT = OpCode(vm.SGT) + EQ = OpCode(vm.EQ) + ISZERO = OpCode(vm.ISZERO) + + // Bit-pattern operations + AND = OpCode(vm.AND) + OR = OpCode(vm.OR) + XOR = OpCode(vm.XOR) + NOT = OpCode(vm.NOT) + BYTE = OpCode(vm.BYTE) + SHL = OpCode(vm.SHL) + SHR = OpCode(vm.SHR) + SAR = OpCode(vm.SAR) + CLZ = OpCode(vm.CLZ) + + // Memory + MSTORE = OpCode(vm.MSTORE) + MSTORE8 = OpCode(vm.MSTORE8) + MLOAD = OpCode(vm.MLOAD) + MSIZE = OpCode(vm.MSIZE) + MCOPY = OpCode(vm.MCOPY) + + // Storage + SLOAD = OpCode(vm.SLOAD) + SSTORE = OpCode(vm.SSTORE) + TLOAD = OpCode(vm.TLOAD) + TSTORE = OpCode(vm.TSTORE) + + // LOG + LOG0 = OpCode(vm.LOG0) + LOG1 = OpCode(vm.LOG1) + LOG2 = OpCode(vm.LOG2) + LOG3 = OpCode(vm.LOG3) + LOG4 = OpCode(vm.LOG4) + + // System level instructions. + ADDRESS = OpCode(vm.ADDRESS) + BALANCE = OpCode(vm.BALANCE) + ORIGIN = OpCode(vm.ORIGIN) + CALLER = OpCode(vm.CALLER) + CALLVALUE = OpCode(vm.CALLVALUE) + CALLDATALOAD = OpCode(vm.CALLDATALOAD) + CALLDATASIZE = OpCode(vm.CALLDATASIZE) + CALLDATACOPY = OpCode(vm.CALLDATACOPY) + CODESIZE = OpCode(vm.CODESIZE) + CODECOPY = OpCode(vm.CODECOPY) + GASPRICE = OpCode(vm.GASPRICE) + EXTCODESIZE = OpCode(vm.EXTCODESIZE) + EXTCODECOPY = OpCode(vm.EXTCODECOPY) + RETURNDATASIZE = OpCode(vm.RETURNDATASIZE) + RETURNDATACOPY = OpCode(vm.RETURNDATACOPY) + EXTCODEHASH = OpCode(vm.EXTCODEHASH) + CREATE = OpCode(vm.CREATE) + CALL = OpCode(vm.CALL) + CALLCODE = OpCode(vm.CALLCODE) + DELEGATECALL = OpCode(vm.DELEGATECALL) + CREATE2 = OpCode(vm.CREATE2) + STATICCALL = OpCode(vm.STATICCALL) + SELFDESTRUCT = OpCode(vm.SELFDESTRUCT) + + // Blockchain instructions + BLOCKHASH = OpCode(vm.BLOCKHASH) + COINBASE = OpCode(vm.COINBASE) + TIMESTAMP = OpCode(vm.TIMESTAMP) + NUMBER = OpCode(vm.NUMBER) + PREVRANDAO = OpCode(vm.PREVRANDAO) + GAS = OpCode(vm.GAS) + GASLIMIT = OpCode(vm.GASLIMIT) + CHAINID = OpCode(vm.CHAINID) + SELFBALANCE = OpCode(vm.SELFBALANCE) + BASEFEE = OpCode(vm.BASEFEE) + BLOBHASH = OpCode(vm.BLOBHASH) + BLOBBASEFEE = OpCode(vm.BLOBBASEFEE) + + // Invalid instruction + INVALID = OpCode(vm.INVALID) +) + +// The following constants define the extended set of OpCodes for the long-form +// EVM. +// These opcodes are specific to the long-form EVM and are not part of the +// original EVM. +const ( + // long-form EVM special instructions + + // JUMP_TO is a special instruction that is used to jump to the end of the + // current basic block. + // + // Since due to the usage of immediate arguments in instructions like PUSH2 + // the code size of basic blocks can shrink compared to the original EVM, + // gaps can appear between the end of a basic block and the beginning of the + // next one indicated by a JUMPDEST instruction. Since all JUMPDEST + // instructions have to remain at the same position in the code as in the + // original EVM code, since jump-destinations of JUMP and JUMPI operations + // are computed dynamically, these gaps have to be filled with NOOP + // instructions. To avoid having to process long sequences of NOOPs, + // JUMP_TO instructions are used to skip them in a single step. + // + // The following restrictions are imposed on JUMP_TO instructions: + // - they must target the immediate succeeding JUMPDEST instruction + // - all instructions between the JUMP_TO and the JUMPDEST must be NOOPs + // + // These restrictions are enforced during the EVM to LFVM code conversion. + JUMP_TO OpCode = iota + 0x100 + + // NOOP is a special instruction that does nothing. It is used as a filler + // instruction to pad basic blocks to the correct size. + NOOP + + // DATA is a special instruction that is used to extend the size of OpCodes + // that require more than the available 2-byte immediate arguments. + // For instance, [PUSH4, 1, 2, 3, 4] in the original EVM code gets converted + // to [(PUSH4, 1<<8 | 2),(DATA, 3<<8 | 4)]. + // Since DATA is marked explicitly as such, jump-destination checks can be + // conducted in O(1) by checking the OpCode of an instruction. In the + // implicit data encoding of EVM byte code, this would require a linear + // search (which could be cached to amortize costs). + DATA + + // Super-instructions + SWAP2_SWAP1_POP_JUMP + SWAP1_POP_SWAP2_SWAP1 + POP_SWAP2_SWAP1_POP + POP_POP + PUSH1_SHL + PUSH1_ADD + PUSH1_DUP1 + PUSH2_JUMP + PUSH2_JUMPI + + PUSH1_PUSH1 + SWAP1_POP + POP_JUMP + SWAP2_SWAP1 + SWAP2_POP + DUP2_MSTORE + DUP2_LT + + ISZERO_PUSH2_JUMPI + PUSH1_PUSH4_DUP3 + AND_SWAP1_POP_SWAP2_SWAP1 + PUSH1_PUSH1_PUSH1_SHL_SUB + + // _highestOpCode is an alias for the OpCode with the highest defined + // numeric value. It is only intended to be used in the unit tests + // associated to this OpCode definition file to verify that the OpCode + // bit mask limit has not been exceeded. + _highestOpCode = PUSH1_PUSH1_PUSH1_SHL_SUB +) + +var toString = map[OpCode]string{ + DATA: "DATA", + NOOP: "NOOP", + JUMP_TO: "JUMP_TO", + + SWAP2_SWAP1_POP_JUMP: "SWAP2_SWAP1_POP_JUMP", + SWAP1_POP_SWAP2_SWAP1: "SWAP1_POP_SWAP2_SWAP1", + POP_SWAP2_SWAP1_POP: "POP_SWAP2_SWAP1_POP", + PUSH2_JUMP: "PUSH2_JUMP", + PUSH2_JUMPI: "PUSH2_JUMPI", + DUP2_MSTORE: "DUP2_MSTORE", + DUP2_LT: "DUP2_LT", + + SWAP1_POP: "SWAP1_POP", + POP_JUMP: "POP_JUMP", + SWAP2_SWAP1: "SWAP2_SWAP1", + SWAP2_POP: "SWAP2_POP", + PUSH1_PUSH1: "PUSH1_PUSH1", + PUSH1_ADD: "PUSH1_ADD", + PUSH1_DUP1: "PUSH1_DUP1", + POP_POP: "POP_POP", + PUSH1_SHL: "PUSH1_SHL", + + ISZERO_PUSH2_JUMPI: "ISZERO_PUSH2_JUMPI", + PUSH1_PUSH4_DUP3: "PUSH1_PUSH4_DUP3", + AND_SWAP1_POP_SWAP2_SWAP1: "AND_SWAP1_POP_SWAP2_SWAP1", + PUSH1_PUSH1_PUSH1_SHL_SUB: "PUSH1_PUSH1_PUSH1_SHL_SUB", +} + +// String returns the string representation of the OpCode. +func (o OpCode) String() string { + if o <= 0xFF { + return vm.OpCode(o).String() + } + + if str, ok := toString[o]; ok { + return str + } + return fmt.Sprintf("op(0x%04X)", int16(o)) +} + +// HasArgument returns true if the second 16-bit word of the instruction is +// argument data. +func (o OpCode) HasArgument() bool { + if PUSH1 <= o && o <= PUSH32 { + return true + } + switch o { + case DATA: + return true + case JUMP_TO: + return true + } + if o.isSuperInstruction() { + for _, subOp := range o.decompose() { + if subOp.HasArgument() { + return true + } + } + } + return false +} + +func (o OpCode) isBaseInstruction() bool { + return o < 0x100 +} + +func (o OpCode) isSuperInstruction() bool { + return o.decompose() != nil +} + +func (o OpCode) decompose() []OpCode { + switch o { + case SWAP2_SWAP1_POP_JUMP: + return []OpCode{SWAP2, SWAP1, POP, JUMP} + case SWAP1_POP_SWAP2_SWAP1: + return []OpCode{SWAP1, POP, SWAP2, SWAP1} + case POP_SWAP2_SWAP1_POP: + return []OpCode{POP, SWAP2, SWAP1, POP} + case POP_POP: + return []OpCode{POP, POP} + case PUSH1_SHL: + return []OpCode{PUSH1, SHL} + case PUSH1_ADD: + return []OpCode{PUSH1, ADD} + case PUSH1_DUP1: + return []OpCode{PUSH1, DUP1} + case PUSH2_JUMP: + return []OpCode{PUSH2, JUMP} + case PUSH2_JUMPI: + return []OpCode{PUSH2, JUMPI} + case PUSH1_PUSH1: + return []OpCode{PUSH1, PUSH1} + case SWAP1_POP: + return []OpCode{SWAP1, POP} + case POP_JUMP: + return []OpCode{POP, JUMP} + case SWAP2_SWAP1: + return []OpCode{SWAP2, SWAP1} + case SWAP2_POP: + return []OpCode{SWAP2, POP} + case DUP2_MSTORE: + return []OpCode{DUP2, MSTORE} + case DUP2_LT: + return []OpCode{DUP2, LT} + case ISZERO_PUSH2_JUMPI: + return []OpCode{ISZERO, PUSH2, JUMPI} + case PUSH1_PUSH4_DUP3: + return []OpCode{PUSH1, PUSH4, DUP3} + case AND_SWAP1_POP_SWAP2_SWAP1: + return []OpCode{AND, SWAP1, POP, SWAP2, SWAP1} + case PUSH1_PUSH1_PUSH1_SHL_SUB: + return []OpCode{PUSH1, PUSH1, PUSH1, SHL, SUB} + } + return nil +} + +// opCodePropertyMap is a generic property map for precomputed values. +// Its purpose is to provide a precomputed lookup table for OpCode properties +// that can be generated from a function that takes an OpCode as input. +// Using this type hides internal details of the opcode implementation. +type opCodePropertyMap[T any] struct { + lookup [numOpCodes]T +} + +// newOpCodePropertyMap creates a new OpCode property map. +// The property function shall be resilient to undefined OpCode values, and not +// panic. The zero values or a sentinel value shall be used in such cases. +func newOpCodePropertyMap[T any](property func(op OpCode) T) opCodePropertyMap[T] { + lookup := [numOpCodes]T{} + for i := 0; i < numOpCodes; i++ { + lookup[i] = property(OpCode(i)) + } + return opCodePropertyMap[T]{lookup} +} + +func (p *opCodePropertyMap[T]) get(op OpCode) T { + // Index may be out of bounds. Nevertheless, bounds check carry a performance + // penalty. If the property map is initialized correctly, the index will be + // within bounds. + return p.lookup[op&opCodeMask] +} diff --git a/go/interpreter/sfvm/opcode_test.go b/go/interpreter/sfvm/opcode_test.go new file mode 100644 index 00000000..020ea2bb --- /dev/null +++ b/go/interpreter/sfvm/opcode_test.go @@ -0,0 +1,80 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "math" + "testing" +) + +func TestOpCode_String(t *testing.T) { + tests := []struct { + op OpCode + want string + }{ + {STOP, "STOP"}, + {SWAP2_SWAP1_POP_JUMP, "SWAP2_SWAP1_POP_JUMP"}, + {0x0c, "op(0x0C)"}, + {0x0200, "op(0x0200)"}, + } + for _, tt := range tests { + if got := tt.op.String(); got != tt.want { + t.Errorf("got = %q, want %q", got, tt.want) + } + } +} + +func TestOpCode_SuperInstructionsAreDecomposedToBasicOpCodes(t *testing.T) { + for _, op := range allOpCodesWhere(OpCode.isSuperInstruction) { + baseOps := op.decompose() + for _, baseOp := range baseOps { + if baseOp.isSuperInstruction() { + t.Errorf("decomposition of %v contains super instruction %v", op, baseOp) + } + } + } +} + +func TestOpCode_AllOpCodesAreSmallerThanTheOpCodeCapacity(t *testing.T) { + if want, get := numOpCodes, opCodeMask+1; want != get { + t.Errorf("opCodeMask+1 = %d, want %d", get, want) + } + if _highestOpCode >= numOpCodes { + t.Errorf( + "highest op code %d exceeds the current OpCode type capacity of %d", + _highestOpCode, + numOpCodes, + ) + } +} + +func TestOpcodeProperty_DoesNotOverflow(t *testing.T) { + identity := newOpCodePropertyMap(func(op OpCode) OpCode { return op }) + for i := OpCode(0); i < OpCode(math.MaxInt16); i++ { + if got, want := identity.get(i), i%numOpCodes; got != want { + t.Errorf("got %d, want %d", got, want) + } + } +} + +func allOpCodesWhere(predicate func(op OpCode) bool) []OpCode { + res := []OpCode{} + for op := OpCode(0); op < numOpCodes; op++ { + if predicate(op) { + res = append(res, op) + } + } + return res +} + +func allOpCodes() []OpCode { + return allOpCodesWhere(func(op OpCode) bool { return true }) +} diff --git a/go/interpreter/sfvm/stack.go b/go/interpreter/sfvm/stack.go new file mode 100644 index 00000000..eee992c1 --- /dev/null +++ b/go/interpreter/sfvm/stack.go @@ -0,0 +1,146 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "strings" + "sync" + + "github.com/holiman/uint256" +) + +const maxStackSize = 1024 // Maximum size of VM stack allowed. + +// stack is the 1024-element 256-bit word-wide stack used by the VM. +// It is a fixed-size stack to prevent memory reallocation during execution. +// Check boundaries are not checked. Users of the stack must prevent over- and +// underflow situations. +// +// Each stack consumes 1024 * 32 bytes = 32KB of memory. Thus, creating and +// destroying stacks could incur significant overhead. To mitigate this, a +// stack pool is provided to reuse stack instances. To obtain an empty stack +// from the pool, use NewStack(). To return a stack to the pool, use +// ReturnStack(s). +// +// Example usage: +// +// s := NewStack() +// defer ReturnStack(s) +// +// +// The stack is not thread-safe. NewStack() and ReturnStack() are thread-safe. +type stack struct { + data [maxStackSize]uint256.Int + stackPointer int +} + +// push adds a copy of the given value to the top of the stack. +func (s *stack) push(data *uint256.Int) { + s.data[s.stackPointer] = *data + s.stackPointer++ +} + +// push adds a value with an undefined value to the top of the stack and returns +// a pointer to this element. Use this function if the element on the top stack +// should be modified directly using the returned pointer. +func (s *stack) pushUndefined() *uint256.Int { + s.stackPointer++ + return &s.data[s.stackPointer-1] +} + +// pop removes the top element from the stack and returns a pointer to it. The +// obtained pointer is only valid until the next push operation. The pointer +// can be used to obtain the popped element without the need to copy it. +func (s *stack) pop() *uint256.Int { + s.stackPointer-- + return &s.data[s.stackPointer] +} + +// peek returns a pointer to the top element of the stack without removing it. +// The returned pointer is only valid until the next operation on the stack. +func (s *stack) peek() *uint256.Int { + return &s.data[s.len()-1] +} + +// peekN returns a pointer to the n-th element from the top of the stack without +// removing it. The top element is at index 0 Thus, peekN(0) is equivalent to +// peek(). +func (s *stack) peekN(n int) *uint256.Int { + return &s.data[s.len()-n-1] +} + +// len returns the number of elements on the stack. +func (s *stack) len() int { + return s.stackPointer +} + +// swap exchanges the top element with the n-th element from the top. The top +// element is at index 0. Thus, swap(0) is a no-op. +func (s *stack) swap(n int) { + s.data[s.len()-n-1], s.data[s.len()-1] = s.data[s.len()-1], s.data[s.len()-n-1] +} + +// dup duplicates the n-th element from the top and pushes it to the top of the +// stack. The top element is at index 0. Thus, dup(0) duplicates the top element. +func (s *stack) dup(n int) { + s.data[s.stackPointer] = s.data[s.stackPointer-n-1] + s.stackPointer++ +} + +// get returns the element at the given index. The bottom element is at index 0. +func (s *stack) get(i int) *uint256.Int { + return &s.data[i] +} + +func (s *stack) String() string { + toHex := func(z *uint256.Int) string { + b := strings.Builder{} + b.WriteString("0x") + bytes := z.Bytes32() + for i, cur := range bytes { + b.WriteString(fmt.Sprintf("%02x", cur)) + if (i+1)%8 == 0 { + b.WriteString(" ") + } + } + return b.String() + } + + b := strings.Builder{} + for i := 0; i < s.len(); i++ { + b.WriteString(fmt.Sprintf(" [%4d] %v\n", s.len()-i-1, toHex(s.peekN(i)))) + } + return b.String() +} + +// ------------------ Stack Pool ------------------ + +var stackPool = sync.Pool{ + New: func() interface{} { + return &stack{} + }, +} + +// NewStack returns a new stack instance from the a reuse pool. Heavy stack +// users should use this function to prevent memory reallocation overhead. +// This function is thread-safe. +func NewStack() *stack { + return stackPool.Get().(*stack) +} + +// ReturnStack returns the stack to the reuse pool. Any stack may only be +// returned once to avoid concurrent re-use. This is not checked internally. +// This function is thread-safe. +func ReturnStack(s *stack) { + s.stackPointer = 0 + stackPool.Put(s) +} diff --git a/go/interpreter/sfvm/stack_test.go b/go/interpreter/sfvm/stack_test.go new file mode 100644 index 00000000..c5da253f --- /dev/null +++ b/go/interpreter/sfvm/stack_test.go @@ -0,0 +1,297 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "strings" + "sync" + "testing" + + "github.com/holiman/uint256" +) + +func TestStack_ZeroStackIsEmpty(t *testing.T) { + var stack stack + if want, got := 0, stack.len(); want != got { + t.Errorf("expected stack to be empty, but got %d elements", got) + } +} + +func TestStack_pushAndPop_CanUseFullCapacity(t *testing.T) { + var stack stack + + for i := 0; i < maxStackSize; i++ { + if want, got := i, stack.len(); want != got { + t.Errorf("expected stack to have %d elements, but got %d", want, got) + } + val := uint256.NewInt(uint64(i)) + stack.push(val) + } + + if want, got := maxStackSize, stack.len(); want != got { + t.Errorf("expected stack to have %d elements, but got %d", want, got) + } + + for i := maxStackSize - 1; i >= 0; i-- { + val := stack.pop() + if want, got := uint256.NewInt(uint64(i)), val; want.Cmp(got) != 0 { + t.Errorf("expected popped value to be %d, but got %d", want, got) + } + if want, got := i, stack.len(); want != got { + t.Errorf("expected stack to have %d elements, but got %d", want, got) + } + } +} + +func TestStack_push_AddsProvidedElementToStack(t *testing.T) { + values := []*uint256.Int{ + uint256.NewInt(0), + uint256.NewInt(1), + new(uint256.Int).Lsh(uint256.NewInt(1), 64), + new(uint256.Int).Lsh(uint256.NewInt(1), 128), + new(uint256.Int).Lsh(uint256.NewInt(1), 192), + } + + stack := NewStack() + defer ReturnStack(stack) + + for _, val := range values { + stack.push(val) + if want, got := val, stack.peek(); want.Cmp(got) != 0 { + t.Errorf("expected top element to be %d, but got %d", want, got) + } + } +} + +func TestStack_pushUndefined_ResultCanBeUsedToManipulatePeek(t *testing.T) { + values := []*uint256.Int{ + uint256.NewInt(0), + uint256.NewInt(1), + new(uint256.Int).Lsh(uint256.NewInt(1), 64), + new(uint256.Int).Lsh(uint256.NewInt(1), 128), + new(uint256.Int).Lsh(uint256.NewInt(1), 192), + } + + stack := NewStack() + defer ReturnStack(stack) + + for _, val := range values { + peek := stack.pushUndefined() + peek.Set(val) + if want, got := val, stack.peek(); want.Cmp(got) != 0 { + t.Errorf("expected top element to be %d, but got %d", want, got) + } + } +} + +func TestStack_peekN_ObtainsNthElementFromTop(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + for i := 0; i < 10; i++ { + val := uint256.NewInt(uint64(i)) + stack.push(val) + } + + if want, got := stack.peek(), stack.peekN(0); want != got { + t.Errorf("expected peekN(0) to be the same as peek(), but got %d and %d", want, got) + } + + for i := 0; i < 10; i++ { + want := uint256.NewInt(uint64(9 - i)) + got := stack.peekN(i) + if want.Cmp(got) != 0 { + t.Errorf("expected %d-th element from top to be %d, but got %d", i, want, got) + } + } +} + +func TestStack_swap_ExchangesTopElementWithSelectedElement(t *testing.T) { + // n => expected order after swap(n) + tests := map[int][]uint64{ + 0: {0, 1, 2, 3, 4}, + 1: {1, 0, 2, 3, 4}, + 2: {2, 1, 0, 3, 4}, + 3: {3, 1, 2, 0, 4}, + 4: {4, 1, 2, 3, 0}, + } + + for n, result := range tests { + t.Run(fmt.Sprintf("swap%d", n), func(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + for i := 4; i >= 0; i-- { + stack.push(uint256.NewInt(uint64(i))) + } + + stack.swap(n) + + for i, want := range result { + got := stack.peekN(i).Uint64() + if want != got { + t.Errorf("expected %d-th element to be %d, but got %d", i, want, got) + } + } + }) + } +} + +func TestStack_swap_WorksForAnyIntegerValue(t *testing.T) { + for _, size := range []int{2, 128, maxStackSize - 1} { + for i := 0; i < size; i++ { + t.Run(fmt.Sprintf("size=%d_swap%d", size, i), func(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + for i := 0; i < size; i++ { + stack.push(uint256.NewInt(uint64(i))) + } + + want := stack.peekN(i).Uint64() + stack.swap(i) + got := stack.peek().Uint64() + + if want != got { + t.Errorf("expected top element to be %d, but got %d", want, got) + } + }) + } + } +} + +func TestStack_dup_DuplicatesSelectedElementFromStack(t *testing.T) { + // n => expected content after dup(n) + tests := map[int][]uint64{ + 0: {0, 0, 1, 2, 3, 4}, + 1: {1, 0, 1, 2, 3, 4}, + 2: {2, 0, 1, 2, 3, 4}, + 3: {3, 0, 1, 2, 3, 4}, + 4: {4, 0, 1, 2, 3, 4}, + } + + for n, result := range tests { + t.Run(fmt.Sprintf("dup%d", n), func(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + for i := 4; i >= 0; i-- { + stack.push(uint256.NewInt(uint64(i))) + } + + stack.dup(n) + + for i, want := range result { + got := stack.peekN(i).Uint64() + if want != got { + t.Errorf("expected %d-th element to be %d, but got %d", i, want, got) + } + } + }) + } +} + +func TestStack_dup_WorksForAnyIntegerValue(t *testing.T) { + for _, size := range []int{2, 128, maxStackSize - 1} { + for i := 0; i < size; i++ { + t.Run(fmt.Sprintf("size=%d_dup%d", size, i), func(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + for i := 0; i < size; i++ { + stack.push(uint256.NewInt(uint64(i))) + } + + want := stack.peekN(i).Uint64() + stack.dup(i) + got := stack.peek().Uint64() + + if want != got { + t.Errorf("expected top element to be %d, but got %d", want, got) + } + }) + } + } +} + +func TestStack_get_IndexesElementsBottomUp(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + for i := 0; i < maxStackSize; i++ { + stack.push(uint256.NewInt(uint64(i))) + } + + for i := 0; i < maxStackSize; i++ { + want := uint256.NewInt(uint64(i)) + got := stack.get(i) + if want.Cmp(got) != 0 { + t.Errorf("expected %d-th element to be %d, but got %d", i, want, got) + } + } +} + +func TestStack_String_PrintsContentUsingFormattedHex(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + for i := 0; i < 256; i++ { + top := stack.pushUndefined() + top.Lsh(uint256.NewInt(1), uint(i)) + } + + print := stack.String() + + wanted := []string{ + "[ 0] 0x0000000000000000 0000000000000000 0000000000000000 0000000000000001", + "[ 1] 0x0000000000000000 0000000000000000 0000000000000000 0000000000000002", + "[ 2] 0x0000000000000000 0000000000000000 0000000000000000 0000000000000004", + "[ 16] 0x0000000000000000 0000000000000000 0000000000000000 0000000000010000", + "[ 64] 0x0000000000000000 0000000000000000 0000000000000001 0000000000000000", + "[ 128] 0x0000000000000000 0000000000000001 0000000000000000 0000000000000000", + "[ 255] 0x8000000000000000 0000000000000000 0000000000000000 0000000000000000", + } + + for _, want := range wanted { + if !strings.Contains(print, want) { + t.Errorf("expected output to contain %q, but got %q", want, print) + } + } +} + +func TestStack_NewStack_IsEmpty(t *testing.T) { + stack := NewStack() + defer ReturnStack(stack) + + if want, got := 0, stack.len(); want != got { + t.Errorf("expected stack to be empty, but got %d elements", got) + } +} + +func TestStack_NewStackAndReturnStack_AreThreadSafe(t *testing.T) { + // this test assumes to be executed using the --race flag. + const parallelism = 10 + const iterations = 1000 + + var wg sync.WaitGroup + wg.Add(parallelism) + for i := 0; i < parallelism; i++ { + go func() { + defer wg.Done() + for j := 0; j < iterations; j++ { + stack := NewStack() + defer ReturnStack(stack) + } + }() + } + wg.Wait() +} diff --git a/go/interpreter/sfvm/stack_usage.go b/go/interpreter/sfvm/stack_usage.go new file mode 100644 index 00000000..61a9ae81 --- /dev/null +++ b/go/interpreter/sfvm/stack_usage.go @@ -0,0 +1,116 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +// stackUsage defines the combined effect of an instruction on the stack. Each +// instruction is accessing a range of elements on the stack relative to the +// stack pointer. The range is given by the interval [from, to) where from is +// the lower end and to is the upper end of the accessed interval. The delta +// field represents the change in the stack size caused by the instruction. +type stackUsage struct { + from, to, delta int +} + +// computeStackUsage computes the stack usage of the given opcode. The result +// is a stackUsage struct that defines the combined effect of the instruction +// on the stack. If the opcode is not known, zero stack usage is reported. +func computeStackUsage(op OpCode) stackUsage { + + // For single instructions it is easiest to define the stack usage based on + // the opcode's pops and pushes. + makeUsage := func(pops, pushes int) stackUsage { + delta := pushes - pops + to := 0 + if delta > 0 { + to = delta + } + return stackUsage{from: -pops, to: to, delta: delta} + } + + if PUSH1 <= op && op <= PUSH32 { + return makeUsage(0, 1) + } + if DUP1 <= op && op <= DUP16 { + return makeUsage(int(op-DUP1+1), int(op-DUP1+2)) + } + if SWAP1 <= op && op <= SWAP16 { + return makeUsage(int(op-SWAP1+2), int(op-SWAP1+2)) + } + if LOG0 <= op && op <= LOG4 { + return makeUsage(int(op-LOG0+2), 0) + } + + switch op { + case JUMPDEST, JUMP_TO, STOP: + return makeUsage(0, 0) + case PUSH0, MSIZE, ADDRESS, ORIGIN, CALLER, CALLVALUE, CALLDATASIZE, + CODESIZE, GASPRICE, COINBASE, TIMESTAMP, NUMBER, + PREVRANDAO, GASLIMIT, PC, GAS, RETURNDATASIZE, + SELFBALANCE, CHAINID, BASEFEE, BLOBBASEFEE: + return makeUsage(0, 1) + case POP, JUMP, SELFDESTRUCT: + return makeUsage(1, 0) + case ISZERO, NOT, BALANCE, CALLDATALOAD, EXTCODESIZE, + BLOCKHASH, MLOAD, SLOAD, TLOAD, EXTCODEHASH, BLOBHASH, CLZ: + return makeUsage(1, 1) + case MSTORE, MSTORE8, SSTORE, TSTORE, JUMPI, RETURN, REVERT: + return makeUsage(2, 0) + case ADD, SUB, MUL, DIV, SDIV, MOD, SMOD, EXP, SIGNEXTEND, + SHA3, LT, GT, SLT, SGT, EQ, AND, XOR, OR, BYTE, + SHL, SHR, SAR: + return makeUsage(2, 1) + case CALLDATACOPY, CODECOPY, RETURNDATACOPY, MCOPY: + return makeUsage(3, 0) + case ADDMOD, MULMOD, CREATE: + return makeUsage(3, 1) + case EXTCODECOPY: + return makeUsage(4, 0) + case CREATE2: + return makeUsage(4, 1) + case STATICCALL, DELEGATECALL: + return makeUsage(6, 1) + case CALL, CALLCODE: + return makeUsage(7, 1) + } + + // For super-instructions, we need to decompose the instruction into its + // sub-instructions and compute the combined stack usage. + if op.isSuperInstruction() { + usages := []stackUsage{} + for _, subOp := range op.decompose() { + usages = append(usages, computeStackUsage(subOp)) + } + return combineStackUsage(usages...) + } + + return stackUsage{} +} + +// combineStackUsage combines the given stack usages into a single stack usage. +func combineStackUsage(usages ...stackUsage) stackUsage { + // This function simulates the effect of the given stack usages on the stack + // step by step. The delta of the resulting stack usage tracks the current + // stack height offset. + res := stackUsage{} + for _, usage := range usages { + from := usage.from + res.delta + to := usage.to + res.delta + + if from < res.from { + res.from = from + } + if to > res.to { + res.to = to + } + res.delta += usage.delta + } + return res +} diff --git a/go/interpreter/sfvm/stack_usage_test.go b/go/interpreter/sfvm/stack_usage_test.go new file mode 100644 index 00000000..88ba1144 --- /dev/null +++ b/go/interpreter/sfvm/stack_usage_test.go @@ -0,0 +1,108 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "fmt" + "testing" +) + +func TestComputeStackUsage_ProducesValidResultsForSingleOps(t *testing.T) { + tests := []struct { + op OpCode + usage stackUsage + }{ + {STOP, stackUsage{from: 0, to: 0, delta: 0}}, + {ADD, stackUsage{from: -2, to: 0, delta: -1}}, + {POP, stackUsage{from: -1, to: 0, delta: -1}}, + {PUSH5, stackUsage{from: 0, to: 1, delta: 1}}, + {SWAP1, stackUsage{from: -2, to: 0, delta: 0}}, + {SWAP10, stackUsage{from: -11, to: 0, delta: 0}}, + {DUP1, stackUsage{from: -1, to: 1, delta: 1}}, + {DUP12, stackUsage{from: -12, to: 1, delta: 1}}, + {LOG3, stackUsage{from: -5, to: 0, delta: -5}}, + } + + for _, test := range tests { + t.Run(test.op.String(), func(t *testing.T) { + usage := computeStackUsage(test.op) + if got, want := usage, test.usage; got != want { + t.Errorf("unexpected result: want %v, got %v", want, got) + } + }) + } +} + +func TestCombineStackUsage(t *testing.T) { + tests := []struct { + ops []OpCode + usage stackUsage + }{ + { + []OpCode{}, + stackUsage{from: 0, to: 0, delta: 0}, + }, + { + []OpCode{PUSH1}, + stackUsage{from: 0, to: 1, delta: 1}, + }, + { + []OpCode{POP}, + stackUsage{from: -1, to: 0, delta: -1}, + }, + { + []OpCode{PUSH1, PUSH1}, + stackUsage{from: 0, to: 2, delta: 2}, + }, + { + []OpCode{PUSH1, POP}, + stackUsage{from: 0, to: 1, delta: 0}, + }, + { + []OpCode{POP, PUSH1}, + stackUsage{from: -1, to: 0, delta: 0}, + }, + { + []OpCode{POP, POP}, + stackUsage{from: -2, to: 0, delta: -2}, + }, + { + []OpCode{PUSH1, PUSH1, POP, POP}, + stackUsage{from: 0, to: 2, delta: 0}, + }, + { + []OpCode{PUSH1, PUSH1, POP, POP, POP, PUSH1, PUSH1}, + stackUsage{from: -1, to: 2, delta: 1}, + }, + { + []OpCode{PUSH1, LOG4, PUSH1}, + stackUsage{from: -5, to: 1, delta: -4}, + }, + { + []OpCode{PUSH1_ADD, ISZERO_PUSH2_JUMPI}, + stackUsage{from: -1, to: 1, delta: -1}, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%v", test.ops), func(t *testing.T) { + usages := []stackUsage{} + for _, op := range test.ops { + usages = append(usages, computeStackUsage(op)) + } + + res := combineStackUsage(usages...) + if res != test.usage { + t.Errorf("unexpected result: want %v, got %v", test.usage, res) + } + }) + } +} diff --git a/go/interpreter/sfvm/super_instructions.go b/go/interpreter/sfvm/super_instructions.go new file mode 100644 index 00000000..048a3538 --- /dev/null +++ b/go/interpreter/sfvm/super_instructions.go @@ -0,0 +1,168 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "math/bits" + + "github.com/holiman/uint256" +) + +// ----------------------------- Super Instructions ----------------------------- + +func opSwap1_Pop(c *context) { + a1 := c.stack.pop() + a2 := c.stack.peek() + *a2 = *a1 +} + +func opSwap2_Pop(c *context) { + a1 := c.stack.pop() + *c.stack.peekN(1) = *a1 +} + +func opPush1_Push1(c *context) { + arg := c.code[c.pc].arg + c.stack.stackPointer += 2 + c.stack.peekN(0).SetUint64(uint64(arg & 0xFF)) + c.stack.peekN(1).SetUint64(uint64(arg >> 8)) +} + +func opPush1_Add(c *context) { + arg := c.code[c.pc].arg + trg := c.stack.peek() + var carry uint64 + trg[0], carry = bits.Add64(trg[0], uint64(arg), 0) + trg[1], carry = bits.Add64(trg[1], 0, carry) + trg[2], carry = bits.Add64(trg[2], 0, carry) + trg[3], _ = bits.Add64(trg[3], 0, carry) +} + +func opPush1_Shl(c *context) { + arg := c.code[c.pc].arg + trg := c.stack.peek() + trg.Lsh(trg, uint(arg)) +} + +func opPush1_Dup1(c *context) { + arg := c.code[c.pc].arg + c.stack.stackPointer += 2 + c.stack.peekN(0).SetUint64(uint64(arg)) + c.stack.peekN(1).SetUint64(uint64(arg)) +} + +func opPush2_Jump(c *context) error { + // Directly take pushed value and jump to destination. + c.pc = int32(c.code[c.pc].arg) - 1 + return checkJumpDest(c) +} + +func opPush2_Jumpi(c *context) error { + // Directly take pushed value and jump to destination. + condition := c.stack.pop() + if !condition.IsZero() { + c.pc = int32(c.code[c.pc].arg) - 1 + return checkJumpDest(c) + } + return nil +} + +func opSwap2_Swap1(c *context) { + a1 := c.stack.peekN(0) + a2 := c.stack.peekN(1) + a3 := c.stack.peekN(2) + *a1, *a2, *a3 = *a2, *a3, *a1 +} + +func opDup2_Mstore(c *context) error { + var value = c.stack.pop() + var offset = c.stack.peek() + v := value.Bytes32() + return c.memory.set(offset, v[:], c) +} + +func opDup2_Lt(c *context) { + b := c.stack.peekN(0) + a := c.stack.peekN(1) + if a.Lt(b) { + b.SetOne() + } else { + b.Clear() + } +} + +func opPopPop(c *context) { + c.stack.stackPointer -= 2 +} + +func opPop_Jump(c *context) error { + opPop(c) + return opJump(c) +} + +func opIsZero_Push2_Jumpi(c *context) error { + condition := c.stack.pop() + if condition.IsZero() { + c.pc = int32(c.code[c.pc].arg) - 1 + return checkJumpDest(c) + } + return nil +} + +func opSwap2_Swap1_Pop_Jump(c *context) error { + top := c.stack.pop() + c.stack.pop() + trg := c.stack.peek() + c.pc = int32(trg.Uint64()) - 1 + *trg = *top + return checkJumpDest(c) +} + +func opSwap1_Pop_Swap2_Swap1(c *context) { + a1 := c.stack.pop() + a2 := c.stack.peekN(0) + a3 := c.stack.peekN(1) + a4 := c.stack.peekN(2) + *a2, *a3, *a4 = *a3, *a4, *a1 +} + +func opPop_Swap2_Swap1_Pop(c *context) { + c.stack.pop() + a2 := c.stack.pop() + a3 := c.stack.peekN(0) + a4 := c.stack.peekN(1) + *a3, *a4 = *a4, *a2 +} + +func opPush1_Push4_Dup3(c *context) { + opPush1(c) + c.pc++ + opPush4(c) + opDup(c, 3) +} + +func opAnd_Swap1_Pop_Swap2_Swap1(c *context) { + opAnd(c) + opSwap1_Pop_Swap2_Swap1(c) +} + +func opPush1_Push1_Push1_Shl_Sub(c *context) { + arg1 := c.code[c.pc].arg + arg2 := c.code[c.pc+1].arg + shift := uint8(arg2) + value := uint8(arg1 & 0xFF) + delta := uint8(arg1 >> 8) + trg := c.stack.pushUndefined() + trg.SetUint64(uint64(value)) + trg.Lsh(trg, uint(shift)) + trg.Sub(trg, uint256.NewInt(uint64(delta))) + c.pc++ +} diff --git a/go/interpreter/sfvm/super_instructions_test.go b/go/interpreter/sfvm/super_instructions_test.go new file mode 100644 index 00000000..7591e0ab --- /dev/null +++ b/go/interpreter/sfvm/super_instructions_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package lfvm + +import ( + "testing" + + "github.com/holiman/uint256" +) + +func TestSI_opDup2_Lt(t *testing.T) { + + tests := map[string]struct { + a, b uint256.Int + result uint256.Int + }{ + "ab": { + a: *uint256.NewInt(1), + b: *uint256.NewInt(0), + result: *uint256.NewInt(1), + }, + "a==b": { + a: *uint256.NewInt(0), + b: *uint256.NewInt(0), + result: *uint256.NewInt(0), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + + ctxt := getEmptyContext() + ctxt.stack = fillStack(test.a, test.b) + + opDup2_Lt(&ctxt) + + if want, got := 2, ctxt.stack.stackPointer; want != got { + t.Errorf("unexpected stack size, got %v, want %v", got, want) + } + + if want, got := test.result, ctxt.stack.peek(); want.Cmp(got) != 0 { + t.Errorf("unexpected result, got %v, expected %v", got, want) + } + }) + } +} From 23234ca7803af1ff99fb736c69f4fb4190faccd3 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Nov 2025 11:48:54 +0100 Subject: [PATCH 2/7] Rename lfvm to sfvm --- go/interpreter/sfvm/converter.go | 10 +-- go/interpreter/sfvm/converter_fuzz_test.go | 40 +++++----- go/interpreter/sfvm/converter_test.go | 28 +++---- go/interpreter/sfvm/ct.go | 64 ++++++++-------- go/interpreter/sfvm/ct_test.go | 76 +++++++++---------- go/interpreter/sfvm/errors.go | 2 +- go/interpreter/sfvm/example_code_test.go | 2 +- go/interpreter/sfvm/gas.go | 2 +- go/interpreter/sfvm/gas_test.go | 2 +- go/interpreter/sfvm/hash_cache.go | 2 +- go/interpreter/sfvm/hash_cache_test.go | 2 +- go/interpreter/sfvm/instruction.go | 6 +- go/interpreter/sfvm/instruction_logger.go | 2 +- .../sfvm/instruction_logger_test.go | 2 +- .../sfvm/instruction_statistcs_test.go | 2 +- go/interpreter/sfvm/instruction_statistics.go | 2 +- go/interpreter/sfvm/instruction_test.go | 2 +- go/interpreter/sfvm/instructions.go | 2 +- go/interpreter/sfvm/instructions_test.go | 2 +- go/interpreter/sfvm/interpreter.go | 6 +- go/interpreter/sfvm/interpreter_mock.go | 6 +- go/interpreter/sfvm/interpreter_test.go | 6 +- go/interpreter/sfvm/keccak.go | 6 +- go/interpreter/sfvm/keccak.h | 4 +- go/interpreter/sfvm/keccak_test.go | 2 +- go/interpreter/sfvm/memory.go | 2 +- go/interpreter/sfvm/memory_test.go | 2 +- go/interpreter/sfvm/opcode.go | 10 +-- go/interpreter/sfvm/opcode_test.go | 2 +- go/interpreter/sfvm/{lfvm.go => sfvm.go} | 30 ++++---- ...terface_test.go => sfvm_interface_test.go} | 22 +++--- .../sfvm/{lfvm_test.go => sfvm_test.go} | 38 +++++----- go/interpreter/sfvm/stack.go | 2 +- go/interpreter/sfvm/stack_test.go | 2 +- go/interpreter/sfvm/stack_usage.go | 2 +- go/interpreter/sfvm/stack_usage_test.go | 2 +- go/interpreter/sfvm/super_instructions.go | 2 +- .../sfvm/super_instructions_test.go | 2 +- 38 files changed, 199 insertions(+), 199 deletions(-) rename go/interpreter/sfvm/{lfvm.go => sfvm.go} (81%) rename go/interpreter/sfvm/{lfvm_interface_test.go => sfvm_interface_test.go} (75%) rename go/interpreter/sfvm/{lfvm_test.go => sfvm_test.go} (56%) diff --git a/go/interpreter/sfvm/converter.go b/go/interpreter/sfvm/converter.go index d5d1bc17..ea2cc525 100644 --- a/go/interpreter/sfvm/converter.go +++ b/go/interpreter/sfvm/converter.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "math" @@ -33,7 +33,7 @@ type ConversionConfig struct { WithSuperInstructions bool } -// Converter converts EVM code to LFVM code. +// Converter converts EVM code to SFVM code. type Converter struct { config ConversionConfig cache *lru.Cache[tosca.Hash, Code] @@ -61,7 +61,7 @@ func NewConverter(config ConversionConfig) (*Converter, error) { }, nil } -// Convert converts EVM code to LFVM code. If the provided code hash is not nil, +// Convert converts EVM code to SFVM code. If the provided code hash is not nil, // it is assumed to be a valid hash of the code and is used to cache the // conversion result. If the hash is nil, the conversion result is not cached. func (c *Converter) Convert(code []byte, codeHash *tosca.Hash) (Code, error) { @@ -143,12 +143,12 @@ func convert(code []byte, options ConversionConfig) Code { return convertWithObserver(code, options, func(int, int) {}) } -// convertWithObserver converts EVM code to LFVM code and calls the observer +// convertWithObserver converts EVM code to SFVM code and calls the observer // with the code position of every pair of instructions converted. func convertWithObserver( code []byte, options ConversionConfig, - observer func(evmPc int, lfvmPc int), + observer func(evmPc int, sfvmPc int), ) Code { res := newCodeBuilder(len(code)) diff --git a/go/interpreter/sfvm/converter_fuzz_test.go b/go/interpreter/sfvm/converter_fuzz_test.go index 92f06e07..f5b8a248 100644 --- a/go/interpreter/sfvm/converter_fuzz_test.go +++ b/go/interpreter/sfvm/converter_fuzz_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "math" @@ -18,9 +18,9 @@ import ( ) // To run this fuzzer use the following command: -// go test ./interpreter/lfvm -run none -fuzz LfvmConverter --fuzztime 10m +// go test ./interpreter/sfvm -run none -fuzz SfvmConverter --fuzztime 10m -func FuzzLfvmConverter(f *testing.F) { +func FuzzSfvmConverter(f *testing.F) { // Add empty code f.Add([]byte{}) @@ -42,12 +42,12 @@ func FuzzLfvmConverter(f *testing.F) { } mapping := make([]int, len(toscaCode)) - lfvmCode := convertWithObserver(toscaCode, ConversionConfig{}, func(evm, lfvm int) { - mapping[evm] = lfvm + sfvmCode := convertWithObserver(toscaCode, ConversionConfig{}, func(evm, sfvm int) { + mapping[evm] = sfvm }) // Check that no super-instructions have been used. - for _, op := range lfvmCode { + for _, op := range sfvmCode { if op.opcode.isSuperInstruction() { t.Errorf("Super-instruction %v used", op.opcode) } @@ -56,29 +56,29 @@ func FuzzLfvmConverter(f *testing.F) { // Check that all operations are mapped to matching operations. for i := 0; i < len(toscaCode); i++ { originalPos := i - lfvmPos := mapping[originalPos] + sfvmPos := mapping[originalPos] toscaOpCode := vm.OpCode(toscaCode[originalPos]) - lfvmOpCode := lfvmCode[lfvmPos].opcode + sfvmOpCode := sfvmCode[sfvmPos].opcode - if !lfvmOpCode.isBaseInstruction() { - t.Errorf("Expected base instructions only, got %v", lfvmOpCode) + if !sfvmOpCode.isBaseInstruction() { + t.Errorf("Expected base instructions only, got %v", sfvmOpCode) } - if vm.OpCode(lfvmOpCode) != toscaOpCode { - t.Errorf("Invalid conversion from %v to %v", toscaOpCode, lfvmOpCode) + if vm.OpCode(sfvmOpCode) != toscaOpCode { + t.Errorf("Invalid conversion from %v to %v", toscaOpCode, sfvmOpCode) } // Check that the position of JUMPDEST ops are preserved. if toscaOpCode == vm.JUMPDEST { - if originalPos != lfvmPos { - t.Errorf("Expected JUMPDEST at %d, got %d", originalPos, lfvmPos) + if originalPos != sfvmPos { + t.Errorf("Expected JUMPDEST at %d, got %d", originalPos, sfvmPos) } } // Check that PC instructions point to the correct target. if toscaOpCode == vm.PC { - target := int(lfvmCode[lfvmPos].arg) + target := int(sfvmCode[sfvmPos].arg) if target != originalPos { t.Errorf("Invalid PC target, wanted %d, got %d", originalPos, target) } @@ -91,17 +91,17 @@ func FuzzLfvmConverter(f *testing.F) { } // Check that JUMP_TO instructions point to their immediately succeeding JUMPDEST. - for i := 0; i < len(lfvmCode); i++ { - if lfvmCode[i].opcode == JUMP_TO { - trg := int(lfvmCode[i].arg) + for i := 0; i < len(sfvmCode); i++ { + if sfvmCode[i].opcode == JUMP_TO { + trg := int(sfvmCode[i].arg) if trg < i { t.Errorf("invalid JUMP_TO target from %d to %d", i, trg) } - if trg >= len(lfvmCode) || lfvmCode[trg].opcode != JUMPDEST { + if trg >= len(sfvmCode) || sfvmCode[trg].opcode != JUMPDEST { t.Fatalf("JUMP_TO target %d is not a JUMPDEST", trg) } for j := i + 1; j < trg; j++ { - cur := lfvmCode[j].opcode + cur := sfvmCode[j].opcode if cur != NOOP { t.Errorf("found %v between JUMP_TO and JUMPDEST at %d", cur, j) } diff --git a/go/interpreter/sfvm/converter_test.go b/go/interpreter/sfvm/converter_test.go index de04207c..e05f12a6 100644 --- a/go/interpreter/sfvm/converter_test.go +++ b/go/interpreter/sfvm/converter_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" @@ -234,7 +234,7 @@ func TestConverter_ConverterIsThreadSafe(t *testing.T) { } } -func TestConvertWithObserver_MapsEvmToLfvmPositions(t *testing.T) { +func TestConvertWithObserver_MapsEvmToSfvmPositions(t *testing.T) { code := []byte{ byte(vm.ADD), byte(vm.PUSH1), 1, @@ -244,11 +244,11 @@ func TestConvertWithObserver_MapsEvmToLfvmPositions(t *testing.T) { } type pair struct { - evm, lfvm int + evm, sfvm int } var pairs []pair - res := convertWithObserver(code, ConversionConfig{}, func(evm, lfvm int) { - pairs = append(pairs, pair{evm, lfvm}) + res := convertWithObserver(code, ConversionConfig{}, func(evm, sfvm int) { + pairs = append(pairs, pair{evm, sfvm}) }) want := []pair{ @@ -264,7 +264,7 @@ func TestConvertWithObserver_MapsEvmToLfvmPositions(t *testing.T) { } for _, p := range pairs { - if want, got := OpCode(code[p.evm]), res[p.lfvm].opcode; want != got { + if want, got := OpCode(code[p.evm]), res[p.sfvm].opcode; want != got { t.Errorf("Expected %v, got %v", want, got) } } @@ -278,25 +278,25 @@ func TestConvertWithObserver_PreservesJumpDestLocations(t *testing.T) { r.Read(code) mapping := map[int]int{} - res := convertWithObserver(code, ConversionConfig{}, func(evm, lfvm int) { + res := convertWithObserver(code, ConversionConfig{}, func(evm, sfvm int) { if _, found := mapping[evm]; found { t.Errorf("Duplicate mapping for EVM position %d", evm) } - mapping[evm] = lfvm + mapping[evm] = sfvm }) // Check that all operations are mapped to matching operations. - for evm, lfvm := range mapping { - if want, got := OpCode(code[evm]), res[lfvm].opcode; want != got { + for evm, sfvm := range mapping { + if want, got := OpCode(code[evm]), res[sfvm].opcode; want != got { t.Errorf("Expected %v, got %v", want, got) } } // Check that the position of JUMPDESTs is preserved. - for evm, lfvm := range mapping { + for evm, sfvm := range mapping { if vm.OpCode(code[evm]) == vm.JUMPDEST { - if evm != lfvm { - t.Errorf("Expected JUMPDEST at %d, got %d", evm, lfvm) + if evm != sfvm { + t.Errorf("Expected JUMPDEST at %d, got %d", evm, sfvm) } } } @@ -483,7 +483,7 @@ func TestConvert_SI_WhenDisabledNoSuperInstructionsAreUsed(t *testing.T) { } } -func TestConverter_SI_FallsBackToLFVMInstructionsWhenNoSuperInstructionIsFit(t *testing.T) { +func TestConverter_SI_FallsBackToSFVMInstructionsWhenNoSuperInstructionIsFit(t *testing.T) { config := ConversionConfig{ WithSuperInstructions: true, diff --git a/go/interpreter/sfvm/ct.go b/go/interpreter/sfvm/ct.go index 479beb44..6100f9dc 100644 --- a/go/interpreter/sfvm/ct.go +++ b/go/interpreter/sfvm/ct.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" @@ -36,7 +36,7 @@ func NewConformanceTestingTarget() ct.Evm { } type ctAdapter struct { - vm *lfvm + vm *sfvm pcMapCache *lru.Cache[[32]byte, *pcMap] } @@ -61,16 +61,16 @@ func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { pcMap := a.getPcMap(state.Code) - memory := convertCtMemoryToLfvmMemory(state.Memory) + memory := convertCtMemoryToSfvmMemory(state.Memory) // Set up execution context. var ctxt = &context{ - pc: int32(pcMap.evmToLfvm[state.Pc]), + pc: int32(pcMap.evmToSfvm[state.Pc]), params: params, context: params.Context, gas: params.Gas, refund: tosca.Gas(state.GasRefund), - stack: convertCtStackToLfvmStack(state.Stack), + stack: convertCtStackToSfvmStack(state.Stack), memory: memory, code: converted, returnData: state.LastCallReturnData.ToBytes(), @@ -88,16 +88,16 @@ func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { } // Update the resulting state. - state.Status = convertLfvmStatusToCtStatus(status) + state.Status = convertSfvmStatusToCtStatus(status) if status == statusRunning { - state.Pc = pcMap.lfvmToEvm[ctxt.pc] + state.Pc = pcMap.sfvmToEvm[ctxt.pc] } state.Gas = ctxt.gas state.GasRefund = ctxt.refund - state.Stack = convertLfvmStackToCtStack(ctxt.stack, state.Stack) - state.Memory = convertLfvmMemoryToCtMemory(ctxt.memory) + state.Stack = convertSfvmStackToCtStack(ctxt.stack, state.Stack) + state.Memory = convertSfvmMemoryToCtMemory(ctxt.memory) state.LastCallReturnData = common.NewBytes(ctxt.returnData) if status == statusReturned || status == statusReverted { state.ReturnData = common.NewBytes(ctxt.returnData) @@ -118,52 +118,52 @@ func (a *ctAdapter) getPcMap(code *st.Code) *pcMap { return pcMap } -// pcMap is a bidirectional map to map program counters between evm <-> lfvm. +// pcMap is a bidirectional map to map program counters between evm <-> sfvm. type pcMap struct { - evmToLfvm []uint16 - lfvmToEvm []uint16 + evmToSfvm []uint16 + sfvmToEvm []uint16 } // genPcMap creates a bidirectional program counter map for a given code, -// allowing mapping from a program counter in evm code to lfvm and vice versa. +// allowing mapping from a program counter in evm code to sfvm and vice versa. func genPcMap(code []byte) *pcMap { - evmToLfvm := make([]uint16, len(code)+1) - lfvmToEvm := make([]uint16, len(code)+1) + evmToSfvm := make([]uint16, len(code)+1) + sfvmToEvm := make([]uint16, len(code)+1) config := ConversionConfig{ WithSuperInstructions: false, } - res := convertWithObserver(code, config, func(evm, lfvm int) { - evmToLfvm[evm] = uint16(lfvm) - lfvmToEvm[lfvm] = uint16(evm) + res := convertWithObserver(code, config, func(evm, sfvm int) { + evmToSfvm[evm] = uint16(sfvm) + sfvmToEvm[sfvm] = uint16(evm) }) // A program counter may correctly point to the position after the last // instruction, which would lead to an implicit STOP. - evmToLfvm[len(code)] = uint16(len(res)) + evmToSfvm[len(code)] = uint16(len(res)) - // The LFVM code could also be longer than the input code if extra padding + // The SFVM code could also be longer than the input code if extra padding // of truncated PUSH instructions has been added. - if len(res)+1 > len(lfvmToEvm) { - lfvmToEvm = append(lfvmToEvm, make([]uint16, len(res)+1-len(lfvmToEvm))...) + if len(res)+1 > len(sfvmToEvm) { + sfvmToEvm = append(sfvmToEvm, make([]uint16, len(res)+1-len(sfvmToEvm))...) } - lfvmToEvm[len(res)] = uint16(len(code)) + sfvmToEvm[len(res)] = uint16(len(code)) - // Locations pointing to JUMP_TO instructions in LFVM need to be updated to + // Locations pointing to JUMP_TO instructions in SFVM need to be updated to // the position of the jump target. for i := 0; i < len(res); i++ { if res[i].opcode == JUMP_TO { - lfvmToEvm[i] = res[i].arg + sfvmToEvm[i] = res[i].arg } } return &pcMap{ - evmToLfvm: evmToLfvm, - lfvmToEvm: lfvmToEvm, + evmToSfvm: evmToSfvm, + sfvmToEvm: sfvmToEvm, } } -func convertLfvmStatusToCtStatus(status status) st.StatusCode { +func convertSfvmStatusToCtStatus(status status) st.StatusCode { switch status { case statusRunning: return st.Running @@ -179,7 +179,7 @@ func convertLfvmStatusToCtStatus(status status) st.StatusCode { return st.Failed } -func convertCtStackToLfvmStack(stack *st.Stack) *stack { +func convertCtStackToSfvmStack(stack *st.Stack) *stack { result := NewStack() for i := stack.Size() - 1; i >= 0; i-- { val := stack.Get(i).Uint256() @@ -188,7 +188,7 @@ func convertCtStackToLfvmStack(stack *st.Stack) *stack { return result } -func convertLfvmStackToCtStack(stack *stack, result *st.Stack) *st.Stack { +func convertSfvmStackToCtStack(stack *stack, result *st.Stack) *st.Stack { len := stack.len() result.Resize(len) for i := 0; i < len; i++ { @@ -197,7 +197,7 @@ func convertLfvmStackToCtStack(stack *stack, result *st.Stack) *st.Stack { return result } -func convertCtMemoryToLfvmMemory(memory *st.Memory) *Memory { +func convertCtMemoryToSfvmMemory(memory *st.Memory) *Memory { data := memory.Read(0, uint64(memory.Size())) mem := NewMemory() words := tosca.SizeInWords(uint64(len(data))) @@ -207,7 +207,7 @@ func convertCtMemoryToLfvmMemory(memory *st.Memory) *Memory { return mem } -func convertLfvmMemoryToCtMemory(memory *Memory) *st.Memory { +func convertSfvmMemoryToCtMemory(memory *Memory) *st.Memory { result := st.NewMemory() result.Set(memory.store) return result diff --git a/go/interpreter/sfvm/ct_test.go b/go/interpreter/sfvm/ct_test.go index 6b7950e3..4cbc37c0 100644 --- a/go/interpreter/sfvm/ct_test.go +++ b/go/interpreter/sfvm/ct_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" @@ -163,9 +163,9 @@ func TestCtAdapter_FillsReturnDataOnResultingState(t *testing.T) { } //////////////////////////////////////////////////////////// -// ct -> lfvm +// ct -> sfvm -func TestConvertToLfvm_StatusCode(t *testing.T) { +func TestConvertToSfvm_StatusCode(t *testing.T) { tests := map[status]st.StatusCode{ statusRunning: st.Running, @@ -177,25 +177,25 @@ func TestConvertToLfvm_StatusCode(t *testing.T) { } for status, test := range tests { - got := convertLfvmStatusToCtStatus(status) + got := convertSfvmStatusToCtStatus(status) if want, got := test, got; want != got { t.Errorf("unexpected conversion, wanted %v, got %v", want, got) } } } -func TestConvertToLfvm_StatusCodeFailsOnUnknownStatus(t *testing.T) { - status := convertLfvmStatusToCtStatus(statusFailed + 1) +func TestConvertToSfvm_StatusCodeFailsOnUnknownStatus(t *testing.T) { + status := convertSfvmStatusToCtStatus(statusFailed + 1) if status != st.Failed { t.Errorf("unexpected conversion, wanted %v, got %v", st.Failed, status) } } -func TestConvertToLfvm_Pc(t *testing.T) { +func TestConvertToSfvm_Pc(t *testing.T) { tests := map[string][]struct { evmCode []byte evmPc uint16 - lfvmPc uint16 + sfvmPc uint16 }{ "empty": {{}}, "pos-0": {{[]byte{byte(vm.STOP)}, 0, 0}}, @@ -220,8 +220,8 @@ func TestConvertToLfvm_Pc(t *testing.T) { t.Run(name, func(t *testing.T) { for _, cur := range test { pcMap := genPcMap(cur.evmCode) - lfvmPc := pcMap.evmToLfvm[cur.evmPc] - if want, got := cur.lfvmPc, lfvmPc; want != got { + sfvmPc := pcMap.evmToSfvm[cur.evmPc] + if want, got := cur.sfvmPc, sfvmPc; want != got { t.Errorf("invalid conversion, wanted %d, got %d", want, got) } } @@ -229,10 +229,10 @@ func TestConvertToLfvm_Pc(t *testing.T) { } } -func TestConvertToLfvm_Code(t *testing.T) { +func TestConvertToSfvm_Code(t *testing.T) { tests := map[string][]struct { evmCode []byte - lfvmCode Code + sfvmCode Code }{ "empty": {{}}, "stop": {{[]byte{byte(vm.STOP)}, Code{Instruction{STOP, 0x0000}}}}, @@ -315,7 +315,7 @@ func TestConvertToLfvm_Code(t *testing.T) { for _, cur := range test { got := convert(cur.evmCode, ConversionConfig{}) - want := cur.lfvmCode + want := cur.sfvmCode if wantSize, gotSize := len(want), len(got); wantSize != gotSize { t.Fatalf("unexpected code size, wanted %d, got %d", wantSize, gotSize) @@ -331,7 +331,7 @@ func TestConvertToLfvm_Code(t *testing.T) { } } -func TestConvertToLfvm_CodeWithSuperInstructions(t *testing.T) { +func TestConvertToSfvm_CodeWithSuperInstructions(t *testing.T) { tests := map[string]struct { evmCode []byte want Code @@ -432,8 +432,8 @@ func TestConvertToLfvm_CodeWithSuperInstructions(t *testing.T) { } } -func TestConvertToLfvm_Stack(t *testing.T) { - newLfvmStack := func(values ...cc.U256) *stack { +func TestConvertToSfvm_Stack(t *testing.T) { + newSfvmStack := func(values ...cc.U256) *stack { stack := NewStack() for i := 0; i < len(values); i++ { value := values[i].Uint256() @@ -444,36 +444,36 @@ func TestConvertToLfvm_Stack(t *testing.T) { tests := map[string]struct { ctStack *st.Stack - lfvmStack *stack + sfvmStack *stack }{ "empty": { st.NewStack(), - newLfvmStack()}, + newSfvmStack()}, "one-element": { st.NewStack(cc.NewU256(7)), - newLfvmStack(cc.NewU256(7))}, + newSfvmStack(cc.NewU256(7))}, "two-elements": { st.NewStack(cc.NewU256(1), cc.NewU256(2)), - newLfvmStack(cc.NewU256(1), cc.NewU256(2))}, + newSfvmStack(cc.NewU256(1), cc.NewU256(2))}, "three-elements": { st.NewStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3)), - newLfvmStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3))}, + newSfvmStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3))}, } for name, test := range tests { t.Run(name, func(t *testing.T) { - stack := convertCtStackToLfvmStack(test.ctStack) - if want, got := test.lfvmStack.len(), stack.len(); want != got { + stack := convertCtStackToSfvmStack(test.ctStack) + if want, got := test.sfvmStack.len(), stack.len(); want != got { t.Fatalf("unexpected stack size, wanted %v, got %v", want, got) } for i := 0; i < stack.len(); i++ { - want := *test.lfvmStack.get(i) + want := *test.sfvmStack.get(i) got := *stack.get(i) if want != got { t.Errorf("unexpected stack value, wanted %v, got %v", want, got) } } - ReturnStack(test.lfvmStack) + ReturnStack(test.sfvmStack) ReturnStack(stack) test.ctStack.Release() }) @@ -481,12 +481,12 @@ func TestConvertToLfvm_Stack(t *testing.T) { } //////////////////////////////////////////////////////////// -// lfvm -> ct +// sfvm -> ct func TestConvertToCt_Pc(t *testing.T) { tests := map[string][]struct { evmCode []byte - lfvmPc uint16 + sfvmPc uint16 evmPc uint16 }{ "empty": {{}}, @@ -512,7 +512,7 @@ func TestConvertToCt_Pc(t *testing.T) { t.Run(name, func(t *testing.T) { for _, cur := range test { pcMap := genPcMap(cur.evmCode) - evmPc := pcMap.lfvmToEvm[cur.lfvmPc] + evmPc := pcMap.sfvmToEvm[cur.sfvmPc] if want, got := cur.evmPc, evmPc; want != got { t.Errorf("invalid conversion, wanted %d, got %d", want, got) } @@ -522,7 +522,7 @@ func TestConvertToCt_Pc(t *testing.T) { } func TestConvertToCt_Stack(t *testing.T) { - newLfvmStack := func(values ...cc.U256) *stack { + newSfvmStack := func(values ...cc.U256) *stack { stack := NewStack() for i := 0; i < len(values); i++ { value := values[i].Uint256() @@ -532,20 +532,20 @@ func TestConvertToCt_Stack(t *testing.T) { } tests := map[string]struct { - lfvmStack *stack + sfvmStack *stack ctStack *st.Stack }{ "empty": { - newLfvmStack(), + newSfvmStack(), st.NewStack()}, "one-element": { - newLfvmStack(cc.NewU256(7)), + newSfvmStack(cc.NewU256(7)), st.NewStack(cc.NewU256(7))}, "two-elements": { - newLfvmStack(cc.NewU256(1), cc.NewU256(2)), + newSfvmStack(cc.NewU256(1), cc.NewU256(2)), st.NewStack(cc.NewU256(1), cc.NewU256(2))}, "three-elements": { - newLfvmStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3)), + newSfvmStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3)), st.NewStack(cc.NewU256(1), cc.NewU256(2), cc.NewU256(3))}, } @@ -553,26 +553,26 @@ func TestConvertToCt_Stack(t *testing.T) { t.Run(name, func(t *testing.T) { want := test.ctStack ctStack := st.NewStack() - got := convertLfvmStackToCtStack(test.lfvmStack, ctStack) + got := convertSfvmStackToCtStack(test.sfvmStack, ctStack) diffs := got.Diff(want) for _, diff := range diffs { t.Errorf("%s", diff) } - ReturnStack(test.lfvmStack) + ReturnStack(test.sfvmStack) test.ctStack.Release() ctStack.Release() }) } } -func BenchmarkLfvmStackToCtStack(b *testing.B) { +func BenchmarkSfvmStackToCtStack(b *testing.B) { stack := NewStack() for i := 0; i < MAX_STACK_SIZE/2; i++ { stack.pushUndefined().SetUint64(uint64(i)) } ctStack := st.NewStack() for i := 0; i < b.N; i++ { - convertLfvmStackToCtStack(stack, ctStack) + convertSfvmStackToCtStack(stack, ctStack) } } diff --git a/go/interpreter/sfvm/errors.go b/go/interpreter/sfvm/errors.go index 98c8eedc..c0166b4b 100644 --- a/go/interpreter/sfvm/errors.go +++ b/go/interpreter/sfvm/errors.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import "github.com/0xsoniclabs/tosca/go/tosca" diff --git a/go/interpreter/sfvm/example_code_test.go b/go/interpreter/sfvm/example_code_test.go index 6d8e3a90..53f9785f 100644 --- a/go/interpreter/sfvm/example_code_test.go +++ b/go/interpreter/sfvm/example_code_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm // An example contract captured for testing and benchmarking. var longExampleCode = []byte{ diff --git a/go/interpreter/sfvm/gas.go b/go/interpreter/sfvm/gas.go index aa903ba1..f22f4e63 100644 --- a/go/interpreter/sfvm/gas.go +++ b/go/interpreter/sfvm/gas.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "github.com/0xsoniclabs/tosca/go/tosca" diff --git a/go/interpreter/sfvm/gas_test.go b/go/interpreter/sfvm/gas_test.go index 1b28aaaf..3fdc8af4 100644 --- a/go/interpreter/sfvm/gas_test.go +++ b/go/interpreter/sfvm/gas_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "testing" diff --git a/go/interpreter/sfvm/hash_cache.go b/go/interpreter/sfvm/hash_cache.go index 33686ba4..d209c63b 100644 --- a/go/interpreter/sfvm/hash_cache.go +++ b/go/interpreter/sfvm/hash_cache.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "sync" diff --git a/go/interpreter/sfvm/hash_cache_test.go b/go/interpreter/sfvm/hash_cache_test.go index 63a5d454..348eb6f7 100644 --- a/go/interpreter/sfvm/hash_cache_test.go +++ b/go/interpreter/sfvm/hash_cache_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/instruction.go b/go/interpreter/sfvm/instruction.go index e08bdf93..21b4c770 100644 --- a/go/interpreter/sfvm/instruction.go +++ b/go/interpreter/sfvm/instruction.go @@ -8,14 +8,14 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" "fmt" ) -// Instruction encodes an instruction for the long-form virtual machine (LFVM). +// Instruction encodes an instruction for the long-form virtual machine (SFVM). type Instruction struct { // The op-code of this instruction. opcode OpCode @@ -23,7 +23,7 @@ type Instruction struct { arg uint16 } -// Code for the LFVM is a slice of instructions. +// Code for the SFVM is a slice of instructions. type Code []Instruction func (i Instruction) String() string { diff --git a/go/interpreter/sfvm/instruction_logger.go b/go/interpreter/sfvm/instruction_logger.go index 99584440..872adddd 100644 --- a/go/interpreter/sfvm/instruction_logger.go +++ b/go/interpreter/sfvm/instruction_logger.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/instruction_logger_test.go b/go/interpreter/sfvm/instruction_logger_test.go index de93f238..a4d3748f 100644 --- a/go/interpreter/sfvm/instruction_logger_test.go +++ b/go/interpreter/sfvm/instruction_logger_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" diff --git a/go/interpreter/sfvm/instruction_statistcs_test.go b/go/interpreter/sfvm/instruction_statistcs_test.go index 7c2b180e..9af10b0c 100644 --- a/go/interpreter/sfvm/instruction_statistcs_test.go +++ b/go/interpreter/sfvm/instruction_statistcs_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/instruction_statistics.go b/go/interpreter/sfvm/instruction_statistics.go index 89f6eeaf..f0a48949 100644 --- a/go/interpreter/sfvm/instruction_statistics.go +++ b/go/interpreter/sfvm/instruction_statistics.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/instruction_test.go b/go/interpreter/sfvm/instruction_test.go index 48687986..b83a2924 100644 --- a/go/interpreter/sfvm/instruction_test.go +++ b/go/interpreter/sfvm/instruction_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import "testing" diff --git a/go/interpreter/sfvm/instructions.go b/go/interpreter/sfvm/instructions.go index 75719033..67b7f2a9 100644 --- a/go/interpreter/sfvm/instructions.go +++ b/go/interpreter/sfvm/instructions.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" diff --git a/go/interpreter/sfvm/instructions_test.go b/go/interpreter/sfvm/instructions_test.go index 455599ee..2ed1dda2 100644 --- a/go/interpreter/sfvm/instructions_test.go +++ b/go/interpreter/sfvm/instructions_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" diff --git a/go/interpreter/sfvm/interpreter.go b/go/interpreter/sfvm/interpreter.go index 39e9c452..6f993d3d 100644 --- a/go/interpreter/sfvm/interpreter.go +++ b/go/interpreter/sfvm/interpreter.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" @@ -16,7 +16,7 @@ import ( "github.com/0xsoniclabs/tosca/go/tosca" ) -//go:generate mockgen -source interpreter.go -destination interpreter_mock.go -package lfvm +//go:generate mockgen -source interpreter.go -destination interpreter_mock.go -package sfvm // status is enumeration of the execution state of an interpreter run. type status byte @@ -38,7 +38,7 @@ type context struct { // Inputs params tosca.Parameters context tosca.RunContext - code Code // the contract code in LFVM format + code Code // the contract code in SFVM format // Execution state pc int32 diff --git a/go/interpreter/sfvm/interpreter_mock.go b/go/interpreter/sfvm/interpreter_mock.go index 944ff591..9a87586a 100644 --- a/go/interpreter/sfvm/interpreter_mock.go +++ b/go/interpreter/sfvm/interpreter_mock.go @@ -13,11 +13,11 @@ // // Generated by this command: // -// mockgen -source interpreter.go -destination interpreter_mock.go -package lfvm +// mockgen -source interpreter.go -destination interpreter_mock.go -package sfvm // -// Package lfvm is a generated GoMock package. -package lfvm +// Package sfvm is a generated GoMock package. +package sfvm import ( reflect "reflect" diff --git a/go/interpreter/sfvm/interpreter_test.go b/go/interpreter/sfvm/interpreter_test.go index 73cf080a..974b2081 100644 --- a/go/interpreter/sfvm/interpreter_test.go +++ b/go/interpreter/sfvm/interpreter_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" @@ -633,7 +633,7 @@ func Test_generateCodeForOps(t *testing.T) { } } -// generateCodeFor generates valid LFVM code for one instruction. +// generateCodeFor generates valid SFVM code for one instruction. // Appends necessary DATA instructions to the code to satisfy stack requirements. // Adds JUMPDEST instruction after JUMP instructions. func generateCodeFor(op OpCode) Code { @@ -718,7 +718,7 @@ func isJump(op OpCode) bool { func benchmarkFib(b *testing.B, arg int, with_super_instructions bool) { example := getFibExample() - // Convert example to LFVM format. + // Convert example to SFVM format. converted := convert(example.code, ConversionConfig{WithSuperInstructions: with_super_instructions}) // Create input data. diff --git a/go/interpreter/sfvm/keccak.go b/go/interpreter/sfvm/keccak.go index 669adac6..764e9607 100644 --- a/go/interpreter/sfvm/keccak.go +++ b/go/interpreter/sfvm/keccak.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm /* #include "keccak.h" @@ -55,14 +55,14 @@ func keccak256_C(data []byte) tosca.Hash { if len(data) == 0 { return emptyKeccak256Hash } - res := C.tosca_lfvm_keccak256(unsafe.Pointer(&data[0]), C.size_t(len(data))) + res := C.tosca_sfvm_keccak256(unsafe.Pointer(&data[0]), C.size_t(len(data))) return tosca.Hash(res) } func keccak256_C_32byte(data [32]byte) tosca.Hash { // The address is passed as 4x 64-bit integer values through the stack to // avoid the need of allocating heap memory for the key. - return tosca.Hash(C.tosca_lfvm_keccak256_32byte( + return tosca.Hash(C.tosca_sfvm_keccak256_32byte( C.uint64_t( uint64(data[7])<<56|uint64(data[6])<<48|uint64(data[5])<<40|uint64(data[4])<<32| uint64(data[3])<<24|uint64(data[2])<<16|uint64(data[1])<<8|uint64(data[0])<<0), diff --git a/go/interpreter/sfvm/keccak.h b/go/interpreter/sfvm/keccak.h index 649e261c..25c2bd9c 100644 --- a/go/interpreter/sfvm/keccak.h +++ b/go/interpreter/sfvm/keccak.h @@ -391,7 +391,7 @@ static inline ALWAYS_INLINE void keccak(uint64_t *out, size_t bits, out[i] = to_le64(state[i]); } -union ethash_hash256 tosca_lfvm_keccak256(const void *in, +union ethash_hash256 tosca_sfvm_keccak256(const void *in, size_t size) noexcept { union ethash_hash256 hash; keccak(hash.word64s, 256, (const uint8_t *)in, size); @@ -439,7 +439,7 @@ static inline ALWAYS_INLINE union ethash_hash256 keccak_32(const uint64_t a, return res; } -union ethash_hash256 tosca_lfvm_keccak256_32byte(const uint64_t a, +union ethash_hash256 tosca_sfvm_keccak256_32byte(const uint64_t a, const uint64_t b, const uint64_t c, const uint64_t d) noexcept { diff --git a/go/interpreter/sfvm/keccak_test.go b/go/interpreter/sfvm/keccak_test.go index da218259..f33e6a90 100644 --- a/go/interpreter/sfvm/keccak_test.go +++ b/go/interpreter/sfvm/keccak_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/memory.go b/go/interpreter/sfvm/memory.go index c4a445e4..09514df6 100644 --- a/go/interpreter/sfvm/memory.go +++ b/go/interpreter/sfvm/memory.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "github.com/0xsoniclabs/tosca/go/tosca" diff --git a/go/interpreter/sfvm/memory_test.go b/go/interpreter/sfvm/memory_test.go index 9c5701b8..80b2cfc4 100644 --- a/go/interpreter/sfvm/memory_test.go +++ b/go/interpreter/sfvm/memory_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "bytes" diff --git a/go/interpreter/sfvm/opcode.go b/go/interpreter/sfvm/opcode.go index 74351259..dac9df1e 100644 --- a/go/interpreter/sfvm/opcode.go +++ b/go/interpreter/sfvm/opcode.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" @@ -24,8 +24,8 @@ type OpCode uint16 // The motivation for this is that the long-form EVM has a number of OpCodes // that are not part of the original EVM. For those, values beyond the range // [0-255] of the EVM's single-byte OpCodes are used. To that end, the OpCode -// data type in the LFVM is increased to 16 bits. However, in several places -// maps from LFVM OpCodes to properties are required to provide efficient +// data type in the SFVM is increased to 16 bits. However, in several places +// maps from SFVM OpCodes to properties are required to provide efficient // lookup tables for properties. To avoid the need to maintain tables of // 2^16 entries, the number of relevant bits is reduced to 9. Any leading bits // are ignored when comparing OpCodes. @@ -34,7 +34,7 @@ const opCodeMask = 0x1ff // numOpCodes is the maximum number of OpCodes that can be defined. const numOpCodes = opCodeMask + 1 -// The following constants define the original EVM OpCodes, in the lfvm OpCode space. +// The following constants define the original EVM OpCodes, in the sfvm OpCode space. const ( // Stack operations POP = OpCode(vm.POP) @@ -237,7 +237,7 @@ const ( // - they must target the immediate succeeding JUMPDEST instruction // - all instructions between the JUMP_TO and the JUMPDEST must be NOOPs // - // These restrictions are enforced during the EVM to LFVM code conversion. + // These restrictions are enforced during the EVM to SFVM code conversion. JUMP_TO OpCode = iota + 0x100 // NOOP is a special instruction that does nothing. It is used as a filler diff --git a/go/interpreter/sfvm/opcode_test.go b/go/interpreter/sfvm/opcode_test.go index 020ea2bb..0daa3735 100644 --- a/go/interpreter/sfvm/opcode_test.go +++ b/go/interpreter/sfvm/opcode_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "math" diff --git a/go/interpreter/sfvm/lfvm.go b/go/interpreter/sfvm/sfvm.go similarity index 81% rename from go/interpreter/sfvm/lfvm.go rename to go/interpreter/sfvm/sfvm.go index 55335da7..bee5b926 100644 --- a/go/interpreter/sfvm/lfvm.go +++ b/go/interpreter/sfvm/sfvm.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" @@ -17,13 +17,13 @@ import ( "github.com/0xsoniclabs/tosca/go/tosca" ) -// Config provides a set of user-definable options for the LFVM interpreter. +// Config provides a set of user-definable options for the SFVM interpreter. type Config struct { } -// NewInterpreter creates a new LFVM interpreter instance with the official +// NewInterpreter creates a new SFVM interpreter instance with the official // configuration for production purposes. -func NewInterpreter(Config) (*lfvm, error) { +func NewInterpreter(Config) (*sfvm, error) { return newVm(config{ ConversionConfig: ConversionConfig{ WithSuperInstructions: false, @@ -34,13 +34,13 @@ func NewInterpreter(Config) (*lfvm, error) { // Registers the long-form EVM as a possible interpreter implementation. func init() { - tosca.MustRegisterInterpreterFactory("lfvm", func(any) (tosca.Interpreter, error) { + tosca.MustRegisterInterpreterFactory("sfvm", func(any) (tosca.Interpreter, error) { return NewInterpreter(Config{}) }) } // RegisterExperimentalInterpreterConfigurations registers all experimental -// LFVM interpreter configurations to Tosca's interpreter registry. This +// SFVM interpreter configurations to Tosca's interpreter registry. This // function should not be called in production code, as the resulting VMs are // not officially supported. func RegisterExperimentalInterpreterConfigurations() error { @@ -69,8 +69,8 @@ func RegisterExperimentalInterpreterConfigurations() error { } } - name := "lfvm" + si + shaCache + mode - if name == "lfvm" { + name := "sfvm" + si + shaCache + mode + if name == "sfvm" { continue } @@ -79,7 +79,7 @@ func RegisterExperimentalInterpreterConfigurations() error { } } - configs["lfvm-no-code-cache"] = config{ + configs["sfvm-no-code-cache"] = config{ ConversionConfig: ConversionConfig{CacheSize: -1}, } @@ -104,23 +104,23 @@ type config struct { runner runner } -type lfvm struct { +type sfvm struct { config config converter *Converter } -func newVm(config config) (*lfvm, error) { +func newVm(config config) (*sfvm, error) { converter, err := NewConverter(config.ConversionConfig) if err != nil { return nil, fmt.Errorf("failed to create converter: %v", err) } - return &lfvm{config: config, converter: converter}, nil + return &sfvm{config: config, converter: converter}, nil } // Defines the newest supported revision for this interpreter implementation const newestSupportedRevision = tosca.R15_Osaka -func (e *lfvm) Run(params tosca.Parameters) (tosca.Result, error) { +func (e *sfvm) Run(params tosca.Parameters) (tosca.Result, error) { if params.Revision > newestSupportedRevision { return tosca.Result{}, &tosca.ErrUnsupportedRevision{Revision: params.Revision} } @@ -136,13 +136,13 @@ func (e *lfvm) Run(params tosca.Parameters) (tosca.Result, error) { return run(e.config, params, converted) } -func (e *lfvm) DumpProfile() { +func (e *sfvm) DumpProfile() { if statsRunner, ok := e.config.runner.(*statisticRunner); ok { fmt.Print(statsRunner.getSummary()) } } -func (e *lfvm) ResetProfile() { +func (e *sfvm) ResetProfile() { if statsRunner, ok := e.config.runner.(*statisticRunner); ok { statsRunner.reset() } diff --git a/go/interpreter/sfvm/lfvm_interface_test.go b/go/interpreter/sfvm/sfvm_interface_test.go similarity index 75% rename from go/interpreter/sfvm/lfvm_interface_test.go rename to go/interpreter/sfvm/sfvm_interface_test.go index c2d4e1e4..a532dfba 100644 --- a/go/interpreter/sfvm/lfvm_interface_test.go +++ b/go/interpreter/sfvm/sfvm_interface_test.go @@ -8,45 +8,45 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm_test +package sfvm_test import ( "testing" - "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" + "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" "github.com/0xsoniclabs/tosca/go/tosca" ) -// TestLfvm_RegisterExperimentalConfigurations tests the registration of +// TestSfvm_RegisterExperimentalConfigurations tests the registration of // experimental configurations. // This test is slightly different from other tests because of dealing with the // global registry: // - It is declared in it's own package to avoid leaking the registration to other tests. // - It tests different properties, in one single function. The reason is that the // order of different functions may change, invalidating the test. -func TestLfvm_RegisterExperimentalConfigurations(t *testing.T) { +func TestSfvm_RegisterExperimentalConfigurations(t *testing.T) { // Fist registration must succeed. - err := lfvm.RegisterExperimentalInterpreterConfigurations() + err := sfvm.RegisterExperimentalInterpreterConfigurations() if err != nil { t.Fatalf("failed to register experimental configurations: %v", err) } // Registering a second time must fail. - err = lfvm.RegisterExperimentalInterpreterConfigurations() + err = sfvm.RegisterExperimentalInterpreterConfigurations() if err == nil { t.Fatalf("expected error when registering experimental configurations twice") } - // Check that lfvm is registered by default, in addition to experimental configurations - if _, ok := tosca.GetAllRegisteredInterpreters()["lfvm"]; !ok { - t.Fatalf("lfvm is not registered") + // Check that sfvm is registered by default, in addition to experimental configurations + if _, ok := tosca.GetAllRegisteredInterpreters()["sfvm"]; !ok { + t.Fatalf("sfvm is not registered") } // Construct all registered interpreter configurations for name, factory := range tosca.GetAllRegisteredInterpreters() { t.Run(name, func(t *testing.T) { - vm, err := factory(lfvm.Config{}) + vm, err := factory(sfvm.Config{}) if err != nil { t.Fatalf("failed to create interpreter: %v", err) } @@ -54,7 +54,7 @@ func TestLfvm_RegisterExperimentalConfigurations(t *testing.T) { // Vms are opaque, we can't check their configuration directly. // We can only check that they do execute some basic code. params := tosca.Parameters{} - params.Code = []byte{byte(lfvm.PUSH1), 0xff, byte(lfvm.POP), byte(lfvm.STOP)} + params.Code = []byte{byte(sfvm.PUSH1), 0xff, byte(sfvm.POP), byte(sfvm.STOP)} params.Gas = 5 res, err := vm.Run(params) if err != nil { diff --git a/go/interpreter/sfvm/lfvm_test.go b/go/interpreter/sfvm/sfvm_test.go similarity index 56% rename from go/interpreter/sfvm/lfvm_test.go rename to go/interpreter/sfvm/sfvm_test.go index d4d7f8b9..839d8735 100644 --- a/go/interpreter/sfvm/lfvm_test.go +++ b/go/interpreter/sfvm/sfvm_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" @@ -18,39 +18,39 @@ import ( ) func TestNewInterpreter_ProducesInstanceWithSanctionedProperties(t *testing.T) { - lfvm, err := NewInterpreter(Config{}) + sfvm, err := NewInterpreter(Config{}) if err != nil { - t.Fatalf("failed to create LFVM instance: %v", err) + t.Fatalf("failed to create SFVM instance: %v", err) } - if lfvm.config.WithShaCache != true { - t.Fatalf("LFVM is not configured with sha cache") + if sfvm.config.WithShaCache != true { + t.Fatalf("SFVM is not configured with sha cache") } - if lfvm.config.WithSuperInstructions != false { - t.Fatalf("LFVM is configured with super instructions") + if sfvm.config.WithSuperInstructions != false { + t.Fatalf("SFVM is configured with super instructions") } } -func TestLfvm_OfficialConfigurationHasSanctionedProperties(t *testing.T) { - vm, err := tosca.NewInterpreter("lfvm") +func TestSfvm_OfficialConfigurationHasSanctionedProperties(t *testing.T) { + vm, err := tosca.NewInterpreter("sfvm") if err != nil { - t.Fatalf("lfvm is not registered: %v", err) + t.Fatalf("sfvm is not registered: %v", err) } - lfvm, ok := vm.(*lfvm) + sfvm, ok := vm.(*sfvm) if !ok { t.Fatalf("unexpected interpreter implementation, got %T", vm) } - if lfvm.config.WithShaCache != true { - t.Fatalf("lfvm is not configured with sha cache") + if sfvm.config.WithShaCache != true { + t.Fatalf("sfvm is not configured with sha cache") } - if lfvm.config.WithSuperInstructions != false { - t.Fatalf("lfvm is configured with super instructions") + if sfvm.config.WithSuperInstructions != false { + t.Fatalf("sfvm is configured with super instructions") } } -func TestLfvm_InterpreterReturnsErrorWhenExecutingUnsupportedRevision(t *testing.T) { - vm, err := tosca.NewInterpreter("lfvm") +func TestSfvm_InterpreterReturnsErrorWhenExecutingUnsupportedRevision(t *testing.T) { + vm, err := tosca.NewInterpreter("sfvm") if err != nil { - t.Fatalf("lfvm is not registered: %v", err) + t.Fatalf("sfvm is not registered: %v", err) } params := tosca.Parameters{} @@ -62,7 +62,7 @@ func TestLfvm_InterpreterReturnsErrorWhenExecutingUnsupportedRevision(t *testing } } -func TestLfvm_newVm_returnsErrorWithWrongConfiguration(t *testing.T) { +func TestSfvm_newVm_returnsErrorWithWrongConfiguration(t *testing.T) { config := config{ ConversionConfig: ConversionConfig{CacheSize: maxCachedCodeLength / 2}, } diff --git a/go/interpreter/sfvm/stack.go b/go/interpreter/sfvm/stack.go index eee992c1..6b12d956 100644 --- a/go/interpreter/sfvm/stack.go +++ b/go/interpreter/sfvm/stack.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/stack_test.go b/go/interpreter/sfvm/stack_test.go index c5da253f..495dc460 100644 --- a/go/interpreter/sfvm/stack_test.go +++ b/go/interpreter/sfvm/stack_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/stack_usage.go b/go/interpreter/sfvm/stack_usage.go index 61a9ae81..5391de8e 100644 --- a/go/interpreter/sfvm/stack_usage.go +++ b/go/interpreter/sfvm/stack_usage.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm // stackUsage defines the combined effect of an instruction on the stack. Each // instruction is accessing a range of elements on the stack relative to the diff --git a/go/interpreter/sfvm/stack_usage_test.go b/go/interpreter/sfvm/stack_usage_test.go index 88ba1144..c30d1dbb 100644 --- a/go/interpreter/sfvm/stack_usage_test.go +++ b/go/interpreter/sfvm/stack_usage_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "fmt" diff --git a/go/interpreter/sfvm/super_instructions.go b/go/interpreter/sfvm/super_instructions.go index 048a3538..a4a3e99a 100644 --- a/go/interpreter/sfvm/super_instructions.go +++ b/go/interpreter/sfvm/super_instructions.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "math/bits" diff --git a/go/interpreter/sfvm/super_instructions_test.go b/go/interpreter/sfvm/super_instructions_test.go index 7591e0ab..c3b049c0 100644 --- a/go/interpreter/sfvm/super_instructions_test.go +++ b/go/interpreter/sfvm/super_instructions_test.go @@ -8,7 +8,7 @@ // On the date above, in accordance with the Business Source License, use of // this software will be governed by the GNU Lesser General Public License v3. -package lfvm +package sfvm import ( "testing" From 7056b2d99191c50c5fb9b886dc150d199ebc7ec6 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Nov 2025 11:51:25 +0100 Subject: [PATCH 3/7] Remove super instructions --- go/interpreter/sfvm/converter.go | 132 +------------- go/interpreter/sfvm/converter_fuzz_test.go | 7 - go/interpreter/sfvm/converter_test.go | 63 +------ go/interpreter/sfvm/ct.go | 4 +- go/interpreter/sfvm/ct_test.go | 102 ----------- go/interpreter/sfvm/gas.go | 8 - go/interpreter/sfvm/instruction_test.go | 3 - go/interpreter/sfvm/instructions_test.go | 29 --- go/interpreter/sfvm/interpreter.go | 41 ----- go/interpreter/sfvm/interpreter_test.go | 60 ++----- go/interpreter/sfvm/opcode.go | 106 +---------- go/interpreter/sfvm/opcode_test.go | 12 -- go/interpreter/sfvm/sfvm.go | 48 +++-- go/interpreter/sfvm/sfvm_test.go | 6 - go/interpreter/sfvm/stack_usage.go | 31 ---- go/interpreter/sfvm/stack_usage_test.go | 67 ------- go/interpreter/sfvm/super_instructions.go | 168 ------------------ .../sfvm/super_instructions_test.go | 59 ------ 18 files changed, 44 insertions(+), 902 deletions(-) delete mode 100644 go/interpreter/sfvm/super_instructions.go delete mode 100644 go/interpreter/sfvm/super_instructions_test.go diff --git a/go/interpreter/sfvm/converter.go b/go/interpreter/sfvm/converter.go index ea2cc525..f63e5bdf 100644 --- a/go/interpreter/sfvm/converter.go +++ b/go/interpreter/sfvm/converter.go @@ -29,8 +29,6 @@ type ConversionConfig struct { // Positive values larger than 0 but less than maxCachedCodeLength are // reported as invalid cache sizes during initialization. CacheSize int - // WithSuperInstructions enables the use of super instructions. - WithSuperInstructions bool } // Converter converts EVM code to SFVM code. @@ -169,20 +167,13 @@ func convertWithObserver( // Convert instructions observer(i, res.nextPos) - inc := appendInstructions(&res, i, code, options.WithSuperInstructions) + inc := appendInstructions(&res, i, code) i += inc + 1 } return res.toCode() } -func appendInstructions(res *codeBuilder, pos int, code []byte, withSuperInstructions bool) int { - // Convert super instructions. - if withSuperInstructions { - if n := appendSuperInstructions(res, pos, code); n > 0 { - return n - } - } - +func appendInstructions(res *codeBuilder, pos int, code []byte) int { // Convert individual instructions. toscaOpCode := vm.OpCode(code[pos]) @@ -238,122 +229,3 @@ func appendInstructions(res *codeBuilder, pos int, code []byte, withSuperInstruc res.appendCode(OpCode(toscaOpCode)) return 0 } - -func appendSuperInstructions(res *codeBuilder, pos int, code []byte) int { - if len(code) > pos+7 { - op0 := vm.OpCode(code[pos]) - op1 := vm.OpCode(code[pos+1]) - op2 := vm.OpCode(code[pos+2]) - op3 := vm.OpCode(code[pos+3]) - op4 := vm.OpCode(code[pos+4]) - op5 := vm.OpCode(code[pos+5]) - op6 := vm.OpCode(code[pos+6]) - op7 := vm.OpCode(code[pos+7]) - if op0 == vm.PUSH1 && op2 == vm.PUSH4 && op7 == vm.DUP3 { - res.appendOp(PUSH1_PUSH4_DUP3, uint16(op1)<<8) - res.appendData(uint16(op3)<<8 | uint16(op4)) - res.appendData(uint16(op5)<<8 | uint16(op6)) - return 7 - } - if op0 == vm.PUSH1 && op2 == vm.PUSH1 && op4 == vm.PUSH1 && op6 == vm.SHL && op7 == vm.SUB { - res.appendOp(PUSH1_PUSH1_PUSH1_SHL_SUB, uint16(op1)<<8|uint16(op3)) - res.appendData(uint16(op5)) - return 7 - } - } - if len(code) > pos+4 { - op0 := vm.OpCode(code[pos]) - op1 := vm.OpCode(code[pos+1]) - op2 := vm.OpCode(code[pos+2]) - op3 := vm.OpCode(code[pos+3]) - op4 := vm.OpCode(code[pos+4]) - if op0 == vm.AND && op1 == vm.SWAP1 && op2 == vm.POP && op3 == vm.SWAP2 && op4 == vm.SWAP1 { - res.appendCode(AND_SWAP1_POP_SWAP2_SWAP1) - return 4 - } - if op0 == vm.ISZERO && op1 == vm.PUSH2 && op4 == vm.JUMPI { - res.appendOp(ISZERO_PUSH2_JUMPI, uint16(op2)<<8|uint16(op3)) - return 4 - } - } - if len(code) > pos+3 { - op0 := vm.OpCode(code[pos]) - op1 := vm.OpCode(code[pos+1]) - op2 := vm.OpCode(code[pos+2]) - op3 := vm.OpCode(code[pos+3]) - if op0 == vm.SWAP2 && op1 == vm.SWAP1 && op2 == vm.POP && op3 == vm.JUMP { - res.appendCode(SWAP2_SWAP1_POP_JUMP) - return 3 - } - if op0 == vm.SWAP1 && op1 == vm.POP && op2 == vm.SWAP2 && op3 == vm.SWAP1 { - res.appendCode(SWAP1_POP_SWAP2_SWAP1) - return 3 - } - if op0 == vm.POP && op1 == vm.SWAP2 && op2 == vm.SWAP1 && op3 == vm.POP { - res.appendCode(POP_SWAP2_SWAP1_POP) - return 3 - } - if op0 == vm.PUSH2 && op3 == vm.JUMP { - res.appendOp(PUSH2_JUMP, uint16(op1)<<8|uint16(op2)) - return 3 - } - if op0 == vm.PUSH2 && op3 == vm.JUMPI { - res.appendOp(PUSH2_JUMPI, uint16(op1)<<8|uint16(op2)) - return 3 - } - if op0 == vm.PUSH1 && op2 == vm.PUSH1 { - res.appendOp(PUSH1_PUSH1, uint16(op1)<<8|uint16(op3)) - return 3 - } - } - if len(code) > pos+2 { - op0 := vm.OpCode(code[pos]) - op1 := vm.OpCode(code[pos+1]) - op2 := vm.OpCode(code[pos+2]) - if op0 == vm.PUSH1 && op2 == vm.ADD { - res.appendOp(PUSH1_ADD, uint16(op1)) - return 2 - } - if op0 == vm.PUSH1 && op2 == vm.SHL { - res.appendOp(PUSH1_SHL, uint16(op1)) - return 2 - } - if op0 == vm.PUSH1 && op2 == vm.DUP1 { - res.appendOp(PUSH1_DUP1, uint16(op1)) - return 2 - } - } - if len(code) > pos+1 { - op0 := vm.OpCode(code[pos]) - op1 := vm.OpCode(code[pos+1]) - if op0 == vm.SWAP1 && op1 == vm.POP { - res.appendCode(SWAP1_POP) - return 1 - } - if op0 == vm.POP && op1 == vm.JUMP { - res.appendCode(POP_JUMP) - return 1 - } - if op0 == vm.POP && op1 == vm.POP { - res.appendCode(POP_POP) - return 1 - } - if op0 == vm.SWAP2 && op1 == vm.SWAP1 { - res.appendCode(SWAP2_SWAP1) - return 1 - } - if op0 == vm.SWAP2 && op1 == vm.POP { - res.appendCode(SWAP2_POP) - return 1 - } - if op0 == vm.DUP2 && op1 == vm.MSTORE { - res.appendCode(DUP2_MSTORE) - return 1 - } - if op0 == vm.DUP2 && op1 == vm.LT { - res.appendCode(DUP2_LT) - return 1 - } - } - return 0 -} diff --git a/go/interpreter/sfvm/converter_fuzz_test.go b/go/interpreter/sfvm/converter_fuzz_test.go index f5b8a248..f42c9943 100644 --- a/go/interpreter/sfvm/converter_fuzz_test.go +++ b/go/interpreter/sfvm/converter_fuzz_test.go @@ -46,13 +46,6 @@ func FuzzSfvmConverter(f *testing.F) { mapping[evm] = sfvm }) - // Check that no super-instructions have been used. - for _, op := range sfvmCode { - if op.opcode.isSuperInstruction() { - t.Errorf("Super-instruction %v used", op.opcode) - } - } - // Check that all operations are mapped to matching operations. for i := 0; i < len(toscaCode); i++ { originalPos := i diff --git a/go/interpreter/sfvm/converter_test.go b/go/interpreter/sfvm/converter_test.go index e05f12a6..ad198819 100644 --- a/go/interpreter/sfvm/converter_test.go +++ b/go/interpreter/sfvm/converter_test.go @@ -337,9 +337,7 @@ func TestConvert_ProgramCounterBeyond16bitAreConvertedIntoInvalidInstructions(t } func TestConvert_BaseInstructionsAreConvertedToEquivalents(t *testing.T) { - config := ConversionConfig{ - WithSuperInstructions: false, - } + config := ConversionConfig{} for _, op := range allOpCodesWhere(OpCode.isBaseInstruction) { t.Run(op.String(), func(t *testing.T) { code := []byte{byte(op)} @@ -441,65 +439,6 @@ func TestConvert_AllJumpToOperationsPointToSubsequentJumpdest(t *testing.T) { } } -func TestConvert_SI_WhenEnabledSuperInstructionsAreUsed(t *testing.T) { - config := ConversionConfig{ - WithSuperInstructions: true, - } - for _, op := range allOpCodesWhere(OpCode.isSuperInstruction) { - t.Run(op.String(), func(t *testing.T) { - code := []byte{} - for _, op := range op.decompose() { - code = append(code, byte(op)) - if PUSH1 <= op && op <= PUSH32 { - code = append(code, make([]byte, int(op)-int(PUSH1)+1)...) - } - } - res := convert(code, config) - if want, got := op, res[0].opcode; want != got { - t.Errorf("Expected %v, got %v", want, got) - } - }) - } -} - -func TestConvert_SI_WhenDisabledNoSuperInstructionsAreUsed(t *testing.T) { - config := ConversionConfig{ - WithSuperInstructions: false, - } - for _, op := range allOpCodesWhere(OpCode.isSuperInstruction) { - t.Run(op.String(), func(t *testing.T) { - code := []byte{} - for _, op := range op.decompose() { - code = append(code, byte(op)) - } - - res := convert(code, config) - for i, instr := range res { - if instr.opcode.isSuperInstruction() { - t.Errorf("Super instruction %v used at position %d", instr.opcode, i) - } - } - }) - } -} - -func TestConverter_SI_FallsBackToSFVMInstructionsWhenNoSuperInstructionIsFit(t *testing.T) { - - config := ConversionConfig{ - WithSuperInstructions: true, - } - code := []byte{byte(PUSH2), 0x12, 0x34, byte(ADD), byte(PUSH1), 0x56, byte(SUB)} - convertedCode := convert(code, config) - if len(convertedCode) != 4 { - t.Fatalf("Expected 4 instructions, got %d", len(convertedCode)) - } - for _, inst := range convertedCode { - if inst.opcode.isSuperInstruction() { - t.Errorf("Super instruction %v used", inst.opcode) - } - } -} - func benchmarkConvertCode(b *testing.B, code []byte, config ConversionConfig) { converter, err := NewConverter(config) if err != nil { diff --git a/go/interpreter/sfvm/ct.go b/go/interpreter/sfvm/ct.go index 6100f9dc..9965be96 100644 --- a/go/interpreter/sfvm/ct.go +++ b/go/interpreter/sfvm/ct.go @@ -130,9 +130,7 @@ func genPcMap(code []byte) *pcMap { evmToSfvm := make([]uint16, len(code)+1) sfvmToEvm := make([]uint16, len(code)+1) - config := ConversionConfig{ - WithSuperInstructions: false, - } + config := ConversionConfig{} res := convertWithObserver(code, config, func(evm, sfvm int) { evmToSfvm[evm] = uint16(sfvm) sfvmToEvm[sfvm] = uint16(evm) diff --git a/go/interpreter/sfvm/ct_test.go b/go/interpreter/sfvm/ct_test.go index 4cbc37c0..060ea062 100644 --- a/go/interpreter/sfvm/ct_test.go +++ b/go/interpreter/sfvm/ct_test.go @@ -14,7 +14,6 @@ import ( "bytes" "errors" "math" - "reflect" "testing" "github.com/0xsoniclabs/tosca/go/ct" @@ -331,107 +330,6 @@ func TestConvertToSfvm_Code(t *testing.T) { } } -func TestConvertToSfvm_CodeWithSuperInstructions(t *testing.T) { - tests := map[string]struct { - evmCode []byte - want Code - }{ - "PUSH1PUSH4DUP3": { - []byte{byte(vm.PUSH1), 0x01, - byte(vm.PUSH4), 0x01, 0x02, 0x03, 0x04, - byte(vm.DUP3)}, - Code{Instruction{PUSH1_PUSH4_DUP3, 0x0100}, - Instruction{DATA, 0x0102}, - Instruction{DATA, 0x0304}, - }}, - "PUSH1_PUSH1_PUSH1_SHL_SUB": { - []byte{byte(vm.PUSH1), 0x01, - byte(vm.PUSH1), 0x01, - byte(vm.PUSH1), 0x01, - byte(vm.SHL), - byte(vm.SUB)}, - Code{Instruction{PUSH1_PUSH1_PUSH1_SHL_SUB, 0x0101}, - Instruction{DATA, 0x0001}, - }}, - "AND_SWAP1_POP_SWAP2_SWAP1": { - []byte{byte(vm.AND), byte(vm.SWAP1), byte(vm.POP), - byte(vm.SWAP2), byte(vm.SWAP1)}, - Code{Instruction{AND_SWAP1_POP_SWAP2_SWAP1, 0x0000}}}, - "ISZERO_PUSH2_JUMPI": { - []byte{byte(vm.ISZERO), - byte(vm.PUSH2), 0x01, 0x02, - byte(vm.JUMPI)}, - Code{Instruction{ISZERO_PUSH2_JUMPI, 0x0102}}}, - "SWAP2_SWAP1_POP_JUMP": { - []byte{byte(vm.SWAP2), byte(vm.SWAP1), byte(vm.POP), - byte(vm.JUMP)}, - Code{Instruction{SWAP2_SWAP1_POP_JUMP, 0x0000}}}, - "SWAP1_POP_SWAP2_SWAP1": { - []byte{byte(vm.SWAP1), byte(vm.POP), byte(vm.SWAP2), - byte(vm.SWAP1)}, - Code{Instruction{SWAP1_POP_SWAP2_SWAP1, 0x0000}}}, - "POP_SWAP2_SWAP1_POP": { - []byte{byte(vm.POP), byte(vm.SWAP2), byte(vm.SWAP1), - byte(vm.POP)}, - Code{Instruction{POP_SWAP2_SWAP1_POP, 0x0000}}}, - "PUSH2_JUMP": { - []byte{byte(vm.PUSH2), 0x01, 0x02, - byte(vm.JUMP)}, - Code{Instruction{PUSH2_JUMP, 0x0102}}}, - "PUSH2_JUMPI": { - []byte{byte(vm.PUSH2), 0x01, 0x02, - byte(vm.JUMPI)}, - Code{Instruction{PUSH2_JUMPI, 0x0102}}}, - "PUSH1_PUSH1": { - []byte{byte(vm.PUSH1), 0x01, - byte(vm.PUSH1), 0x01}, - Code{Instruction{PUSH1_PUSH1, 0x0101}}}, - "PUSH1_ADD": { - []byte{byte(vm.PUSH1), 0x01, - byte(vm.ADD)}, - Code{Instruction{PUSH1_ADD, 0x0001}}}, - "PUSH1_SHL": { - []byte{byte(vm.PUSH1), 0x01, - byte(vm.SHL)}, - Code{Instruction{PUSH1_SHL, 0x0001}}}, - "PUSH1_DUP1": { - []byte{byte(vm.PUSH1), 0x01, - byte(vm.DUP1)}, - Code{Instruction{PUSH1_DUP1, 0x0001}}}, - "SWAP1_POP": { - []byte{byte(vm.SWAP1), byte(vm.POP)}, - Code{Instruction{SWAP1_POP, 0x0000}}}, - "POP_JUMP": { - []byte{byte(vm.POP), byte(vm.JUMP)}, - Code{Instruction{POP_JUMP, 0x0000}}}, - "POP_POP": { - []byte{byte(vm.POP), byte(vm.POP)}, - Code{Instruction{POP_POP, 0x0000}}}, - "SWAP2_SWAP1": { - []byte{byte(vm.SWAP2), byte(vm.SWAP1)}, - Code{Instruction{SWAP2_SWAP1, 0x0000}}}, - "SWAP2_POP": { - []byte{byte(vm.SWAP2), byte(vm.POP)}, - Code{Instruction{SWAP2_POP, 0x0000}}}, - "DUP2_MSTORE": { - []byte{byte(vm.DUP2), byte(vm.MSTORE)}, - Code{Instruction{DUP2_MSTORE, 0x0000}}}, - "DUP2_LT": { - []byte{byte(vm.DUP2), byte(vm.LT)}, - Code{Instruction{DUP2_LT, 0x0000}}}, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - options := ConversionConfig{WithSuperInstructions: true} - got := convert(test.evmCode, options) - if !reflect.DeepEqual(test.want, got) { - t.Fatalf("unexpected code, wanted %v, got %v", test.want, got) - } - }) - } -} - func TestConvertToSfvm_Stack(t *testing.T) { newSfvmStack := func(values ...cc.U256) *stack { stack := NewStack() diff --git a/go/interpreter/sfvm/gas.go b/go/interpreter/sfvm/gas.go index f22f4e63..bc51e971 100644 --- a/go/interpreter/sfvm/gas.go +++ b/go/interpreter/sfvm/gas.go @@ -226,14 +226,6 @@ func getStaticGasPriceInternal(op OpCode) tosca.Gas { return 5000 } - if op.isSuperInstruction() { - var sum tosca.Gas - for _, subOp := range op.decompose() { - sum += getStaticGasPriceInternal(subOp) - } - return sum - } - return UNKNOWN_GAS_PRICE } diff --git a/go/interpreter/sfvm/instruction_test.go b/go/interpreter/sfvm/instruction_test.go index b83a2924..60dbdddd 100644 --- a/go/interpreter/sfvm/instruction_test.go +++ b/go/interpreter/sfvm/instruction_test.go @@ -23,9 +23,6 @@ func TestInstruction_String(t *testing.T) { {Instruction{opcode: PUSH2, arg: 0x0002}, "PUSH2 0x0002"}, {Instruction{opcode: DATA, arg: 0x0002}, "DATA 0x0002"}, {Instruction{opcode: JUMP_TO, arg: 0x0002}, "JUMP_TO 0x0002"}, - {Instruction{opcode: PUSH2_JUMP, arg: 0x0002}, "PUSH2_JUMP 0x0002"}, - {Instruction{opcode: PUSH2_JUMPI, arg: 0x0002}, "PUSH2_JUMPI 0x0002"}, - {Instruction{opcode: PUSH1_PUSH4_DUP3, arg: 0x0002}, "PUSH1_PUSH4_DUP3 0x0002"}, } for _, tt := range tests { diff --git a/go/interpreter/sfvm/instructions_test.go b/go/interpreter/sfvm/instructions_test.go index 2ed1dda2..3580a942 100644 --- a/go/interpreter/sfvm/instructions_test.go +++ b/go/interpreter/sfvm/instructions_test.go @@ -1215,26 +1215,6 @@ func TestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { implementation: opJumpi, stack: []uint64{1, 1}, }, - SWAP2_SWAP1_POP_JUMP: { - implementation: opSwap2_Swap1_Pop_Jump, - stack: []uint64{1, 1, 1}, - }, - POP_JUMP: { - implementation: opPop_Jump, - stack: []uint64{1, 1}, - }, - PUSH2_JUMP: { - implementation: opPush2_Jump, - stack: []uint64{1}, - }, - PUSH2_JUMPI: { - implementation: opPush2_Jumpi, - stack: []uint64{1}, - }, - ISZERO_PUSH2_JUMPI: { - implementation: opIsZero_Push2_Jumpi, - stack: []uint64{0}, - }, } // test that all jump instructions are tested @@ -1262,7 +1242,6 @@ func TestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { func TestInstructions_ConditionalJumpOpsIgnoreDestinationWhenJumpNotTaken(t *testing.T) { zero := *uint256.NewInt(0) - one := *uint256.NewInt(1) maxUint256 := *uint256.NewInt(0).Sub(uint256.NewInt(0), uint256.NewInt(1)) tests := map[OpCode]struct { @@ -1274,14 +1253,6 @@ func TestInstructions_ConditionalJumpOpsIgnoreDestinationWhenJumpNotTaken(t *tes // ignores destination, even if it would overflow stack: []uint256.Int{maxUint256, zero}, }, - PUSH2_JUMPI: { - implementation: opPush2_Jumpi, - stack: []uint256.Int{zero}, - }, - ISZERO_PUSH2_JUMPI: { - implementation: opIsZero_Push2_Jumpi, - stack: []uint256.Int{one}, - }, } for op, test := range tests { diff --git a/go/interpreter/sfvm/interpreter.go b/go/interpreter/sfvm/interpreter.go index 6f993d3d..223b2e5c 100644 --- a/go/interpreter/sfvm/interpreter.go +++ b/go/interpreter/sfvm/interpreter.go @@ -507,47 +507,6 @@ func steps(c *context, oneStepOnly bool) (status, error) { err = opLog(c, 3) case LOG4: err = opLog(c, 4) - // --- Super Instructions --- - case SWAP2_SWAP1_POP_JUMP: - err = opSwap2_Swap1_Pop_Jump(c) - case SWAP1_POP_SWAP2_SWAP1: - opSwap1_Pop_Swap2_Swap1(c) - case POP_SWAP2_SWAP1_POP: - opPop_Swap2_Swap1_Pop(c) - case POP_POP: - opPopPop(c) - case PUSH1_SHL: - opPush1_Shl(c) - case PUSH1_ADD: - opPush1_Add(c) - case PUSH1_DUP1: - opPush1_Dup1(c) - case PUSH2_JUMP: - err = opPush2_Jump(c) - case PUSH2_JUMPI: - err = opPush2_Jumpi(c) - case PUSH1_PUSH1: - opPush1_Push1(c) - case SWAP1_POP: - opSwap1_Pop(c) - case POP_JUMP: - err = opPop_Jump(c) - case SWAP2_SWAP1: - opSwap2_Swap1(c) - case SWAP2_POP: - opSwap2_Pop(c) - case DUP2_MSTORE: - err = opDup2_Mstore(c) - case DUP2_LT: - opDup2_Lt(c) - case ISZERO_PUSH2_JUMPI: - err = opIsZero_Push2_Jumpi(c) - case PUSH1_PUSH4_DUP3: - opPush1_Push4_Dup3(c) - case AND_SWAP1_POP_SWAP2_SWAP1: - opAnd_Swap1_Pop_Swap2_Swap1(c) - case PUSH1_PUSH1_PUSH1_SHL_SUB: - opPush1_Push1_Push1_Shl_Sub(c) default: err = errInvalidOpCode } diff --git a/go/interpreter/sfvm/interpreter_test.go b/go/interpreter/sfvm/interpreter_test.go index 974b2081..fb19e3fa 100644 --- a/go/interpreter/sfvm/interpreter_test.go +++ b/go/interpreter/sfvm/interpreter_test.go @@ -588,11 +588,7 @@ func TestInterpreter_ExecuteReturnsFailureOnExecutionError(t *testing.T) { // Benchmarks func BenchmarkFib10(b *testing.B) { - benchmarkFib(b, 10, false) -} - -func BenchmarkFib10_SI(b *testing.B) { - benchmarkFib(b, 10, true) + benchmarkFib(b, 10) } func BenchmarkSatisfiesStackRequirements(b *testing.B) { @@ -611,17 +607,14 @@ func BenchmarkSatisfiesStackRequirements(b *testing.B) { func Test_generateCodeForOps(t *testing.T) { tests := map[OpCode]int{ - PUSH1: 1, - PUSH2: 1, - PUSH3: 2, - PUSH4: 2, - PUSH5: 3, - PUSH6: 3, - PUSH31: 16, - PUSH32: 16, - PUSH1_PUSH1: 1, - PUSH1_PUSH4_DUP3: 3, - PUSH1_PUSH1_PUSH1_SHL_SUB: 2, + PUSH1: 1, + PUSH2: 1, + PUSH3: 2, + PUSH4: 2, + PUSH5: 3, + PUSH6: 3, + PUSH31: 16, + PUSH32: 16, } for op, test := range tests { t.Run(op.String(), func(t *testing.T) { @@ -639,27 +632,13 @@ func Test_generateCodeForOps(t *testing.T) { func generateCodeFor(op OpCode) Code { var code []Instruction + code = append(code, Instruction{op, 0}) - switch op { - case PUSH1_PUSH4_DUP3: - code = append(code, Instruction{op, 0}, Instruction{DATA, 0}) - case PUSH1_PUSH1_PUSH1_SHL_SUB: - code = append(code, Instruction{op, 0}, Instruction{DATA, 0}) - case PUSH2_JUMP: - code = append(code, Instruction{op, 1}) // hardcoded jump destination - case PUSH2_JUMPI: - code = append(code, Instruction{op, 1}) // hardcoded jump destination - default: - code = append(code, Instruction{op, 0}) - } - - for _, op := range append(op.decompose(), op) { - if PUSH3 <= op && op <= PUSH32 { - n := int(op) - int(PUSH3) + 3 - numInstructions := n/2 + n%2 - for i := 0; i < numInstructions-1; i++ { - code = append(code, Instruction{DATA, 0}) - } + if PUSH3 <= op && op <= PUSH32 { + n := int(op) - int(PUSH3) + 3 + numInstructions := n/2 + n%2 + for i := 0; i < numInstructions-1; i++ { + code = append(code, Instruction{DATA, 0}) } } @@ -709,17 +688,14 @@ func isExecutable(op OpCode) bool { } func isJump(op OpCode) bool { - ops := append(op.decompose(), op) - return slices.ContainsFunc(ops, func(op OpCode) bool { - return op == JUMP || op == JUMPI - }) + return op == JUMP || op == JUMPI } -func benchmarkFib(b *testing.B, arg int, with_super_instructions bool) { +func benchmarkFib(b *testing.B, arg int) { example := getFibExample() // Convert example to SFVM format. - converted := convert(example.code, ConversionConfig{WithSuperInstructions: with_super_instructions}) + converted := convert(example.code, ConversionConfig{}) // Create input data. diff --git a/go/interpreter/sfvm/opcode.go b/go/interpreter/sfvm/opcode.go index dac9df1e..876d10f4 100644 --- a/go/interpreter/sfvm/opcode.go +++ b/go/interpreter/sfvm/opcode.go @@ -254,64 +254,17 @@ const ( // search (which could be cached to amortize costs). DATA - // Super-instructions - SWAP2_SWAP1_POP_JUMP - SWAP1_POP_SWAP2_SWAP1 - POP_SWAP2_SWAP1_POP - POP_POP - PUSH1_SHL - PUSH1_ADD - PUSH1_DUP1 - PUSH2_JUMP - PUSH2_JUMPI - - PUSH1_PUSH1 - SWAP1_POP - POP_JUMP - SWAP2_SWAP1 - SWAP2_POP - DUP2_MSTORE - DUP2_LT - - ISZERO_PUSH2_JUMPI - PUSH1_PUSH4_DUP3 - AND_SWAP1_POP_SWAP2_SWAP1 - PUSH1_PUSH1_PUSH1_SHL_SUB - // _highestOpCode is an alias for the OpCode with the highest defined // numeric value. It is only intended to be used in the unit tests // associated to this OpCode definition file to verify that the OpCode // bit mask limit has not been exceeded. - _highestOpCode = PUSH1_PUSH1_PUSH1_SHL_SUB + _highestOpCode = DATA ) var toString = map[OpCode]string{ DATA: "DATA", NOOP: "NOOP", JUMP_TO: "JUMP_TO", - - SWAP2_SWAP1_POP_JUMP: "SWAP2_SWAP1_POP_JUMP", - SWAP1_POP_SWAP2_SWAP1: "SWAP1_POP_SWAP2_SWAP1", - POP_SWAP2_SWAP1_POP: "POP_SWAP2_SWAP1_POP", - PUSH2_JUMP: "PUSH2_JUMP", - PUSH2_JUMPI: "PUSH2_JUMPI", - DUP2_MSTORE: "DUP2_MSTORE", - DUP2_LT: "DUP2_LT", - - SWAP1_POP: "SWAP1_POP", - POP_JUMP: "POP_JUMP", - SWAP2_SWAP1: "SWAP2_SWAP1", - SWAP2_POP: "SWAP2_POP", - PUSH1_PUSH1: "PUSH1_PUSH1", - PUSH1_ADD: "PUSH1_ADD", - PUSH1_DUP1: "PUSH1_DUP1", - POP_POP: "POP_POP", - PUSH1_SHL: "PUSH1_SHL", - - ISZERO_PUSH2_JUMPI: "ISZERO_PUSH2_JUMPI", - PUSH1_PUSH4_DUP3: "PUSH1_PUSH4_DUP3", - AND_SWAP1_POP_SWAP2_SWAP1: "AND_SWAP1_POP_SWAP2_SWAP1", - PUSH1_PUSH1_PUSH1_SHL_SUB: "PUSH1_PUSH1_PUSH1_SHL_SUB", } // String returns the string representation of the OpCode. @@ -338,13 +291,6 @@ func (o OpCode) HasArgument() bool { case JUMP_TO: return true } - if o.isSuperInstruction() { - for _, subOp := range o.decompose() { - if subOp.HasArgument() { - return true - } - } - } return false } @@ -352,56 +298,6 @@ func (o OpCode) isBaseInstruction() bool { return o < 0x100 } -func (o OpCode) isSuperInstruction() bool { - return o.decompose() != nil -} - -func (o OpCode) decompose() []OpCode { - switch o { - case SWAP2_SWAP1_POP_JUMP: - return []OpCode{SWAP2, SWAP1, POP, JUMP} - case SWAP1_POP_SWAP2_SWAP1: - return []OpCode{SWAP1, POP, SWAP2, SWAP1} - case POP_SWAP2_SWAP1_POP: - return []OpCode{POP, SWAP2, SWAP1, POP} - case POP_POP: - return []OpCode{POP, POP} - case PUSH1_SHL: - return []OpCode{PUSH1, SHL} - case PUSH1_ADD: - return []OpCode{PUSH1, ADD} - case PUSH1_DUP1: - return []OpCode{PUSH1, DUP1} - case PUSH2_JUMP: - return []OpCode{PUSH2, JUMP} - case PUSH2_JUMPI: - return []OpCode{PUSH2, JUMPI} - case PUSH1_PUSH1: - return []OpCode{PUSH1, PUSH1} - case SWAP1_POP: - return []OpCode{SWAP1, POP} - case POP_JUMP: - return []OpCode{POP, JUMP} - case SWAP2_SWAP1: - return []OpCode{SWAP2, SWAP1} - case SWAP2_POP: - return []OpCode{SWAP2, POP} - case DUP2_MSTORE: - return []OpCode{DUP2, MSTORE} - case DUP2_LT: - return []OpCode{DUP2, LT} - case ISZERO_PUSH2_JUMPI: - return []OpCode{ISZERO, PUSH2, JUMPI} - case PUSH1_PUSH4_DUP3: - return []OpCode{PUSH1, PUSH4, DUP3} - case AND_SWAP1_POP_SWAP2_SWAP1: - return []OpCode{AND, SWAP1, POP, SWAP2, SWAP1} - case PUSH1_PUSH1_PUSH1_SHL_SUB: - return []OpCode{PUSH1, PUSH1, PUSH1, SHL, SUB} - } - return nil -} - // opCodePropertyMap is a generic property map for precomputed values. // Its purpose is to provide a precomputed lookup table for OpCode properties // that can be generated from a function that takes an OpCode as input. diff --git a/go/interpreter/sfvm/opcode_test.go b/go/interpreter/sfvm/opcode_test.go index 0daa3735..835d4013 100644 --- a/go/interpreter/sfvm/opcode_test.go +++ b/go/interpreter/sfvm/opcode_test.go @@ -21,7 +21,6 @@ func TestOpCode_String(t *testing.T) { want string }{ {STOP, "STOP"}, - {SWAP2_SWAP1_POP_JUMP, "SWAP2_SWAP1_POP_JUMP"}, {0x0c, "op(0x0C)"}, {0x0200, "op(0x0200)"}, } @@ -32,17 +31,6 @@ func TestOpCode_String(t *testing.T) { } } -func TestOpCode_SuperInstructionsAreDecomposedToBasicOpCodes(t *testing.T) { - for _, op := range allOpCodesWhere(OpCode.isSuperInstruction) { - baseOps := op.decompose() - for _, baseOp := range baseOps { - if baseOp.isSuperInstruction() { - t.Errorf("decomposition of %v contains super instruction %v", op, baseOp) - } - } - } -} - func TestOpCode_AllOpCodesAreSmallerThanTheOpCodeCapacity(t *testing.T) { if want, get := numOpCodes, opCodeMask+1; want != get { t.Errorf("opCodeMask+1 = %d, want %d", get, want) diff --git a/go/interpreter/sfvm/sfvm.go b/go/interpreter/sfvm/sfvm.go index bee5b926..21d74913 100644 --- a/go/interpreter/sfvm/sfvm.go +++ b/go/interpreter/sfvm/sfvm.go @@ -25,10 +25,8 @@ type Config struct { // configuration for production purposes. func NewInterpreter(Config) (*sfvm, error) { return newVm(config{ - ConversionConfig: ConversionConfig{ - WithSuperInstructions: false, - }, - WithShaCache: true, + ConversionConfig: ConversionConfig{}, + WithShaCache: true, }) } @@ -47,35 +45,31 @@ func RegisterExperimentalInterpreterConfigurations() error { configs := map[string]config{} - for _, si := range []string{"", "-si"} { - for _, shaCache := range []string{"", "-no-sha-cache"} { - for _, mode := range []string{"", "-stats", "-logging"} { + for _, shaCache := range []string{"", "-no-sha-cache"} { + for _, mode := range []string{"", "-stats", "-logging"} { - config := config{ - ConversionConfig: ConversionConfig{ - WithSuperInstructions: si == "-si", - }, - WithShaCache: shaCache != "-no-sha-cache", - } + config := config{ + ConversionConfig: ConversionConfig{}, + WithShaCache: shaCache != "-no-sha-cache", + } - switch mode { - case "-stats": - config.runner = &statisticRunner{ - stats: newStatistics(), - } - case "-logging": - config.runner = loggingRunner{ - log: os.Stdout, - } + switch mode { + case "-stats": + config.runner = &statisticRunner{ + stats: newStatistics(), } - - name := "sfvm" + si + shaCache + mode - if name == "sfvm" { - continue + case "-logging": + config.runner = loggingRunner{ + log: os.Stdout, } + } - configs[name] = config + name := "sfvm" + shaCache + mode + if name == "sfvm" { + continue } + + configs[name] = config } } diff --git a/go/interpreter/sfvm/sfvm_test.go b/go/interpreter/sfvm/sfvm_test.go index 839d8735..798fe991 100644 --- a/go/interpreter/sfvm/sfvm_test.go +++ b/go/interpreter/sfvm/sfvm_test.go @@ -25,9 +25,6 @@ func TestNewInterpreter_ProducesInstanceWithSanctionedProperties(t *testing.T) { if sfvm.config.WithShaCache != true { t.Fatalf("SFVM is not configured with sha cache") } - if sfvm.config.WithSuperInstructions != false { - t.Fatalf("SFVM is configured with super instructions") - } } func TestSfvm_OfficialConfigurationHasSanctionedProperties(t *testing.T) { @@ -42,9 +39,6 @@ func TestSfvm_OfficialConfigurationHasSanctionedProperties(t *testing.T) { if sfvm.config.WithShaCache != true { t.Fatalf("sfvm is not configured with sha cache") } - if sfvm.config.WithSuperInstructions != false { - t.Fatalf("sfvm is configured with super instructions") - } } func TestSfvm_InterpreterReturnsErrorWhenExecutingUnsupportedRevision(t *testing.T) { diff --git a/go/interpreter/sfvm/stack_usage.go b/go/interpreter/sfvm/stack_usage.go index 5391de8e..eeae6070 100644 --- a/go/interpreter/sfvm/stack_usage.go +++ b/go/interpreter/sfvm/stack_usage.go @@ -81,36 +81,5 @@ func computeStackUsage(op OpCode) stackUsage { return makeUsage(7, 1) } - // For super-instructions, we need to decompose the instruction into its - // sub-instructions and compute the combined stack usage. - if op.isSuperInstruction() { - usages := []stackUsage{} - for _, subOp := range op.decompose() { - usages = append(usages, computeStackUsage(subOp)) - } - return combineStackUsage(usages...) - } - return stackUsage{} } - -// combineStackUsage combines the given stack usages into a single stack usage. -func combineStackUsage(usages ...stackUsage) stackUsage { - // This function simulates the effect of the given stack usages on the stack - // step by step. The delta of the resulting stack usage tracks the current - // stack height offset. - res := stackUsage{} - for _, usage := range usages { - from := usage.from + res.delta - to := usage.to + res.delta - - if from < res.from { - res.from = from - } - if to > res.to { - res.to = to - } - res.delta += usage.delta - } - return res -} diff --git a/go/interpreter/sfvm/stack_usage_test.go b/go/interpreter/sfvm/stack_usage_test.go index c30d1dbb..620b7e47 100644 --- a/go/interpreter/sfvm/stack_usage_test.go +++ b/go/interpreter/sfvm/stack_usage_test.go @@ -11,7 +11,6 @@ package sfvm import ( - "fmt" "testing" ) @@ -40,69 +39,3 @@ func TestComputeStackUsage_ProducesValidResultsForSingleOps(t *testing.T) { }) } } - -func TestCombineStackUsage(t *testing.T) { - tests := []struct { - ops []OpCode - usage stackUsage - }{ - { - []OpCode{}, - stackUsage{from: 0, to: 0, delta: 0}, - }, - { - []OpCode{PUSH1}, - stackUsage{from: 0, to: 1, delta: 1}, - }, - { - []OpCode{POP}, - stackUsage{from: -1, to: 0, delta: -1}, - }, - { - []OpCode{PUSH1, PUSH1}, - stackUsage{from: 0, to: 2, delta: 2}, - }, - { - []OpCode{PUSH1, POP}, - stackUsage{from: 0, to: 1, delta: 0}, - }, - { - []OpCode{POP, PUSH1}, - stackUsage{from: -1, to: 0, delta: 0}, - }, - { - []OpCode{POP, POP}, - stackUsage{from: -2, to: 0, delta: -2}, - }, - { - []OpCode{PUSH1, PUSH1, POP, POP}, - stackUsage{from: 0, to: 2, delta: 0}, - }, - { - []OpCode{PUSH1, PUSH1, POP, POP, POP, PUSH1, PUSH1}, - stackUsage{from: -1, to: 2, delta: 1}, - }, - { - []OpCode{PUSH1, LOG4, PUSH1}, - stackUsage{from: -5, to: 1, delta: -4}, - }, - { - []OpCode{PUSH1_ADD, ISZERO_PUSH2_JUMPI}, - stackUsage{from: -1, to: 1, delta: -1}, - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("%v", test.ops), func(t *testing.T) { - usages := []stackUsage{} - for _, op := range test.ops { - usages = append(usages, computeStackUsage(op)) - } - - res := combineStackUsage(usages...) - if res != test.usage { - t.Errorf("unexpected result: want %v, got %v", test.usage, res) - } - }) - } -} diff --git a/go/interpreter/sfvm/super_instructions.go b/go/interpreter/sfvm/super_instructions.go deleted file mode 100644 index a4a3e99a..00000000 --- a/go/interpreter/sfvm/super_instructions.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "math/bits" - - "github.com/holiman/uint256" -) - -// ----------------------------- Super Instructions ----------------------------- - -func opSwap1_Pop(c *context) { - a1 := c.stack.pop() - a2 := c.stack.peek() - *a2 = *a1 -} - -func opSwap2_Pop(c *context) { - a1 := c.stack.pop() - *c.stack.peekN(1) = *a1 -} - -func opPush1_Push1(c *context) { - arg := c.code[c.pc].arg - c.stack.stackPointer += 2 - c.stack.peekN(0).SetUint64(uint64(arg & 0xFF)) - c.stack.peekN(1).SetUint64(uint64(arg >> 8)) -} - -func opPush1_Add(c *context) { - arg := c.code[c.pc].arg - trg := c.stack.peek() - var carry uint64 - trg[0], carry = bits.Add64(trg[0], uint64(arg), 0) - trg[1], carry = bits.Add64(trg[1], 0, carry) - trg[2], carry = bits.Add64(trg[2], 0, carry) - trg[3], _ = bits.Add64(trg[3], 0, carry) -} - -func opPush1_Shl(c *context) { - arg := c.code[c.pc].arg - trg := c.stack.peek() - trg.Lsh(trg, uint(arg)) -} - -func opPush1_Dup1(c *context) { - arg := c.code[c.pc].arg - c.stack.stackPointer += 2 - c.stack.peekN(0).SetUint64(uint64(arg)) - c.stack.peekN(1).SetUint64(uint64(arg)) -} - -func opPush2_Jump(c *context) error { - // Directly take pushed value and jump to destination. - c.pc = int32(c.code[c.pc].arg) - 1 - return checkJumpDest(c) -} - -func opPush2_Jumpi(c *context) error { - // Directly take pushed value and jump to destination. - condition := c.stack.pop() - if !condition.IsZero() { - c.pc = int32(c.code[c.pc].arg) - 1 - return checkJumpDest(c) - } - return nil -} - -func opSwap2_Swap1(c *context) { - a1 := c.stack.peekN(0) - a2 := c.stack.peekN(1) - a3 := c.stack.peekN(2) - *a1, *a2, *a3 = *a2, *a3, *a1 -} - -func opDup2_Mstore(c *context) error { - var value = c.stack.pop() - var offset = c.stack.peek() - v := value.Bytes32() - return c.memory.set(offset, v[:], c) -} - -func opDup2_Lt(c *context) { - b := c.stack.peekN(0) - a := c.stack.peekN(1) - if a.Lt(b) { - b.SetOne() - } else { - b.Clear() - } -} - -func opPopPop(c *context) { - c.stack.stackPointer -= 2 -} - -func opPop_Jump(c *context) error { - opPop(c) - return opJump(c) -} - -func opIsZero_Push2_Jumpi(c *context) error { - condition := c.stack.pop() - if condition.IsZero() { - c.pc = int32(c.code[c.pc].arg) - 1 - return checkJumpDest(c) - } - return nil -} - -func opSwap2_Swap1_Pop_Jump(c *context) error { - top := c.stack.pop() - c.stack.pop() - trg := c.stack.peek() - c.pc = int32(trg.Uint64()) - 1 - *trg = *top - return checkJumpDest(c) -} - -func opSwap1_Pop_Swap2_Swap1(c *context) { - a1 := c.stack.pop() - a2 := c.stack.peekN(0) - a3 := c.stack.peekN(1) - a4 := c.stack.peekN(2) - *a2, *a3, *a4 = *a3, *a4, *a1 -} - -func opPop_Swap2_Swap1_Pop(c *context) { - c.stack.pop() - a2 := c.stack.pop() - a3 := c.stack.peekN(0) - a4 := c.stack.peekN(1) - *a3, *a4 = *a4, *a2 -} - -func opPush1_Push4_Dup3(c *context) { - opPush1(c) - c.pc++ - opPush4(c) - opDup(c, 3) -} - -func opAnd_Swap1_Pop_Swap2_Swap1(c *context) { - opAnd(c) - opSwap1_Pop_Swap2_Swap1(c) -} - -func opPush1_Push1_Push1_Shl_Sub(c *context) { - arg1 := c.code[c.pc].arg - arg2 := c.code[c.pc+1].arg - shift := uint8(arg2) - value := uint8(arg1 & 0xFF) - delta := uint8(arg1 >> 8) - trg := c.stack.pushUndefined() - trg.SetUint64(uint64(value)) - trg.Lsh(trg, uint(shift)) - trg.Sub(trg, uint256.NewInt(uint64(delta))) - c.pc++ -} diff --git a/go/interpreter/sfvm/super_instructions_test.go b/go/interpreter/sfvm/super_instructions_test.go deleted file mode 100644 index c3b049c0..00000000 --- a/go/interpreter/sfvm/super_instructions_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "testing" - - "github.com/holiman/uint256" -) - -func TestSI_opDup2_Lt(t *testing.T) { - - tests := map[string]struct { - a, b uint256.Int - result uint256.Int - }{ - "ab": { - a: *uint256.NewInt(1), - b: *uint256.NewInt(0), - result: *uint256.NewInt(1), - }, - "a==b": { - a: *uint256.NewInt(0), - b: *uint256.NewInt(0), - result: *uint256.NewInt(0), - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - - ctxt := getEmptyContext() - ctxt.stack = fillStack(test.a, test.b) - - opDup2_Lt(&ctxt) - - if want, got := 2, ctxt.stack.stackPointer; want != got { - t.Errorf("unexpected stack size, got %v, want %v", got, want) - } - - if want, got := test.result, ctxt.stack.peek(); want.Cmp(got) != 0 { - t.Errorf("unexpected result, got %v, expected %v", got, want) - } - }) - } -} From becbd5d3a5db3f4442f7b96b0c08faa82905e9ec Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Nov 2025 13:06:10 +0100 Subject: [PATCH 4/7] Remove experimental versions --- go/interpreter/sfvm/instruction_logger.go | 50 ----- .../sfvm/instruction_logger_test.go | 146 -------------- .../sfvm/instruction_statistcs_test.go | 177 ----------------- go/interpreter/sfvm/instruction_statistics.go | 184 ------------------ go/interpreter/sfvm/sfvm.go | 42 ---- 5 files changed, 599 deletions(-) delete mode 100644 go/interpreter/sfvm/instruction_logger.go delete mode 100644 go/interpreter/sfvm/instruction_logger_test.go delete mode 100644 go/interpreter/sfvm/instruction_statistcs_test.go delete mode 100644 go/interpreter/sfvm/instruction_statistics.go diff --git a/go/interpreter/sfvm/instruction_logger.go b/go/interpreter/sfvm/instruction_logger.go deleted file mode 100644 index 872adddd..00000000 --- a/go/interpreter/sfvm/instruction_logger.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "fmt" - "io" -) - -// loggingRunner is a runner that logs the execution of the contract code to an io.Writer. -// If the output log is nil, nothing is logged. -type loggingRunner struct { - log io.Writer -} - -// newLogger creates a new logging runner that writes to the provided -// io.Writer. -func newLogger(writer io.Writer) loggingRunner { - return loggingRunner{log: writer} -} - -func (l loggingRunner) run(c *context) (status, error) { - status := statusRunning - var err error - for status == statusRunning { - // log format: , , \n - if int(c.pc) < len(c.code) { - top := "-empty-" - if c.stack.len() > 0 { - top = c.stack.peek().ToBig().String() - } - if l.log != nil { - _, err = fmt.Fprintf(l.log, "%v, %d, %v\n", c.code[c.pc].opcode, c.gas, top) - if err != nil { - return status, err - } - } - } - status = execute(c, true) - } - return status, nil -} diff --git a/go/interpreter/sfvm/instruction_logger_test.go b/go/interpreter/sfvm/instruction_logger_test.go deleted file mode 100644 index a4d3748f..00000000 --- a/go/interpreter/sfvm/instruction_logger_test.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "bytes" - "fmt" - "io" - "os" - "strings" - "testing" - - "github.com/0xsoniclabs/tosca/go/tosca" -) - -func TestInterpreter_Logger_ExecutesCodeAndLogs(t *testing.T) { - - tests := map[string]struct { - code []Instruction - want string - }{ - "empty": {}, - "stop": { - code: []Instruction{{STOP, 0}}, - want: "STOP, 3, -empty-\n", - }, - "multiple codes": { - code: []Instruction{{PUSH4, 0}, {DATA, 1}, {STOP, 0}}, - want: "PUSH4, 3, -empty-\nSTOP, 0, 1\n", - }, - "out of gas": { - code: []Instruction{ - {PUSH1, 0}, - {PUSH1, 64}, - {MSTORE8, 0}, - {STOP, 0}, - }, - want: "PUSH1, 3, -empty-\nPUSH1, 0, 0\n", - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - - // Get tosca.Parameters - params := tosca.Parameters{Gas: 3} - code := test.code - buffer := bytes.NewBuffer([]byte{}) - logger := newLogger(buffer) - config := config{ - runner: logger, - } - _, err := run(config, params, code) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if strings.Compare(buffer.String(), test.want) != 0 { - t.Errorf("unexpected log: want %v, got %v", test.want, buffer.String()) - } - }) - } -} - -func TestInterpreter_Logger_RunsWithoutOutput(t *testing.T) { - - // Get tosca.Parameters - params := tosca.Parameters{} - code := []Instruction{{STOP, 0}} - - // redirect stdout - oldOut := os.Stdout - rOut, wOut, _ := os.Pipe() - os.Stdout = wOut - defer func() { os.Stdout = oldOut }() - - oldErr := os.Stderr - rErr, wErr, _ := os.Pipe() - os.Stderr = wErr - defer func() { os.Stderr = oldErr }() - - logger := newLogger(nil) - config := config{ - runner: logger, - } - - _, err := run(config, params, code) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - err = wOut.Close() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - outOut, err := io.ReadAll(rOut) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - err = wErr.Close() - if err != nil { - t.Errorf("unexpected error: %v", err) - } - outErr, err := io.ReadAll(rErr) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if len(outOut) != 0 { - t.Errorf("unexpected stdout: want \"\", got \"%v\"", outOut) - } - if len(outErr) != 0 { - t.Errorf("unexpected stderr: want \"\", got \"%v\"", outErr) - } -} - -type loggerErrorMock struct{} - -func (l loggerErrorMock) Write(p []byte) (n int, err error) { - return 0, fmt.Errorf("error") -} - -func TestInterpreter_logger_PropagatesWriterError(t *testing.T) { - - logger := newLogger(loggerErrorMock{}) - config := config{ - runner: logger, - } - // Get tosca.Parameters - params := tosca.Parameters{} - code := []Instruction{{STOP, 0}} - - _, err := run(config, params, code) - if strings.Compare(err.Error(), "error") != 0 { - t.Errorf("unexpected error: want error, got %v", err) - } -} diff --git a/go/interpreter/sfvm/instruction_statistcs_test.go b/go/interpreter/sfvm/instruction_statistcs_test.go deleted file mode 100644 index 9af10b0c..00000000 --- a/go/interpreter/sfvm/instruction_statistcs_test.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "fmt" - "strings" - "testing" - - "github.com/0xsoniclabs/tosca/go/tosca" - "github.com/0xsoniclabs/tosca/go/tosca/vm" -) - -func TestStatisticsRunner_RunWithStatistics(t *testing.T) { - // Get tosca.Parameters - params := tosca.Parameters{ - Input: []byte{}, - Static: true, - Gas: 10, - Code: []byte{byte(STOP), 0}, - } - code := []Instruction{{STOP, 0}} - - statsRunner := &statisticRunner{ - stats: newStatistics(), - } - config := config{ - runner: statsRunner, - } - _, err := run(config, params, code) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if got := statsRunner.stats.singleCount[uint64(STOP)]; got != 1 { - t.Errorf("unexpected statistics: want 1 stop, got %v", got) - } -} - -func TestStatisticsRunner_DumpProfilePrintsExpectedOutput(t *testing.T) { - - tests := map[string]struct { - code tosca.Code - findInOutput []string - }{ - "singles": {tosca.Code{byte(vm.STOP)}, - []string{ - "Steps: 1", - "STOP : 1 (100.00%)", - }}, - "pairs": {tosca.Code{byte(vm.PUSH1), 0x01, byte(vm.STOP)}, - []string{ - "Steps: 2", - "PUSH1 : 1 (50.00%)", - "STOP : 1 (50.00%)", - "PUSH1 STOP : 1"}}, - "triples": {tosca.Code{byte(vm.PUSH1), 0x01, byte(vm.PUSH1), 0x01, byte(vm.STOP)}, - []string{ - "Steps: 3", - "PUSH1 : 2 (66.67%)", - "STOP : 1 (33.33%)", - "PUSH1 PUSH1 STOP : 1"}}, - "quads": {tosca.Code{byte(vm.PUSH1), 0x01, byte(vm.PUSH1), 0x01, byte(vm.PUSH1), 0x01, byte(vm.STOP)}, - []string{ - "Steps: 4", - "PUSH1 : 3 (75.00%)", - "STOP : 1 (25.00%)", - "PUSH1 PUSH1 PUSH1 : 1 (25.00%)", - "PUSH1 PUSH1 STOP : 1 (25.00%)", - "PUSH1 PUSH1 PUSH1 STOP : 1 (25.00%)", - }}, - } - - for name, test := range tests { - t.Run(fmt.Sprintf("%v", name), func(t *testing.T) { - statsRunner := &statisticRunner{ - stats: newStatistics(), - } - - instance, err := newVm(config{ - runner: statsRunner, - }) - if err != nil { - t.Fatalf("Failed to create VM: %v", err) - } - instance.ResetProfile() - //run code - _, err = instance.Run(tosca.Parameters{Input: []byte{}, Static: true, Gas: 10, - Code: test.code}) - if err != nil { - t.Fatalf("Failed to run code: %v", err) - } - - // Run testing code - instance.DumpProfile() - - out := statsRunner.stats.print() - for _, s := range test.findInOutput { - if !strings.Contains(string(out), s) { - t.Errorf("did not find occurrences of %v in %v", s, string(out)) - } - } - }) - } -} - -func TestStatisticsRunner_getSummaryInitializesNewStatsWhenUninitialized(t *testing.T) { - statsRunner := &statisticRunner{ - stats: nil, - } - _ = statsRunner.getSummary() - if statsRunner.stats == nil { - t.Errorf("summary should have been initialized") - } -} - -func TestStatisticsRunner_runInitializesNewStatsWhenUninitialized(t *testing.T) { - statsRunner := &statisticRunner{ - stats: nil, - } - _, _ = statsRunner.run(&context{ - code: []Instruction{{STOP, 0}}, - stack: NewStack(), - }) - if statsRunner.stats == nil { - t.Errorf("run should have initialized stats") - } -} - -func TestStatisticsRunner_statisticsStopsWhenExecutionEncountersAnError(t *testing.T) { - statsRunner := &statisticRunner{ - stats: nil, - } - _, _ = statsRunner.run(&context{ - // this code should not reach a STOP since MCOPY should fail because - // there are not enough items on the stack - code: []Instruction{{MCOPY, 0}, {STOP, 0}}, - stack: NewStack(), - }) - if statsRunner.stats.singleCount[uint64(STOP)] != 0 { - t.Errorf("unexpected statistics: stop should not be executed, got %v", statsRunner.stats.singleCount[uint64(STOP)]) - } -} - -func TestStatisticsRunner_print_getTopN_returnFirstNElementsOfManyMore(t *testing.T) { - want := "\n----- Statistics ------\n" - want += "\nSteps: 0\n" - want += "\nSingles:\n" - want += "\tPUSH5 : 5 (+Inf%)\n" - want += "\tPUSH4 : 4 (+Inf%)\n" - want += "\tPUSH3 : 3 (+Inf%)\n" - want += "\tPUSH2 : 2 (+Inf%)\n" - want += "\tSTOP : 1 (+Inf%)\n" - want += "\nPairs:\n\nTriples:\n\nQuads:\n\n" - - stats := statistics{ - singleCount: map[uint64]uint64{ - uint64(STOP): 1, - uint64(PUSH1): 1, - uint64(PUSH2): 2, - uint64(PUSH3): 3, - uint64(PUSH4): 4, - uint64(PUSH5): 5, - }, - } - got := stats.print() - if got != want { - t.Errorf("unexpected output: want %v, got %v", want, got) - } -} diff --git a/go/interpreter/sfvm/instruction_statistics.go b/go/interpreter/sfvm/instruction_statistics.go deleted file mode 100644 index f0a48949..00000000 --- a/go/interpreter/sfvm/instruction_statistics.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "fmt" - "sort" - "strings" - "sync" -) - -// statisticRunner is a runner that collects statistics about the instruction -// sequence of the executed code. -type statisticRunner struct { - mutex sync.Mutex - stats *statistics -} - -func (s *statisticRunner) run(c *context) (status, error) { - stats := statsCollector{stats: newStatistics()} - status := statusRunning - for status == statusRunning { - if c.pc < int32(len(c.code)) { - stats.nextOp(c.code[c.pc].opcode) - } - status = execute(c, true) - } - s.mutex.Lock() - defer s.mutex.Unlock() - if s.stats == nil { - s.stats = newStatistics() - } - s.stats.insert(stats.stats) - return status, nil -} - -// getSummary returns a summary of the collected statistics in a human-readable -// format. -func (s *statisticRunner) getSummary() string { - s.mutex.Lock() - defer s.mutex.Unlock() - if s.stats == nil { - s.stats = newStatistics() - } - return s.stats.print() -} - -// reset clears the collected statistics. -func (s *statisticRunner) reset() { - s.mutex.Lock() - defer s.mutex.Unlock() - s.stats = newStatistics() -} - -// statistics contains the instruction sequence statistics of a code execution. -// It counts the number of times each instruction is executed, as well as the -// number of times each pair, triple, and quad of instructions are executed. -type statistics struct { - count uint64 - singleCount map[uint64]uint64 - pairCount map[uint64]uint64 - tripleCount map[uint64]uint64 - quadCount map[uint64]uint64 -} - -func newStatistics() *statistics { - return &statistics{ - singleCount: map[uint64]uint64{}, - pairCount: map[uint64]uint64{}, - tripleCount: map[uint64]uint64{}, - quadCount: map[uint64]uint64{}, - } -} - -// insert adds the instruction counts of the given statistics to this instance. -func (s *statistics) insert(src *statistics) { - s.count += src.count - for k, v := range src.singleCount { - s.singleCount[k] += v - } - for k, v := range src.pairCount { - s.pairCount[k] += v - } - for k, v := range src.tripleCount { - s.tripleCount[k] += v - } - for k, v := range src.quadCount { - s.quadCount[k] += v - } -} - -// print returns a human-readable summary of the collected statistics. -func (s *statistics) print() string { - - type entry struct { - value uint64 - count uint64 - } - - getTopN := func(data map[uint64]uint64, n int) []entry { - list := make([]entry, 0, len(data)) - for k, c := range data { - list = append(list, entry{k, c}) - } - sort.Slice(list, func(i, j int) bool { - if list[i].count != list[j].count { - return list[i].count > list[j].count - } - return list[i].value < list[j].value - }) - if len(list) < n { - return list - } - return list[0:n] - } - - builder := strings.Builder{} - write := func(format string, args ...interface{}) { - builder.WriteString(fmt.Sprintf(format, args...)) - } - - write("\n----- Statistics ------\n") - write("\nSteps: %d\n", s.count) - write("\nSingles:\n") - for _, e := range getTopN(s.singleCount, 5) { - write("\t%-30v: %d (%.2f%%)\n", OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) - } - write("\nPairs:\n") - for _, e := range getTopN(s.pairCount, 5) { - write("\t%-30v%-30v: %d (%.2f%%)\n", OpCode(e.value>>16), OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) - } - write("\nTriples:\n") - for _, e := range getTopN(s.tripleCount, 5) { - write("\t%-30v%-30v%-30v: %d (%.2f%%)\n", OpCode(e.value>>32), OpCode(e.value>>16), OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) - } - - write("\nQuads:\n") - for _, e := range getTopN(s.quadCount, 5) { - write("\t%-30v%-30v%-30v%-30v: %d (%.2f%%)\n", OpCode(e.value>>48), OpCode(e.value>>32), OpCode(e.value>>16), OpCode(e.value), e.count, float32(e.count*100)/float32(s.count)) - } - write("\n") - - return builder.String() -} - -// statsCollector is a helper struct that keeps track of the resent history of -// instructions executed by the VM to collect instruction sequence statistics. -type statsCollector struct { - stats *statistics - - last uint64 - secondLast uint64 - thirdLast uint64 -} - -func (s *statsCollector) nextOp(op OpCode) { - cur := uint64(op) - s.stats.count++ - s.stats.singleCount[cur]++ - if s.stats.count == 1 { - s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast - return - } - s.stats.pairCount[s.last<<16|cur]++ - if s.stats.count == 2 { - s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast - return - } - s.stats.tripleCount[s.secondLast<<32|s.last<<16|cur]++ - if s.stats.count == 3 { - s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast - return - } - s.stats.quadCount[s.thirdLast<<48|s.secondLast<<32|s.last<<16|cur]++ - s.last, s.secondLast, s.thirdLast = cur, s.last, s.secondLast -} diff --git a/go/interpreter/sfvm/sfvm.go b/go/interpreter/sfvm/sfvm.go index 21d74913..0e2cd6a2 100644 --- a/go/interpreter/sfvm/sfvm.go +++ b/go/interpreter/sfvm/sfvm.go @@ -12,7 +12,6 @@ package sfvm import ( "fmt" - "os" "github.com/0xsoniclabs/tosca/go/tosca" ) @@ -44,35 +43,6 @@ func init() { func RegisterExperimentalInterpreterConfigurations() error { configs := map[string]config{} - - for _, shaCache := range []string{"", "-no-sha-cache"} { - for _, mode := range []string{"", "-stats", "-logging"} { - - config := config{ - ConversionConfig: ConversionConfig{}, - WithShaCache: shaCache != "-no-sha-cache", - } - - switch mode { - case "-stats": - config.runner = &statisticRunner{ - stats: newStatistics(), - } - case "-logging": - config.runner = loggingRunner{ - log: os.Stdout, - } - } - - name := "sfvm" + shaCache + mode - if name == "sfvm" { - continue - } - - configs[name] = config - } - } - configs["sfvm-no-code-cache"] = config{ ConversionConfig: ConversionConfig{CacheSize: -1}, } @@ -129,15 +99,3 @@ func (e *sfvm) Run(params tosca.Parameters) (tosca.Result, error) { return run(e.config, params, converted) } - -func (e *sfvm) DumpProfile() { - if statsRunner, ok := e.config.runner.(*statisticRunner); ok { - fmt.Print(statsRunner.getSummary()) - } -} - -func (e *sfvm) ResetProfile() { - if statsRunner, ok := e.config.runner.(*statisticRunner); ok { - statsRunner.reset() - } -} From d5222a1de4b06fb676f0acc912757eaf078b311a Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Nov 2025 13:56:23 +0100 Subject: [PATCH 5/7] Remove long format --- go/interpreter/sfvm/converter.go | 231 ---------- go/interpreter/sfvm/converter_fuzz_test.go | 105 ----- go/interpreter/sfvm/converter_test.go | 493 --------------------- go/interpreter/sfvm/ct.go | 83 +--- go/interpreter/sfvm/ct_test.go | 210 --------- go/interpreter/sfvm/example_code_test.go | 16 - go/interpreter/sfvm/gas.go | 196 ++++---- go/interpreter/sfvm/instruction.go | 42 -- go/interpreter/sfvm/instruction_test.go | 44 -- go/interpreter/sfvm/instructions.go | 101 +++-- go/interpreter/sfvm/instructions_test.go | 292 +++--------- go/interpreter/sfvm/interpreter.go | 346 +++++++-------- go/interpreter/sfvm/interpreter_mock.go | 64 --- go/interpreter/sfvm/interpreter_test.go | 241 ++++------ go/interpreter/sfvm/opcode.go | 325 -------------- go/interpreter/sfvm/opcode_test.go | 68 --- go/interpreter/sfvm/sfvm.go | 52 +-- go/interpreter/sfvm/sfvm_interface_test.go | 20 +- go/interpreter/sfvm/sfvm_test.go | 10 - go/interpreter/sfvm/stack_usage.go | 54 +-- go/interpreter/sfvm/stack_usage_test.go | 22 +- 21 files changed, 549 insertions(+), 2466 deletions(-) delete mode 100644 go/interpreter/sfvm/converter.go delete mode 100644 go/interpreter/sfvm/converter_fuzz_test.go delete mode 100644 go/interpreter/sfvm/converter_test.go delete mode 100644 go/interpreter/sfvm/example_code_test.go delete mode 100644 go/interpreter/sfvm/instruction.go delete mode 100644 go/interpreter/sfvm/instruction_test.go delete mode 100644 go/interpreter/sfvm/interpreter_mock.go delete mode 100644 go/interpreter/sfvm/opcode.go delete mode 100644 go/interpreter/sfvm/opcode_test.go diff --git a/go/interpreter/sfvm/converter.go b/go/interpreter/sfvm/converter.go deleted file mode 100644 index f63e5bdf..00000000 --- a/go/interpreter/sfvm/converter.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "math" - "unsafe" - - "github.com/0xsoniclabs/tosca/go/ct/common" - "github.com/0xsoniclabs/tosca/go/tosca" - - "github.com/0xsoniclabs/tosca/go/tosca/vm" - lru "github.com/hashicorp/golang-lru/v2" -) - -// ConversionConfig contains a set of configuration options for the code conversion. -type ConversionConfig struct { - // CacheSize is the maximum size of the maintained code cache in bytes. - // If set to 0, a default size is used. If negative, no cache is used. - // Cache sizes are grown in increments of maxCachedCodeLength. - // Positive values larger than 0 but less than maxCachedCodeLength are - // reported as invalid cache sizes during initialization. - CacheSize int -} - -// Converter converts EVM code to SFVM code. -type Converter struct { - config ConversionConfig - cache *lru.Cache[tosca.Hash, Code] -} - -// NewConverter creates a new code converter with the provided configuration. -func NewConverter(config ConversionConfig) (*Converter, error) { - if config.CacheSize == 0 { - config.CacheSize = (1 << 30) // = 1GiB - } - - var cache *lru.Cache[tosca.Hash, Code] - if config.CacheSize > 0 { - var err error - const instructionSize = int(unsafe.Sizeof(Instruction{})) - capacity := config.CacheSize / maxCachedCodeLength / instructionSize - cache, err = lru.New[tosca.Hash, Code](capacity) - if err != nil { - return nil, err - } - } - return &Converter{ - config: config, - cache: cache, - }, nil -} - -// Convert converts EVM code to SFVM code. If the provided code hash is not nil, -// it is assumed to be a valid hash of the code and is used to cache the -// conversion result. If the hash is nil, the conversion result is not cached. -func (c *Converter) Convert(code []byte, codeHash *tosca.Hash) (Code, error) { - if len(code) > math.MaxUint16 { - return Code{}, errCodeSizeExceeded - } - - if c.cache == nil || codeHash == nil { - return convert(code, c.config), nil - } - - res, exists := c.cache.Get(*codeHash) - if exists { - return res, nil - } - - res = convert(code, c.config) - if len(res) > maxCachedCodeLength { - return res, nil - } - - c.cache.Add(*codeHash, res) - return res, nil -} - -// maxCachedCodeLength is the maximum length of a code in bytes that are -// retained in the cache. To avoid excessive memory usage, longer codes are not -// cached. The defined limit is the current limit for codes stored on the chain. -// Only initialization codes can be longer. Since the Shanghai hard fork, the -// maximum size of initialization codes is 2 * 24_576 = 49_152 bytes (see -// https://eips.ethereum.org/EIPS/eip-3860). Such init codes are deliberately -// not cached due to the expected limited re-use and the missing code hash. -const maxCachedCodeLength = 1<<14 + 1<<13 // = 24_576 bytes - -// --- code builder --- - -type codeBuilder struct { - code []Instruction - nextPos int -} - -func newCodeBuilder(codelength int) codeBuilder { - return codeBuilder{make([]Instruction, codelength), 0} -} - -func (b *codeBuilder) length() int { - return b.nextPos -} - -func (b *codeBuilder) appendOp(opcode OpCode, arg uint16) *codeBuilder { - b.code[b.nextPos].opcode = opcode - b.code[b.nextPos].arg = arg - b.nextPos++ - return b -} - -func (b *codeBuilder) appendCode(opcode OpCode) *codeBuilder { - b.code[b.nextPos].opcode = opcode - b.nextPos++ - return b -} - -func (b *codeBuilder) appendData(data uint16) *codeBuilder { - return b.appendOp(DATA, data) -} - -func (b *codeBuilder) padNoOpsUntil(pos int) { - for i := b.nextPos; i < pos; i++ { - b.code[i].opcode = NOOP - } - b.nextPos = pos -} - -func (b *codeBuilder) toCode() Code { - return b.code[0:b.nextPos] -} - -func convert(code []byte, options ConversionConfig) Code { - return convertWithObserver(code, options, func(int, int) {}) -} - -// convertWithObserver converts EVM code to SFVM code and calls the observer -// with the code position of every pair of instructions converted. -func convertWithObserver( - code []byte, - options ConversionConfig, - observer func(evmPc int, sfvmPc int), -) Code { - res := newCodeBuilder(len(code)) - - // Convert each individual instruction. - for i := 0; i < len(code); { - // Handle jump destinations - if code[i] == byte(vm.JUMPDEST) { - // Jump to the next jump destination and fill space with noops - if res.length() < i { - res.appendOp(JUMP_TO, uint16(i)) - } - res.padNoOpsUntil(i) - res.appendCode(JUMPDEST) - observer(i, i) - i++ - continue - } - - // Convert instructions - observer(i, res.nextPos) - inc := appendInstructions(&res, i, code) - i += inc + 1 - } - return res.toCode() -} - -func appendInstructions(res *codeBuilder, pos int, code []byte) int { - // Convert individual instructions. - toscaOpCode := vm.OpCode(code[pos]) - - if toscaOpCode == vm.PC { - if pos > math.MaxUint16 { - res.appendCode(INVALID) - return 1 - } - res.appendOp(PC, uint16(pos)) - return 0 - } - - if vm.PUSH1 <= toscaOpCode && toscaOpCode <= vm.PUSH32 { - // Determine the number of bytes to be pushed. - numBytes := int(toscaOpCode) - int(vm.PUSH1) + 1 - - var data []byte - // If there are not enough bytes left in the code, rest is filled with 0 - // zeros are padded right - if len(code) < pos+numBytes+2 { - extension := (pos + numBytes + 2 - len(code)) / 2 - if (pos+numBytes+2-len(code))%2 > 0 { - extension++ - } - if extension > 0 { - instruction := common.RightPadSlice(res.code[:], len(res.code)+extension) - res.code = instruction - } - data = common.RightPadSlice(code[pos+1:], numBytes+1) - } else { - data = code[pos+1 : pos+1+numBytes] - } - - // Fix the op-codes of the resulting instructions - if numBytes == 1 { - res.appendOp(PUSH1, uint16(data[0])<<8) - } else { - res.appendOp(PUSH1+OpCode(numBytes-1), uint16(data[0])<<8|uint16(data[1])) - } - - // Fix the arguments by packing them in pairs into the instructions. - for i := 2; i < numBytes-1; i += 2 { - res.appendData(uint16(data[i])<<8 | uint16(data[i+1])) - } - if numBytes > 1 && numBytes%2 == 1 { - res.appendData(uint16(data[numBytes-1]) << 8) - } - - return numBytes - } - - // All the rest converts to a single instruction. - res.appendCode(OpCode(toscaOpCode)) - return 0 -} diff --git a/go/interpreter/sfvm/converter_fuzz_test.go b/go/interpreter/sfvm/converter_fuzz_test.go deleted file mode 100644 index f42c9943..00000000 --- a/go/interpreter/sfvm/converter_fuzz_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "math" - "testing" - - "github.com/0xsoniclabs/tosca/go/tosca/vm" -) - -// To run this fuzzer use the following command: -// go test ./interpreter/sfvm -run none -fuzz SfvmConverter --fuzztime 10m - -func FuzzSfvmConverter(f *testing.F) { - - // Add empty code - f.Add([]byte{}) - - f.Fuzz(func(t *testing.T, toscaCode []byte) { - - // EIP-170 stablish maximum code size to 0x6000 bytes, ~22 KB - // (see https://eips.ethereum.org/EIPS/eip-170) - // EIP-3860 stablish maximum init code size to 49_152 bytes - // (see https://eips.ethereum.org/EIPS/eip-3860) - // Before EIP-3860, any size was allowed, but in the Fantom - // network anything larger than 2^16 was observed, which is - // also the limit for the conversion code (due to a 16-bit PC - // counter representation in the PC instruction). Thus, we test - // the code up to this level. - maxCodeSize := math.MaxUint16 - if len(toscaCode) > maxCodeSize { - t.Skip() - } - - mapping := make([]int, len(toscaCode)) - sfvmCode := convertWithObserver(toscaCode, ConversionConfig{}, func(evm, sfvm int) { - mapping[evm] = sfvm - }) - - // Check that all operations are mapped to matching operations. - for i := 0; i < len(toscaCode); i++ { - originalPos := i - sfvmPos := mapping[originalPos] - - toscaOpCode := vm.OpCode(toscaCode[originalPos]) - sfvmOpCode := sfvmCode[sfvmPos].opcode - - if !sfvmOpCode.isBaseInstruction() { - t.Errorf("Expected base instructions only, got %v", sfvmOpCode) - } - - if vm.OpCode(sfvmOpCode) != toscaOpCode { - t.Errorf("Invalid conversion from %v to %v", toscaOpCode, sfvmOpCode) - } - - // Check that the position of JUMPDEST ops are preserved. - if toscaOpCode == vm.JUMPDEST { - if originalPos != sfvmPos { - t.Errorf("Expected JUMPDEST at %d, got %d", originalPos, sfvmPos) - } - } - - // Check that PC instructions point to the correct target. - if toscaOpCode == vm.PC { - target := int(sfvmCode[sfvmPos].arg) - if target != originalPos { - t.Errorf("Invalid PC target, wanted %d, got %d", originalPos, target) - } - } - - // Skip the data section of PUSH instructions. - if vm.PUSH1 <= toscaOpCode && toscaOpCode <= vm.PUSH32 { - i += int(toscaOpCode-vm.PUSH1) + 1 - } - } - - // Check that JUMP_TO instructions point to their immediately succeeding JUMPDEST. - for i := 0; i < len(sfvmCode); i++ { - if sfvmCode[i].opcode == JUMP_TO { - trg := int(sfvmCode[i].arg) - if trg < i { - t.Errorf("invalid JUMP_TO target from %d to %d", i, trg) - } - if trg >= len(sfvmCode) || sfvmCode[trg].opcode != JUMPDEST { - t.Fatalf("JUMP_TO target %d is not a JUMPDEST", trg) - } - for j := i + 1; j < trg; j++ { - cur := sfvmCode[j].opcode - if cur != NOOP { - t.Errorf("found %v between JUMP_TO and JUMPDEST at %d", cur, j) - } - } - } - } - }) -} diff --git a/go/interpreter/sfvm/converter_test.go b/go/interpreter/sfvm/converter_test.go deleted file mode 100644 index ad198819..00000000 --- a/go/interpreter/sfvm/converter_test.go +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "bytes" - "errors" - "math" - "math/rand" - "slices" - "testing" - "time" - "unsafe" - - "github.com/0xsoniclabs/tosca/go/tosca" - "github.com/0xsoniclabs/tosca/go/tosca/vm" - "golang.org/x/sync/errgroup" -) - -func TestNewConverter_UsesDefaultCapacity(t *testing.T) { - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - if want, got := (1 << 30), converter.config.CacheSize; got != want { - t.Errorf("Expected default cache capacity of %d, got %d", want, got) - } -} - -func TestNewConverter_CacheCanBeDisabled(t *testing.T) { - converter, err := NewConverter(ConversionConfig{ - CacheSize: -1, - }) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - if want, got := -1, converter.config.CacheSize; got != want { - t.Errorf("Expected default cache capacity of %d, got %d", want, got) - } - if converter.cache != nil { - t.Errorf("Expected cache to be disabled") - } - // Conversion should still work without a nil pointer dereference. - _, err = converter.Convert([]byte{0}, &tosca.Hash{0}) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - } -} - -func TestNewConverter_TooSmallCapacityLeadsToCreationIssues(t *testing.T) { - _, err := NewConverter(ConversionConfig{ - CacheSize: maxCachedCodeLength / 2, - }) - if err == nil { - t.Fatalf("expected error when creating converter with too small cache size") - } -} - -func TestConverter_LongExampleCode(t *testing.T) { - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - _, err = converter.Convert(longExampleCode, nil) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - } -} - -func TestConverter_CodeLargerThanMaxUint16ReturnsAnError(t *testing.T) { - tests := map[string]struct { - codeSize int - err error - }{ - "small code": { - codeSize: math.MaxUint16 - 1, - err: nil, - }, - "exact code": { - codeSize: math.MaxUint16, - err: nil, - }, - "large code": { - codeSize: math.MaxUint16 + 1, - err: errCodeSizeExceeded, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - config := config{ - ConversionConfig: ConversionConfig{}, - } - vm, err := newVm(config) - if err != nil { - t.Fatalf("unexpected error") - } - _, err = vm.Run(tosca.Parameters{Code: make([]byte, test.codeSize)}) - if !errors.Is(err, test.err) { - t.Fatalf("unexpected error: want %v, got %v", test.err, err) - } - }) - } -} - -func TestConverter_InputsAreCachedUsingHashAsKey(t *testing.T) { - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - code := []byte{byte(vm.STOP)} - hash := tosca.Hash{byte(1)} - want, err := converter.Convert(code, &hash) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - - } - got, err := converter.Convert(code, &hash) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - } - if &want[0] != &got[0] { // < it needs to be the same slice - t.Errorf("cached conversion result not returned") - } -} - -func TestConverter_CacheSizeLimitIsEnforced(t *testing.T) { - for _, limit := range []int{10, 100, 1000} { - const instructionSize = int(unsafe.Sizeof(Instruction{})) - converter, err := NewConverter(ConversionConfig{ - CacheSize: limit * maxCachedCodeLength * instructionSize, - }) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - for i := 0; i < limit*10; i++ { - hash := tosca.Hash{byte(i), byte(i >> 8), byte(i >> 16)} - _, err = converter.Convert([]byte{0}, &hash) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - } - } - if got := len(converter.cache.Keys()); got > limit { - t.Errorf("Conversion cache grew to %d entries", got) - } - } -} - -func TestConverter_ExceedinglyLongCodesAreNotCached(t *testing.T) { - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - if want, got := 0, len(converter.cache.Keys()); want != got { - t.Errorf("Expected %d entries in the cache, got %d", want, got) - } - _, err = converter.Convert([]byte{0}, &tosca.Hash{0}) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - } - if want, got := 1, len(converter.cache.Keys()); want != got { - t.Errorf("Expected %d entries in the cache, got %d", want, got) - } - // Codes with an excessive length should not be cached. - _, err = converter.Convert(make([]byte, maxCachedCodeLength+1), &tosca.Hash{1}) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - } - if want, got := 1, len(converter.cache.Keys()); want != got { - t.Errorf("Expected %d entries in the cache, got %d", want, got) - } -} - -func TestConverter_ResultsAreCached(t *testing.T) { - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - code := []byte{byte(vm.STOP)} - hash := tosca.Hash{byte(1)} - want, err := converter.Convert(code, &hash) - if err != nil { - t.Fatalf("failed to convert code: %v", err) - } - if got, found := converter.cache.Get(hash); !found || !slices.Equal(want, got) { - t.Errorf("converted code not added to cache") - } -} - -func TestConverter_ConverterIsThreadSafe(t *testing.T) { - // This test is to be run with --race to detect concurrency issues. - const ( - NumGoroutines = 100 - NumSteps = 1000 - ) - - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - t.Fatalf("failed to create converter: %v", err) - } - code := []byte{byte(vm.STOP)} - hash := tosca.Hash{byte(1)} - - errs, _ := errgroup.WithContext(t.Context()) - errs.SetLimit(NumGoroutines) - for i := range NumGoroutines { - errs.Go(func() error { - for j := 0; j < NumSteps; j++ { - // read a value every go routine is requesting - _, routineErr := converter.Convert(code, &hash) - if routineErr != nil { - return routineErr - } - // convert a value only this go routine is requesting - _, routineErr = converter.Convert(code, &tosca.Hash{byte(i), byte(j)}) - if routineErr != nil { - return routineErr - } - } - return nil - }) - } - err = errs.Wait() - if err != nil { - t.Fatalf("failed to run converter: %v", err) - } -} - -func TestConvertWithObserver_MapsEvmToSfvmPositions(t *testing.T) { - code := []byte{ - byte(vm.ADD), - byte(vm.PUSH1), 1, - byte(vm.PUSH3), 1, 2, 3, - byte(vm.SWAP1), - byte(vm.JUMPDEST), - } - - type pair struct { - evm, sfvm int - } - var pairs []pair - res := convertWithObserver(code, ConversionConfig{}, func(evm, sfvm int) { - pairs = append(pairs, pair{evm, sfvm}) - }) - - want := []pair{ - {0, 0}, - {1, 1}, - {3, 2}, - {7, 4}, - {8, 8}, - } - - if !slices.Equal(pairs, want) { - t.Errorf("Expected %v, got %v", want, pairs) - } - - for _, p := range pairs { - if want, got := OpCode(code[p.evm]), res[p.sfvm].opcode; want != got { - t.Errorf("Expected %v, got %v", want, got) - } - } -} - -func TestConvertWithObserver_PreservesJumpDestLocations(t *testing.T) { - r := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) - - for i := 0; i < 100; i++ { - code := make([]byte, 100) - r.Read(code) - - mapping := map[int]int{} - res := convertWithObserver(code, ConversionConfig{}, func(evm, sfvm int) { - if _, found := mapping[evm]; found { - t.Errorf("Duplicate mapping for EVM position %d", evm) - } - mapping[evm] = sfvm - }) - - // Check that all operations are mapped to matching operations. - for evm, sfvm := range mapping { - if want, got := OpCode(code[evm]), res[sfvm].opcode; want != got { - t.Errorf("Expected %v, got %v", want, got) - } - } - - // Check that the position of JUMPDESTs is preserved. - for evm, sfvm := range mapping { - if vm.OpCode(code[evm]) == vm.JUMPDEST { - if evm != sfvm { - t.Errorf("Expected JUMPDEST at %d, got %d", evm, sfvm) - } - } - } - - // Check that all JUMPDEST operations got mapped. - for i := 0; i < len(code); i++ { - cur := vm.OpCode(code[i]) - if cur == vm.JUMPDEST { - if _, found := mapping[i]; !found { - t.Errorf("JUMPDEST at %d not mapped", i) - } - } - if vm.PUSH1 <= cur && cur <= vm.PUSH32 { - i += int(cur - vm.PUSH1 + 1) - } - } - } -} - -func TestConvert_ProgramCounterBeyond16bitAreConvertedIntoInvalidInstructions(t *testing.T) { - max := math.MaxUint16 - positions := []int{0, 1, max / 2, max - 1, max, max + 1} - code := make([]byte, max+2) - for _, pos := range positions { - code[pos] = byte(vm.PC) - } - res := convert(code, ConversionConfig{}) - - for _, pos := range positions { - want := PC - if pos > max { - want = INVALID - } - if got := res[pos].opcode; want != got { - t.Errorf("Expected %v at position %d, got %v", want, pos, got) - } - } -} - -func TestConvert_BaseInstructionsAreConvertedToEquivalents(t *testing.T) { - config := ConversionConfig{} - for _, op := range allOpCodesWhere(OpCode.isBaseInstruction) { - t.Run(op.String(), func(t *testing.T) { - code := []byte{byte(op)} - res := convert(code, config) - if want, got := op, res[0].opcode; want != got { - t.Errorf("Expected %v, got %v", want, got) - } - }) - } -} - -func TestConvert_PushOperationsUsePaddedImmediateData(t *testing.T) { - data := []byte{} - for i := 0; i < 32; i++ { - data = append(data, byte(i+1)) - } - for op := PUSH1; op <= PUSH32; op++ { - t.Run(op.String(), func(t *testing.T) { - // Test all possible truncated push data lengths. - length := int(op) - int(PUSH1) + 1 - for i := 0; i <= length; i++ { - code := append([]byte{byte(op)}, data[:i]...) - res := convert(code, ConversionConfig{}) - - // the push operation is correct - if want, got := op, res[0].opcode; want != got { - t.Errorf("Expected %v, got %v", want, got) - } - - // all the rest is data - for i, op := range res[1:] { - if op.opcode != DATA { - t.Errorf("Expected DATA at position %d, got %v", i, op.opcode) - } - } - - // there is enough data - if got, want := len(res), length/2+length%2; got != want { - t.Errorf("Expected %d instructions, got %d", want, got) - } - - // re-construct data - gotData := make([]byte, length+1) - for i, op := range res { - gotData[i*2] = byte(op.arg >> 8) - gotData[i*2+1] = byte(op.arg) - } - gotData = gotData[:length] - - // make sure prefix is correct - if want, got := data[:i], gotData[:i]; !bytes.Equal(want, got) { - t.Errorf("Expected %x, got %x", want, got) - } - - // make sure zero padding is correct - if want, got := make([]byte, length-i), gotData[i:]; !bytes.Equal(want, got) { - t.Errorf("Expected %x, got %x", want, got) - } - } - }) - } -} - -func TestConvert_AllJumpToOperationsPointToSubsequentJumpdest(t *testing.T) { - r := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) - - counter := 0 - for i := 0; i < 1000; i++ { - code := make([]byte, 100) - r.Read(code) - res := convert(code, ConversionConfig{}) - - for i, instruction := range res { - if instruction.opcode == JUMP_TO { - counter++ - trg := instruction.arg - if trg <= uint16(i) { - t.Errorf("JUMP_TO %d points to preceding position %d", trg, i) - } - if trg >= uint16(len(res)) { - t.Fatalf("JUMP_TO %d out of bounds", trg) - } - if res[trg].opcode != JUMPDEST { - t.Errorf("JUMP_TO %d does not point to JUMPDEST", trg) - } - - // Everything from the JUMP_TO to to the jump destination is a - // NOOP instruction. - for pos := i + 1; pos < int(trg); pos++ { - if res[pos].opcode != NOOP { - t.Errorf("Expected NOOP at position %d, got %v", pos, res[pos].opcode) - } - } - } - } - } - if counter == 0 { - t.Errorf("No JUMP_TO operations found") - } -} - -func benchmarkConvertCode(b *testing.B, code []byte, config ConversionConfig) { - converter, err := NewConverter(config) - if err != nil { - b.Fatalf("failed to create converter: %v", err) - } - for i := 0; i < b.N; i++ { - _, err = converter.Convert(code, nil) - if err != nil { - b.Fatalf("failed to convert code: %v", err) - } - } -} - -func BenchmarkConvertLongExampleCodeNoCache(b *testing.B) { - benchmarkConvertCode(b, longExampleCode, ConversionConfig{CacheSize: -1}) -} - -func BenchmarkConvertLongExampleCode(b *testing.B) { - benchmarkConvertCode(b, longExampleCode, ConversionConfig{}) -} - -func BenchmarkConversionCacheLookupSpeed(b *testing.B) { - // This benchmark measures the lookup speed of the conversion cache - // by converting the same code snippet multiple times. - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - b.Fatalf("failed to create converter: %v", err) - } - hash := &tosca.Hash{0} - for i := 0; i < b.N; i++ { - _, err = converter.Convert(nil, hash) - if err != nil { - b.Fatalf("failed to convert code: %v", err) - } - } -} - -func BenchmarkConversionCacheUpdateSpeed(b *testing.B) { - // This benchmark measures the update speed of the conversion cache - // by converting codes with different (reported) hashes. - converter, err := NewConverter(ConversionConfig{}) - if err != nil { - b.Fatalf("failed to create converter: %v", err) - } - for i := 0; i < b.N; i++ { - hash := tosca.Hash{byte(i), byte(i >> 8), byte(i >> 16), byte(i >> 24)} - _, err = converter.Convert(nil, &hash) - if err != nil { - b.Fatalf("failed to convert code: %v", err) - } - } -} diff --git a/go/interpreter/sfvm/ct.go b/go/interpreter/sfvm/ct.go index 9965be96..bb6f7f72 100644 --- a/go/interpreter/sfvm/ct.go +++ b/go/interpreter/sfvm/ct.go @@ -11,14 +11,11 @@ package sfvm import ( - "fmt" - "github.com/0xsoniclabs/tosca/go/ct" "github.com/0xsoniclabs/tosca/go/ct/common" "github.com/0xsoniclabs/tosca/go/ct/st" "github.com/0xsoniclabs/tosca/go/ct/utils" "github.com/0xsoniclabs/tosca/go/tosca" - lru "github.com/hashicorp/golang-lru/v2" ) func NewConformanceTestingTarget() ct.Evm { @@ -26,18 +23,13 @@ func NewConformanceTestingTarget() ct.Evm { // Can only fail for invalid configuration. Configuration is hardcoded. sanctionedVm, _ := NewInterpreter(Config{}) - // can only fail for non-positive size - cache, _ := lru.New[[32]byte, *pcMap](4096) - return &ctAdapter{ - vm: sanctionedVm, - pcMapCache: cache, + vm: sanctionedVm, } } type ctAdapter struct { - vm *sfvm - pcMapCache *lru.Cache[[32]byte, *pcMap] + vm *sfvm } func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { @@ -51,28 +43,18 @@ func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { return state, nil } - converted, err := a.vm.converter.Convert( - params.Code, - params.CodeHash, - ) - if err != nil { - return &st.State{}, fmt.Errorf("failed to convert code: %w", err) - } - - pcMap := a.getPcMap(state.Code) - memory := convertCtMemoryToSfvmMemory(state.Memory) // Set up execution context. var ctxt = &context{ - pc: int32(pcMap.evmToSfvm[state.Pc]), + pc: int32(state.Pc), params: params, context: params.Context, gas: params.Gas, refund: tosca.Gas(state.GasRefund), stack: convertCtStackToSfvmStack(state.Stack), memory: memory, - code: converted, + code: params.Code, returnData: state.LastCallReturnData.ToBytes(), withShaCache: a.vm.config.WithShaCache, } @@ -91,7 +73,7 @@ func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { state.Status = convertSfvmStatusToCtStatus(status) if status == statusRunning { - state.Pc = pcMap.sfvmToEvm[ctxt.pc] + state.Pc = uint16(ctxt.pc) } state.Gas = ctxt.gas @@ -106,61 +88,6 @@ func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { return state, nil } -func (a *ctAdapter) getPcMap(code *st.Code) *pcMap { - hash := code.Hash() - pcMap, found := a.pcMapCache.Get(hash) - if found { - return pcMap - } - byteCode := code.Copy() - pcMap = genPcMap(byteCode) - a.pcMapCache.Add(hash, pcMap) - return pcMap -} - -// pcMap is a bidirectional map to map program counters between evm <-> sfvm. -type pcMap struct { - evmToSfvm []uint16 - sfvmToEvm []uint16 -} - -// genPcMap creates a bidirectional program counter map for a given code, -// allowing mapping from a program counter in evm code to sfvm and vice versa. -func genPcMap(code []byte) *pcMap { - evmToSfvm := make([]uint16, len(code)+1) - sfvmToEvm := make([]uint16, len(code)+1) - - config := ConversionConfig{} - res := convertWithObserver(code, config, func(evm, sfvm int) { - evmToSfvm[evm] = uint16(sfvm) - sfvmToEvm[sfvm] = uint16(evm) - }) - - // A program counter may correctly point to the position after the last - // instruction, which would lead to an implicit STOP. - evmToSfvm[len(code)] = uint16(len(res)) - - // The SFVM code could also be longer than the input code if extra padding - // of truncated PUSH instructions has been added. - if len(res)+1 > len(sfvmToEvm) { - sfvmToEvm = append(sfvmToEvm, make([]uint16, len(res)+1-len(sfvmToEvm))...) - } - sfvmToEvm[len(res)] = uint16(len(code)) - - // Locations pointing to JUMP_TO instructions in SFVM need to be updated to - // the position of the jump target. - for i := 0; i < len(res); i++ { - if res[i].opcode == JUMP_TO { - sfvmToEvm[i] = res[i].arg - } - } - - return &pcMap{ - evmToSfvm: evmToSfvm, - sfvmToEvm: sfvmToEvm, - } -} - func convertSfvmStatusToCtStatus(status status) st.StatusCode { switch status { case statusRunning: diff --git a/go/interpreter/sfvm/ct_test.go b/go/interpreter/sfvm/ct_test.go index 060ea062..d95904f3 100644 --- a/go/interpreter/sfvm/ct_test.go +++ b/go/interpreter/sfvm/ct_test.go @@ -13,7 +13,6 @@ package sfvm import ( "bytes" "errors" - "math" "testing" "github.com/0xsoniclabs/tosca/go/ct" @@ -59,24 +58,6 @@ func TestCtAdapter_Interface(t *testing.T) { var _ ct.Evm = &ctAdapter{} } -func TestCTAdapter_DoesNotAddDuplicatedCodeToPCMap(t *testing.T) { - s := st.NewState(st.NewCode([]byte{ - byte(vm.STOP), - })) - c := NewConformanceTestingTarget() - - for i := 0; i < 3; i++ { - s.Status = st.Running - _, err := c.StepN(s, 1) - if err != nil { - t.Fatalf("unexpected conversion error: %v", err) - } - if want, got := 1, c.(*ctAdapter).pcMapCache.Len(); want != got { - t.Fatalf("unexpected pc map size, wanted %d, got %d", want, got) - } - } -} - func TestCtAdapter_ReturnsErrorForUnsupportedRevisions(t *testing.T) { unsupportedRevision := newestSupportedRevision + 1 want := &tosca.ErrUnsupportedRevision{Revision: unsupportedRevision} @@ -94,19 +75,6 @@ func TestCtAdapter_ReturnsErrorForUnsupportedRevisions(t *testing.T) { } } -func TestCtAdapter_ReturnsErrorIfCodeSizeExceeded(t *testing.T) { - s := st.NewState(st.NewCode(make([]byte, math.MaxUint16+1))) - s.Status = st.Running - s.Revision = tosca.R07_Istanbul - - c := NewConformanceTestingTarget() - _, err := c.StepN(s, 1) - - if !errors.Is(err, errCodeSizeExceeded) { - t.Errorf("unexpected error, wanted %v, got %v", errCodeSizeExceeded, err) - } -} - func TestCtAdapter_DoesNotAffectNonRunningStates(t *testing.T) { s := st.NewState(st.NewCode([]byte{ byte(vm.STOP), @@ -190,146 +158,6 @@ func TestConvertToSfvm_StatusCodeFailsOnUnknownStatus(t *testing.T) { } } -func TestConvertToSfvm_Pc(t *testing.T) { - tests := map[string][]struct { - evmCode []byte - evmPc uint16 - sfvmPc uint16 - }{ - "empty": {{}}, - "pos-0": {{[]byte{byte(vm.STOP)}, 0, 0}}, - "pos-1": {{[]byte{byte(vm.STOP), byte(vm.STOP), byte(vm.STOP)}, 1, 1}}, - "one-past-end": {{[]byte{byte(vm.STOP)}, 1, 1}}, - "shifted": {{[]byte{ - byte(vm.PUSH1), 0x01, - byte(vm.PUSH1), 0x02, - byte(vm.ADD)}, 2, 1}}, - "jumpdest": {{[]byte{ - byte(vm.PUSH3), 0x00, 0x00, 0x06, - byte(vm.JUMP), - byte(vm.INVALID), - byte(vm.JUMPDEST)}, - 6, 6}}, - "extra padding for truncated push": {{[]byte{ - byte(vm.PUSH14), 0x2e, 0x5a, 0x30, 0x10, 0x64, - }, 6, 7}}, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - for _, cur := range test { - pcMap := genPcMap(cur.evmCode) - sfvmPc := pcMap.evmToSfvm[cur.evmPc] - if want, got := cur.sfvmPc, sfvmPc; want != got { - t.Errorf("invalid conversion, wanted %d, got %d", want, got) - } - } - }) - } -} - -func TestConvertToSfvm_Code(t *testing.T) { - tests := map[string][]struct { - evmCode []byte - sfvmCode Code - }{ - "empty": {{}}, - "stop": {{[]byte{byte(vm.STOP)}, Code{Instruction{STOP, 0x0000}}}}, - "add": {{[]byte{ - byte(vm.PUSH1), 0x01, - byte(vm.PUSH1), 0x02, - byte(vm.ADD)}, - Code{Instruction{PUSH1, 0x0100}, - Instruction{PUSH1, 0x0200}, - Instruction{ADD, 0x0000}}}}, - "jump": {{[]byte{ - byte(vm.PUSH1), 0x04, - byte(vm.JUMP), - byte(vm.INVALID), - byte(vm.JUMPDEST)}, - Code{Instruction{PUSH1, 0x0400}, - Instruction{JUMP, 0x0000}, - Instruction{INVALID, 0x0000}, - Instruction{JUMP_TO, 0x0004}, - Instruction{JUMPDEST, 0x0000}}}}, - "jumpdest": {{[]byte{ - byte(vm.PUSH3), 0x00, 0x00, 0x06, - byte(vm.JUMP), - byte(vm.INVALID), - byte(vm.JUMPDEST)}, - Code{Instruction{PUSH3, 0x0000}, - Instruction{DATA, 0x0600}, - Instruction{JUMP, 0x0000}, - Instruction{INVALID, 0x0000}, - Instruction{JUMP_TO, 0x0006}, - Instruction{NOOP, 0x0000}, - Instruction{JUMPDEST, 0x0000}}}}, - "push2": {{[]byte{byte(vm.PUSH2), 0xBA, 0xAD}, Code{Instruction{PUSH2, 0xBAAD}}}}, - "push3": {{[]byte{byte(vm.PUSH3), 0xBA, 0xAD, 0xC0}, Code{Instruction{PUSH3, 0xBAAD}, Instruction{DATA, 0xC000}}}}, - "push31": {{[]byte{ - byte(vm.PUSH31), - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}, - Code{Instruction{PUSH31, 0x0102}, - Instruction{DATA, 0x0304}, - Instruction{DATA, 0x0506}, - Instruction{DATA, 0x0708}, - Instruction{DATA, 0x090A}, - Instruction{DATA, 0x0B0C}, - Instruction{DATA, 0x0D0E}, - Instruction{DATA, 0x0F10}, - Instruction{DATA, 0x1112}, - Instruction{DATA, 0x1314}, - Instruction{DATA, 0x1516}, - Instruction{DATA, 0x1718}, - Instruction{DATA, 0x191A}, - Instruction{DATA, 0x1B1C}, - Instruction{DATA, 0x1D1E}, - Instruction{DATA, 0x1F00}}}}, - "push32": {{[]byte{ - byte(vm.PUSH32), - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF}, - Code{Instruction{PUSH32, 0x0102}, - Instruction{DATA, 0x0304}, - Instruction{DATA, 0x0506}, - Instruction{DATA, 0x0708}, - Instruction{DATA, 0x090A}, - Instruction{DATA, 0x0B0C}, - Instruction{DATA, 0x0D0E}, - Instruction{DATA, 0x0F10}, - Instruction{DATA, 0x1112}, - Instruction{DATA, 0x1314}, - Instruction{DATA, 0x1516}, - Instruction{DATA, 0x1718}, - Instruction{DATA, 0x191A}, - Instruction{DATA, 0x1B1C}, - Instruction{DATA, 0x1D1E}, - Instruction{DATA, 0x1FFF}}}}, - "invalid": {{[]byte{byte(vm.INVALID)}, Code{Instruction{INVALID, 0x0000}}}}, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - for _, cur := range test { - got := convert(cur.evmCode, ConversionConfig{}) - - want := cur.sfvmCode - - if wantSize, gotSize := len(want), len(got); wantSize != gotSize { - t.Fatalf("unexpected code size, wanted %d, got %d", wantSize, gotSize) - } - - for i := 0; i < len(got); i++ { - if wantInst, gotInst := want[i], got[i]; wantInst != gotInst { - t.Errorf("unexpected instruction, wanted %v, got %v", wantInst, gotInst) - } - } - } - }) - } -} - func TestConvertToSfvm_Stack(t *testing.T) { newSfvmStack := func(values ...cc.U256) *stack { stack := NewStack() @@ -381,44 +209,6 @@ func TestConvertToSfvm_Stack(t *testing.T) { //////////////////////////////////////////////////////////// // sfvm -> ct -func TestConvertToCt_Pc(t *testing.T) { - tests := map[string][]struct { - evmCode []byte - sfvmPc uint16 - evmPc uint16 - }{ - "empty": {{}}, - "pos-0": {{[]byte{byte(vm.STOP)}, 0, 0}}, - "pos-1": {{[]byte{byte(vm.STOP), byte(vm.STOP), byte(vm.STOP)}, 1, 1}}, - "one-past-end": {{[]byte{byte(vm.STOP)}, 1, 1}}, - "shifted": {{[]byte{ - byte(vm.PUSH1), 0x01, - byte(vm.PUSH1), 0x02, - byte(vm.ADD)}, 1, 2}}, - "jumpdest": {{[]byte{ - byte(vm.PUSH3), 0x00, 0x00, 0x06, - byte(vm.JUMP), - byte(vm.INVALID), - byte(vm.JUMPDEST)}, - 6, 6}}, - "extra padding for truncated push": {{[]byte{ - byte(vm.PUSH14), 0x2e, 0x5a, 0x30, 0x10, 0x64, - }, 7, 6}}, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - for _, cur := range test { - pcMap := genPcMap(cur.evmCode) - evmPc := pcMap.sfvmToEvm[cur.sfvmPc] - if want, got := cur.evmPc, evmPc; want != got { - t.Errorf("invalid conversion, wanted %d, got %d", want, got) - } - } - }) - } -} - func TestConvertToCt_Stack(t *testing.T) { newSfvmStack := func(values ...cc.U256) *stack { stack := NewStack() diff --git a/go/interpreter/sfvm/example_code_test.go b/go/interpreter/sfvm/example_code_test.go deleted file mode 100644 index 53f9785f..00000000 --- a/go/interpreter/sfvm/example_code_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -// An example contract captured for testing and benchmarking. -var longExampleCode = []byte{ - 96, 4, 54, 16, 21, 97, 0, 13, 87, 97, 66, 157, 86, 91, 96, 0, 53, 96, 28, 82, 96, 0, 81, 52, 21, 97, 0, 33, 87, 96, 0, 128, 253, 91, 99, 49, 60, 229, 103, 129, 20, 21, 97, 0, 56, 87, 96, 18, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 169, 5, 156, 187, 129, 20, 21, 97, 0, 138, 87, 96, 4, 53, 96, 160, 28, 21, 97, 0, 84, 87, 96, 0, 128, 253, 91, 51, 97, 1, 64, 82, 96, 4, 53, 97, 1, 96, 82, 96, 36, 53, 97, 1, 128, 82, 97, 1, 128, 81, 97, 1, 96, 81, 97, 1, 64, 81, 96, 6, 88, 1, 97, 66, 163, 86, 91, 96, 0, 80, 96, 1, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 35, 184, 114, 221, 129, 20, 21, 97, 1, 115, 87, 96, 4, 53, 96, 160, 28, 21, 97, 0, 166, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 160, 28, 21, 97, 0, 182, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 1, 64, 82, 96, 36, 53, 97, 1, 96, 82, 96, 68, 53, 97, 1, 128, 82, 97, 1, 128, 81, 97, 1, 96, 81, 97, 1, 64, 81, 96, 6, 88, 1, 97, 66, 163, 86, 91, 96, 0, 80, 96, 22, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 84, 97, 1, 64, 82, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 1, 64, 81, 24, 21, 97, 1, 104, 87, 97, 1, 64, 81, 96, 68, 53, 128, 130, 16, 21, 97, 1, 67, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 22, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 85, 91, 96, 1, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 9, 94, 167, 179, 129, 20, 21, 97, 1, 236, 87, 96, 4, 53, 96, 160, 28, 21, 97, 1, 143, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 22, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 85, 96, 36, 53, 97, 1, 64, 82, 96, 4, 53, 51, 127, 140, 91, 225, 229, 235, 236, 125, 91, 209, 79, 113, 66, 125, 30, 132, 243, 221, 3, 20, 192, 247, 178, 41, 30, 91, 32, 10, 200, 199, 195, 185, 37, 96, 32, 97, 1, 64, 163, 96, 1, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 217, 108, 127, 206, 129, 20, 21, 97, 2, 33, 87, 96, 4, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 96, 64, 97, 1, 96, 243, 91, 99, 20, 240, 89, 121, 129, 20, 21, 97, 2, 86, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 96, 64, 97, 1, 96, 243, 91, 99, 15, 107, 168, 227, 129, 20, 21, 97, 3, 0, 87, 96, 64, 54, 97, 1, 64, 55, 97, 1, 128, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 68, 97, 1, 128, 81, 96, 2, 129, 16, 97, 2, 136, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 96, 4, 97, 1, 128, 81, 96, 2, 129, 16, 97, 2, 160, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 128, 130, 16, 21, 97, 2, 178, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 132, 53, 128, 128, 97, 2, 199, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 1, 64, 97, 1, 128, 81, 96, 2, 129, 16, 97, 2, 225, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 2, 117, 87, 91, 80, 80, 96, 64, 97, 1, 64, 243, 91, 99, 68, 105, 227, 14, 129, 20, 21, 97, 3, 53, 87, 96, 5, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 96, 64, 97, 1, 96, 243, 91, 99, 244, 70, 193, 208, 129, 20, 21, 97, 3, 100, 87, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 64, 82, 97, 1, 64, 81, 96, 100, 128, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 118, 162, 240, 240, 129, 20, 21, 97, 3, 138, 87, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 64, 82, 97, 1, 64, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 187, 123, 139, 128, 129, 20, 21, 97, 4, 255, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 64, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 96, 82, 80, 97, 1, 96, 81, 97, 1, 64, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 1, 192, 82, 97, 1, 224, 82, 97, 1, 192, 128, 81, 97, 2, 0, 82, 128, 96, 32, 1, 81, 97, 2, 32, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 64, 81, 97, 2, 96, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 0, 81, 97, 2, 128, 82, 97, 2, 32, 81, 97, 2, 160, 82, 97, 2, 96, 81, 97, 2, 192, 82, 97, 2, 192, 81, 97, 2, 160, 81, 97, 2, 128, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 3, 32, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 32, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 4, 219, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 23, 84, 128, 128, 97, 4, 240, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 94, 13, 68, 63, 129, 20, 21, 97, 5, 28, 87, 96, 0, 97, 1, 64, 82, 96, 0, 97, 1, 96, 82, 97, 5, 61, 86, 91, 99, 126, 66, 252, 12, 129, 20, 21, 97, 5, 56, 87, 96, 64, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 5, 61, 86, 91, 97, 6, 45, 86, 91, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 5, 77, 87, 25, 91, 96, 127, 28, 21, 97, 5, 90, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 5, 109, 87, 25, 91, 96, 127, 28, 21, 97, 5, 122, 87, 96, 0, 128, 253, 91, 144, 80, 80, 97, 1, 64, 81, 97, 1, 128, 82, 97, 1, 96, 81, 97, 1, 160, 82, 97, 1, 64, 81, 21, 21, 97, 5, 186, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 96, 4, 53, 97, 1, 192, 82, 96, 36, 53, 97, 1, 224, 82, 96, 68, 53, 97, 2, 0, 82, 97, 1, 128, 81, 97, 2, 32, 82, 97, 1, 160, 81, 97, 2, 64, 82, 97, 2, 64, 81, 97, 2, 32, 81, 97, 2, 0, 81, 97, 1, 224, 81, 97, 1, 192, 81, 96, 6, 88, 1, 97, 79, 140, 86, 91, 97, 2, 160, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 160, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 7, 33, 30, 247, 129, 20, 21, 97, 6, 74, 87, 96, 0, 97, 1, 64, 82, 96, 0, 97, 1, 96, 82, 97, 6, 107, 86, 91, 99, 227, 111, 213, 1, 129, 20, 21, 97, 6, 102, 87, 96, 64, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 6, 107, 86, 91, 97, 12, 244, 86, 91, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 6, 123, 87, 25, 91, 96, 127, 28, 21, 97, 6, 136, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 6, 155, 87, 25, 91, 96, 127, 28, 21, 97, 6, 168, 87, 96, 0, 128, 253, 91, 144, 80, 80, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 128, 82, 96, 32, 97, 2, 32, 96, 4, 99, 187, 123, 139, 128, 97, 1, 192, 82, 97, 1, 220, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 6, 240, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 6, 253, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 32, 81, 97, 1, 160, 82, 97, 1, 64, 81, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 64, 81, 21, 21, 97, 7, 69, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 192, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 224, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 1, 192, 81, 97, 2, 0, 82, 97, 1, 224, 81, 97, 2, 32, 82, 97, 2, 32, 81, 97, 2, 0, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 128, 82, 97, 2, 160, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 128, 128, 81, 97, 1, 192, 82, 128, 96, 32, 1, 81, 97, 1, 224, 82, 80, 96, 4, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 7, 198, 87, 25, 91, 96, 127, 28, 21, 97, 7, 211, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 0, 82, 96, 36, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 7, 242, 87, 25, 91, 96, 127, 28, 21, 97, 7, 255, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 32, 82, 96, 1, 97, 2, 64, 82, 96, 1, 97, 2, 96, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 8, 41, 87, 96, 4, 53, 97, 2, 64, 82, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 8, 61, 87, 96, 36, 53, 97, 2, 96, 82, 91, 96, 0, 97, 2, 128, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 8, 204, 87, 97, 1, 192, 96, 4, 53, 96, 2, 129, 16, 97, 8, 98, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 68, 53, 97, 1, 128, 96, 4, 53, 96, 2, 129, 16, 97, 8, 125, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 8, 150, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 8, 188, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 128, 82, 97, 10, 168, 86, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 10, 65, 87, 96, 64, 54, 97, 2, 160, 55, 96, 68, 53, 97, 2, 160, 97, 2, 0, 81, 96, 2, 129, 16, 97, 8, 246, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 96, 32, 97, 3, 160, 96, 100, 99, 237, 142, 132, 243, 97, 2, 224, 82, 97, 2, 160, 81, 97, 3, 0, 82, 97, 2, 192, 81, 97, 3, 32, 82, 96, 1, 97, 3, 64, 82, 97, 2, 252, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 9, 68, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 9, 81, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 160, 81, 97, 1, 160, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 9, 112, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 128, 82, 97, 2, 128, 128, 81, 97, 2, 128, 81, 96, 32, 97, 3, 64, 96, 4, 99, 221, 202, 63, 67, 97, 2, 224, 82, 97, 2, 252, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 9, 199, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 9, 212, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 64, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 9, 239, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 4, 168, 23, 200, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 10, 16, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 97, 2, 128, 128, 81, 97, 1, 224, 81, 129, 129, 131, 1, 16, 21, 97, 10, 50, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 10, 168, 86, 91, 96, 32, 97, 3, 96, 96, 100, 99, 94, 13, 68, 63, 97, 2, 160, 82, 97, 2, 0, 81, 97, 2, 192, 82, 97, 2, 32, 81, 97, 2, 224, 82, 96, 68, 53, 97, 3, 0, 82, 97, 2, 188, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 10, 139, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 10, 152, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 96, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 2, 64, 81, 97, 2, 192, 82, 97, 2, 96, 81, 97, 2, 224, 82, 97, 2, 128, 81, 97, 3, 0, 82, 97, 1, 192, 81, 97, 3, 32, 82, 97, 1, 224, 81, 97, 3, 64, 82, 97, 3, 64, 81, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 3, 160, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 160, 81, 97, 2, 160, 82, 97, 1, 192, 97, 2, 96, 81, 96, 2, 129, 16, 97, 11, 109, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 81, 128, 130, 16, 21, 97, 11, 131, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 11, 153, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 192, 82, 96, 2, 84, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 11, 191, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 97, 2, 192, 81, 97, 2, 224, 81, 128, 130, 16, 21, 97, 11, 236, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 12, 16, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 128, 97, 2, 96, 81, 96, 2, 129, 16, 97, 12, 43, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 12, 59, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 96, 0, 97, 2, 32, 81, 18, 21, 21, 97, 12, 231, 87, 96, 32, 97, 3, 160, 96, 68, 99, 204, 43, 39, 215, 97, 3, 0, 82, 97, 2, 192, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 12, 131, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 160, 81, 128, 128, 97, 12, 153, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 32, 82, 97, 2, 32, 81, 97, 3, 64, 82, 97, 3, 28, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 12, 206, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 12, 219, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 160, 81, 97, 2, 192, 82, 91, 97, 2, 192, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 237, 142, 132, 243, 129, 20, 21, 97, 13, 11, 87, 96, 0, 97, 1, 64, 82, 97, 13, 60, 86, 91, 99, 228, 126, 107, 158, 129, 20, 21, 97, 13, 55, 87, 96, 100, 53, 96, 1, 28, 21, 97, 13, 39, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 13, 60, 86, 91, 97, 15, 221, 86, 91, 96, 68, 53, 96, 1, 28, 21, 97, 13, 76, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 97, 1, 96, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 128, 81, 97, 1, 96, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 97, 1, 64, 81, 21, 97, 13, 191, 87, 96, 4, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 128, 81, 97, 1, 224, 82, 97, 1, 160, 81, 97, 2, 0, 82, 97, 1, 96, 81, 97, 2, 32, 82, 97, 2, 32, 81, 97, 2, 0, 81, 97, 1, 224, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 128, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 128, 81, 97, 1, 192, 82, 97, 1, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 68, 53, 21, 97, 14, 132, 87, 97, 1, 128, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 72, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 96, 4, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 97, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 129, 129, 131, 1, 16, 21, 97, 14, 117, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 14, 206, 86, 91, 97, 1, 128, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 152, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 96, 4, 97, 1, 224, 81, 96, 2, 129, 16, 97, 14, 177, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 128, 130, 16, 21, 97, 14, 195, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 14, 44, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 1, 128, 81, 97, 2, 0, 82, 97, 1, 160, 81, 97, 2, 32, 82, 97, 1, 96, 81, 97, 2, 64, 82, 97, 2, 64, 81, 97, 2, 32, 81, 97, 2, 0, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 160, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 160, 81, 97, 1, 224, 82, 96, 0, 97, 2, 0, 82, 96, 68, 53, 21, 97, 15, 124, 87, 97, 1, 224, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 15, 108, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 0, 82, 97, 15, 157, 86, 91, 97, 1, 192, 81, 97, 1, 224, 81, 128, 130, 16, 21, 97, 15, 145, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 0, 82, 91, 97, 2, 0, 81, 96, 23, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 15, 184, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 192, 81, 128, 128, 97, 15, 206, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 11, 76, 126, 77, 129, 20, 21, 97, 15, 243, 87, 51, 97, 1, 64, 82, 97, 16, 36, 86, 91, 99, 12, 62, 75, 84, 129, 20, 21, 97, 16, 31, 87, 96, 100, 53, 96, 160, 28, 21, 97, 16, 15, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 16, 36, 86, 91, 97, 23, 175, 86, 91, 96, 24, 84, 21, 97, 16, 49, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 17, 84, 21, 97, 16, 67, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 97, 1, 64, 81, 97, 1, 96, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 128, 81, 97, 1, 96, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 128, 81, 97, 1, 224, 82, 97, 1, 160, 81, 97, 2, 0, 82, 97, 1, 96, 81, 97, 2, 32, 82, 97, 2, 32, 81, 97, 2, 0, 81, 97, 1, 224, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 128, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 128, 81, 97, 1, 192, 82, 96, 23, 84, 97, 1, 224, 82, 97, 1, 128, 81, 97, 2, 0, 82, 97, 1, 160, 81, 97, 2, 32, 82, 97, 2, 64, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 224, 81, 21, 21, 97, 17, 80, 87, 96, 0, 96, 4, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 65, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 17, 97, 17, 80, 87, 96, 0, 128, 253, 91, 97, 1, 128, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 100, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 4, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 124, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 129, 129, 131, 1, 16, 21, 97, 17, 144, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 0, 97, 2, 64, 81, 96, 2, 129, 16, 97, 17, 171, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 17, 34, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 32, 81, 97, 2, 128, 82, 97, 1, 96, 81, 97, 2, 160, 82, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 96, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 3, 0, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 0, 81, 97, 2, 64, 82, 97, 1, 192, 81, 97, 2, 64, 81, 17, 97, 18, 86, 87, 96, 0, 128, 253, 91, 96, 64, 54, 97, 2, 96, 55, 97, 2, 64, 81, 97, 2, 160, 82, 96, 0, 97, 2, 192, 82, 96, 0, 97, 1, 224, 81, 17, 21, 97, 21, 189, 87, 96, 2, 84, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 18, 144, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 4, 128, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 96, 3, 84, 97, 3, 0, 82, 97, 3, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 2, 64, 81, 97, 1, 128, 97, 3, 32, 81, 96, 2, 129, 16, 97, 18, 207, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 18, 232, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 192, 81, 128, 128, 97, 18, 254, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 64, 82, 96, 0, 97, 3, 96, 82, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 34, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 64, 81, 17, 21, 97, 19, 107, 87, 97, 3, 64, 81, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 73, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 19, 91, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 96, 82, 97, 19, 161, 86, 91, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 127, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 64, 81, 128, 130, 16, 21, 97, 19, 149, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 96, 82, 91, 97, 2, 224, 81, 97, 3, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 19, 189, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 96, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 229, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 19, 254, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 23, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 20, 52, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 20, 85, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 109, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 2, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 141, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 97, 2, 96, 97, 3, 32, 81, 96, 2, 129, 16, 97, 20, 167, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 20, 185, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 18, 183, 87, 91, 80, 80, 97, 1, 64, 97, 3, 32, 82, 91, 97, 3, 32, 81, 81, 96, 32, 97, 3, 32, 81, 1, 97, 3, 32, 82, 97, 3, 32, 97, 3, 32, 81, 16, 21, 97, 21, 0, 87, 97, 20, 222, 86, 91, 97, 2, 0, 81, 97, 3, 64, 82, 97, 2, 32, 81, 97, 3, 96, 82, 97, 1, 96, 81, 97, 3, 128, 82, 97, 3, 128, 81, 97, 3, 96, 81, 97, 3, 64, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 3, 224, 82, 97, 3, 0, 97, 3, 32, 82, 91, 97, 3, 32, 81, 82, 96, 32, 97, 3, 32, 81, 3, 97, 3, 32, 82, 97, 1, 64, 97, 3, 32, 81, 16, 21, 21, 97, 21, 92, 87, 97, 21, 57, 86, 91, 97, 3, 224, 81, 97, 2, 160, 82, 97, 1, 224, 81, 97, 2, 160, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 21, 125, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 21, 152, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 192, 81, 128, 128, 97, 21, 174, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 97, 21, 224, 86, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 97, 2, 0, 81, 129, 85, 97, 2, 32, 81, 96, 1, 130, 1, 85, 80, 97, 2, 64, 81, 97, 2, 192, 82, 91, 96, 68, 53, 97, 2, 192, 81, 16, 21, 97, 21, 242, 87, 96, 0, 128, 253, 91, 97, 2, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 0, 96, 4, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 19, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 17, 21, 97, 22, 167, 87, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 47, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 22, 69, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 35, 184, 114, 221, 97, 3, 0, 82, 51, 97, 3, 32, 82, 48, 97, 3, 64, 82, 96, 4, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 113, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 97, 3, 96, 82, 97, 3, 28, 96, 0, 97, 2, 224, 81, 96, 2, 129, 16, 97, 22, 144, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 22, 167, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 21, 254, 87, 91, 80, 80, 97, 1, 224, 128, 81, 97, 2, 192, 81, 129, 129, 131, 1, 16, 21, 97, 22, 209, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 96, 21, 97, 1, 64, 81, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 2, 192, 81, 129, 129, 131, 1, 16, 21, 97, 23, 1, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 85, 80, 97, 1, 224, 81, 96, 23, 85, 97, 2, 192, 81, 97, 2, 224, 82, 97, 1, 64, 81, 96, 0, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 2, 224, 163, 96, 4, 53, 97, 2, 224, 82, 96, 36, 53, 97, 3, 0, 82, 97, 2, 96, 81, 97, 3, 32, 82, 97, 2, 128, 81, 97, 3, 64, 82, 97, 2, 64, 81, 97, 3, 96, 82, 97, 1, 224, 81, 97, 3, 128, 82, 51, 127, 38, 245, 90, 133, 8, 29, 36, 151, 78, 133, 198, 192, 0, 69, 208, 240, 69, 57, 145, 233, 88, 115, 245, 43, 255, 13, 33, 175, 64, 121, 167, 104, 96, 192, 97, 2, 224, 162, 97, 2, 192, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 61, 240, 33, 36, 129, 20, 21, 97, 23, 197, 87, 51, 97, 1, 64, 82, 97, 23, 246, 86, 91, 99, 221, 193, 245, 157, 129, 20, 21, 97, 23, 241, 87, 96, 132, 53, 96, 160, 28, 21, 97, 23, 225, 87, 96, 0, 128, 253, 91, 96, 32, 96, 132, 97, 1, 64, 55, 96, 0, 80, 97, 23, 246, 86, 91, 97, 29, 218, 86, 91, 96, 24, 84, 21, 97, 24, 3, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 24, 24, 87, 25, 91, 96, 127, 28, 21, 97, 24, 37, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 24, 56, 87, 25, 91, 96, 127, 28, 21, 97, 24, 69, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 17, 84, 21, 97, 24, 85, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 96, 82, 96, 32, 97, 2, 0, 96, 4, 99, 187, 123, 139, 128, 97, 1, 160, 82, 97, 1, 188, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 24, 174, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 24, 187, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 0, 81, 97, 1, 128, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 160, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 192, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 1, 160, 81, 97, 2, 32, 82, 97, 1, 192, 81, 97, 2, 64, 82, 97, 2, 64, 81, 97, 2, 32, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 160, 82, 97, 2, 192, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 160, 128, 81, 97, 1, 224, 82, 128, 96, 32, 1, 81, 97, 2, 0, 82, 80, 97, 1, 224, 96, 4, 53, 96, 2, 129, 16, 97, 25, 111, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 68, 53, 97, 1, 96, 96, 4, 53, 96, 2, 129, 16, 97, 25, 138, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 25, 163, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 25, 201, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 32, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 96, 4, 53, 97, 2, 96, 82, 96, 36, 53, 97, 2, 128, 82, 97, 2, 32, 81, 97, 2, 160, 82, 97, 1, 224, 81, 97, 2, 192, 82, 97, 2, 0, 81, 97, 2, 224, 82, 97, 2, 224, 81, 97, 2, 192, 81, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 96, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 3, 64, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 64, 81, 97, 2, 64, 82, 97, 1, 224, 96, 36, 53, 96, 2, 129, 16, 97, 26, 126, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 64, 81, 128, 130, 16, 21, 97, 26, 148, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 26, 170, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 96, 82, 97, 2, 96, 81, 96, 2, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 26, 208, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 128, 82, 97, 2, 96, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 26, 253, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 27, 33, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 96, 36, 53, 96, 2, 129, 16, 97, 27, 59, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 27, 75, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 96, 100, 53, 97, 2, 96, 81, 16, 21, 97, 27, 103, 87, 96, 0, 128, 253, 91, 97, 2, 128, 81, 96, 3, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 27, 130, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 160, 82, 97, 2, 160, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 27, 187, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 96, 36, 53, 96, 2, 129, 16, 97, 27, 213, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 27, 229, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 160, 82, 97, 1, 160, 96, 4, 53, 96, 2, 129, 16, 97, 28, 2, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 96, 68, 53, 129, 129, 131, 1, 16, 21, 97, 28, 25, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 4, 53, 96, 2, 129, 16, 97, 28, 48, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 1, 160, 96, 36, 53, 96, 2, 129, 16, 97, 28, 79, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 81, 128, 130, 16, 21, 97, 28, 101, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 160, 81, 128, 130, 16, 21, 97, 28, 125, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 36, 53, 96, 2, 129, 16, 97, 28, 148, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 96, 4, 53, 96, 2, 129, 16, 97, 28, 176, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 28, 198, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 35, 184, 114, 221, 97, 2, 192, 82, 51, 97, 2, 224, 82, 48, 97, 3, 0, 82, 96, 68, 53, 97, 3, 32, 82, 97, 2, 220, 96, 0, 96, 4, 53, 96, 2, 129, 16, 97, 28, 251, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 29, 18, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 2, 129, 16, 97, 29, 34, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 29, 56, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 2, 192, 82, 97, 1, 64, 81, 97, 2, 224, 82, 97, 2, 96, 81, 97, 3, 0, 82, 97, 2, 220, 96, 0, 96, 36, 53, 96, 2, 129, 16, 97, 29, 108, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 29, 131, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 2, 192, 82, 96, 68, 53, 97, 2, 224, 82, 96, 36, 53, 97, 3, 0, 82, 97, 2, 96, 81, 97, 3, 32, 82, 51, 127, 139, 62, 150, 242, 184, 137, 250, 119, 28, 83, 201, 129, 180, 13, 175, 0, 95, 99, 246, 55, 241, 134, 159, 112, 112, 82, 209, 90, 61, 217, 113, 64, 96, 128, 97, 2, 192, 162, 97, 2, 96, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 166, 65, 126, 214, 129, 20, 21, 97, 29, 240, 87, 51, 97, 1, 64, 82, 97, 30, 33, 86, 91, 99, 68, 238, 25, 134, 129, 20, 21, 97, 30, 28, 87, 96, 132, 53, 96, 160, 28, 21, 97, 30, 12, 87, 96, 0, 128, 253, 91, 96, 32, 96, 132, 97, 1, 64, 55, 96, 0, 80, 97, 30, 33, 86, 91, 97, 41, 39, 86, 91, 96, 24, 84, 21, 97, 30, 46, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 4, 53, 128, 128, 96, 0, 129, 18, 21, 97, 30, 67, 87, 25, 91, 96, 127, 28, 21, 97, 30, 80, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 30, 99, 87, 25, 91, 96, 127, 28, 21, 97, 30, 112, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 17, 84, 21, 97, 30, 128, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 96, 68, 53, 97, 1, 96, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 128, 82, 96, 32, 97, 2, 32, 96, 4, 99, 187, 123, 139, 128, 97, 1, 192, 82, 97, 1, 220, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 30, 224, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 30, 237, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 32, 81, 97, 1, 160, 82, 115, 141, 17, 236, 56, 163, 235, 94, 149, 107, 5, 47, 103, 218, 139, 220, 155, 239, 138, 191, 62, 97, 1, 192, 82, 115, 4, 6, 141, 166, 200, 58, 252, 250, 14, 19, 186, 21, 166, 105, 102, 98, 51, 93, 91, 117, 97, 1, 224, 82, 96, 4, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 31, 63, 87, 25, 91, 96, 127, 28, 21, 97, 31, 76, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 0, 82, 96, 36, 53, 96, 1, 128, 130, 3, 128, 128, 96, 0, 129, 18, 21, 97, 31, 107, 87, 25, 91, 96, 127, 28, 21, 97, 31, 120, 87, 96, 0, 128, 253, 91, 144, 80, 144, 80, 144, 80, 97, 2, 32, 82, 96, 1, 97, 2, 64, 82, 96, 1, 97, 2, 96, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 31, 162, 87, 96, 4, 53, 97, 2, 64, 82, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 31, 182, 87, 96, 36, 53, 97, 2, 96, 82, 91, 96, 96, 54, 97, 2, 128, 55, 96, 0, 97, 2, 0, 81, 18, 21, 97, 31, 238, 87, 96, 4, 53, 96, 2, 129, 16, 97, 31, 217, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 2, 160, 82, 97, 32, 12, 86, 91, 97, 1, 192, 97, 2, 0, 81, 96, 2, 129, 16, 97, 32, 2, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 82, 91, 96, 0, 97, 2, 32, 81, 18, 21, 97, 32, 61, 87, 96, 36, 53, 96, 2, 129, 16, 97, 32, 40, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 2, 192, 82, 97, 32, 91, 86, 91, 97, 1, 192, 97, 2, 32, 81, 96, 2, 129, 16, 97, 32, 81, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 192, 82, 91, 97, 2, 160, 81, 59, 97, 32, 105, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 35, 184, 114, 221, 97, 2, 224, 82, 51, 97, 3, 0, 82, 48, 97, 3, 32, 82, 97, 1, 96, 81, 97, 3, 64, 82, 97, 2, 252, 96, 0, 97, 2, 160, 81, 90, 241, 97, 32, 158, 87, 96, 0, 128, 253, 91, 96, 0, 97, 2, 0, 81, 18, 21, 97, 32, 177, 87, 96, 1, 97, 32, 185, 86, 91, 96, 0, 97, 2, 32, 81, 18, 91, 21, 97, 39, 133, 87, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 2, 224, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 3, 0, 82, 80, 97, 1, 64, 97, 3, 96, 82, 91, 97, 3, 96, 81, 81, 96, 32, 97, 3, 96, 81, 1, 97, 3, 96, 82, 97, 3, 96, 97, 3, 96, 81, 16, 21, 97, 33, 10, 87, 97, 32, 232, 86, 91, 97, 2, 224, 81, 97, 3, 128, 82, 97, 3, 0, 81, 97, 3, 160, 82, 97, 3, 160, 81, 97, 3, 128, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 4, 0, 82, 97, 4, 32, 82, 97, 3, 64, 97, 3, 96, 82, 91, 97, 3, 96, 81, 82, 96, 32, 97, 3, 96, 81, 3, 97, 3, 96, 82, 97, 1, 64, 97, 3, 96, 81, 16, 21, 21, 97, 33, 94, 87, 97, 33, 59, 86, 91, 97, 4, 0, 128, 81, 97, 3, 32, 82, 128, 96, 32, 1, 81, 97, 3, 64, 82, 80, 96, 0, 97, 3, 96, 82, 96, 0, 97, 2, 0, 81, 18, 21, 97, 34, 1, 87, 97, 3, 32, 96, 4, 53, 96, 2, 129, 16, 97, 33, 150, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 96, 81, 97, 1, 128, 96, 4, 53, 96, 2, 129, 16, 97, 33, 178, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 33, 203, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 33, 241, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 3, 96, 82, 97, 35, 144, 86, 91, 96, 64, 54, 97, 3, 128, 55, 97, 1, 96, 81, 97, 3, 128, 97, 2, 0, 81, 96, 2, 129, 16, 97, 34, 32, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 96, 1, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 3, 192, 82, 96, 32, 97, 4, 96, 96, 36, 99, 112, 160, 130, 49, 97, 3, 224, 82, 48, 97, 4, 0, 82, 97, 3, 252, 97, 3, 192, 81, 90, 250, 97, 34, 94, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 34, 107, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 96, 81, 97, 3, 96, 82, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 59, 97, 34, 149, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 11, 76, 126, 77, 97, 3, 224, 82, 97, 3, 128, 81, 97, 4, 0, 82, 97, 3, 160, 81, 97, 4, 32, 82, 96, 0, 97, 4, 64, 82, 97, 3, 252, 96, 0, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 241, 97, 34, 223, 87, 96, 0, 128, 253, 91, 96, 32, 97, 4, 96, 96, 36, 99, 112, 160, 130, 49, 97, 3, 224, 82, 48, 97, 4, 0, 82, 97, 3, 252, 97, 3, 192, 81, 90, 250, 97, 35, 6, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 35, 19, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 96, 81, 97, 3, 96, 81, 128, 130, 16, 21, 97, 35, 43, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 96, 82, 97, 1, 96, 81, 97, 1, 160, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 35, 82, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 97, 3, 96, 82, 97, 3, 96, 128, 81, 97, 3, 64, 81, 129, 129, 131, 1, 16, 21, 97, 35, 133, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 91, 97, 1, 64, 97, 3, 160, 82, 91, 97, 3, 160, 81, 81, 96, 32, 97, 3, 160, 81, 1, 97, 3, 160, 82, 97, 3, 160, 97, 3, 160, 81, 16, 21, 97, 35, 186, 87, 97, 35, 152, 86, 91, 97, 2, 64, 81, 97, 3, 192, 82, 97, 2, 96, 81, 97, 3, 224, 82, 97, 3, 96, 81, 97, 4, 0, 82, 97, 3, 32, 81, 97, 4, 32, 82, 97, 3, 64, 81, 97, 4, 64, 82, 97, 4, 64, 81, 97, 4, 32, 81, 97, 4, 0, 81, 97, 3, 224, 81, 97, 3, 192, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 4, 160, 82, 97, 3, 128, 97, 3, 160, 82, 91, 97, 3, 160, 81, 82, 96, 32, 97, 3, 160, 81, 3, 97, 3, 160, 82, 97, 1, 64, 97, 3, 160, 81, 16, 21, 21, 97, 36, 46, 87, 97, 36, 11, 86, 91, 97, 4, 160, 81, 97, 3, 128, 82, 97, 3, 32, 97, 2, 96, 81, 96, 2, 129, 16, 97, 36, 74, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 3, 128, 81, 128, 130, 16, 21, 97, 36, 96, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 36, 118, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 97, 2, 128, 81, 96, 2, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 36, 156, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 3, 160, 82, 97, 2, 128, 81, 97, 3, 160, 81, 128, 130, 16, 21, 97, 36, 201, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 36, 237, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 128, 97, 2, 96, 81, 96, 2, 129, 16, 97, 37, 8, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 37, 24, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 128, 82, 97, 3, 160, 81, 96, 3, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 37, 61, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 3, 192, 82, 97, 3, 192, 81, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 37, 118, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 128, 97, 2, 96, 81, 96, 2, 129, 16, 97, 37, 145, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 37, 161, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 192, 82, 97, 2, 224, 97, 2, 64, 81, 96, 2, 129, 16, 97, 37, 191, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 96, 81, 129, 129, 131, 1, 16, 21, 97, 37, 215, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 64, 81, 96, 2, 129, 16, 97, 37, 239, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 2, 224, 97, 2, 96, 81, 96, 2, 129, 16, 97, 38, 15, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 38, 37, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 192, 81, 128, 130, 16, 21, 97, 38, 61, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 96, 81, 96, 2, 129, 16, 97, 38, 85, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 96, 0, 97, 2, 32, 81, 18, 21, 21, 97, 39, 110, 87, 96, 32, 97, 4, 128, 96, 36, 99, 112, 160, 130, 49, 97, 4, 0, 82, 48, 97, 4, 32, 82, 97, 4, 28, 97, 2, 192, 81, 90, 250, 97, 38, 149, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 38, 162, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 128, 81, 97, 3, 224, 82, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 59, 97, 38, 204, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 100, 99, 26, 77, 1, 210, 97, 4, 0, 82, 97, 2, 128, 81, 97, 4, 32, 82, 97, 2, 32, 81, 97, 4, 64, 82, 96, 0, 97, 4, 96, 82, 97, 4, 28, 96, 0, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 241, 97, 39, 22, 87, 96, 0, 128, 253, 91, 96, 32, 97, 4, 128, 96, 36, 99, 112, 160, 130, 49, 97, 4, 0, 82, 48, 97, 4, 32, 82, 97, 4, 28, 97, 2, 192, 81, 90, 250, 97, 39, 61, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 39, 74, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 4, 128, 81, 97, 3, 224, 81, 128, 130, 16, 21, 97, 39, 98, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 91, 96, 100, 53, 97, 2, 128, 81, 16, 21, 97, 39, 128, 87, 96, 0, 128, 253, 91, 97, 40, 142, 86, 91, 96, 32, 97, 3, 96, 96, 36, 99, 112, 160, 130, 49, 97, 2, 224, 82, 48, 97, 3, 0, 82, 97, 2, 252, 97, 2, 192, 81, 90, 250, 97, 39, 172, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 39, 185, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 96, 81, 97, 2, 128, 82, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 59, 97, 39, 227, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 132, 99, 61, 240, 33, 36, 97, 2, 224, 82, 97, 2, 0, 81, 97, 3, 0, 82, 97, 2, 32, 81, 97, 3, 32, 82, 97, 1, 96, 81, 97, 3, 64, 82, 96, 100, 53, 97, 3, 96, 82, 97, 2, 252, 96, 0, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 241, 97, 40, 54, 87, 96, 0, 128, 253, 91, 96, 32, 97, 3, 96, 96, 36, 99, 112, 160, 130, 49, 97, 2, 224, 82, 48, 97, 3, 0, 82, 97, 2, 252, 97, 2, 192, 81, 90, 250, 97, 40, 93, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 40, 106, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 96, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 40, 130, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 91, 97, 2, 192, 81, 59, 97, 40, 156, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 2, 224, 82, 97, 1, 64, 81, 97, 3, 0, 82, 97, 2, 128, 81, 97, 3, 32, 82, 97, 2, 252, 96, 0, 97, 2, 192, 81, 90, 241, 97, 40, 207, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 2, 224, 82, 97, 1, 96, 81, 97, 3, 0, 82, 96, 36, 53, 97, 3, 32, 82, 97, 2, 128, 81, 97, 3, 64, 82, 51, 127, 208, 19, 202, 35, 231, 122, 101, 0, 60, 44, 101, 156, 84, 66, 192, 12, 128, 83, 113, 183, 252, 30, 189, 76, 32, 108, 65, 209, 83, 107, 217, 11, 96, 128, 97, 2, 224, 162, 97, 2, 128, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 91, 54, 56, 156, 129, 20, 21, 97, 41, 61, 87, 51, 97, 1, 64, 82, 97, 41, 110, 86, 91, 99, 62, 177, 113, 159, 129, 20, 21, 97, 41, 105, 87, 96, 100, 53, 96, 160, 28, 21, 97, 41, 89, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 41, 110, 86, 91, 97, 43, 247, 86, 91, 96, 24, 84, 21, 97, 41, 123, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 96, 23, 84, 97, 1, 96, 82, 96, 64, 54, 97, 1, 128, 55, 97, 1, 192, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 192, 81, 96, 2, 129, 16, 97, 41, 191, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 224, 82, 97, 1, 224, 81, 96, 4, 53, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 41, 234, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 128, 128, 97, 42, 0, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 0, 82, 96, 36, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 29, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 97, 2, 0, 81, 16, 21, 97, 42, 49, 87, 96, 0, 128, 253, 91, 97, 1, 224, 81, 97, 2, 0, 81, 128, 130, 16, 21, 97, 42, 70, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 94, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 2, 0, 81, 97, 1, 128, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 130, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 152, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 42, 174, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 2, 32, 82, 97, 1, 64, 81, 97, 2, 64, 82, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 60, 96, 0, 97, 1, 192, 81, 96, 2, 129, 16, 97, 42, 227, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 42, 250, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 41, 174, 87, 91, 80, 80, 97, 1, 96, 128, 81, 96, 4, 53, 128, 130, 16, 21, 97, 43, 33, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 96, 21, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 96, 4, 53, 128, 130, 16, 21, 97, 43, 75, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 97, 1, 96, 81, 96, 23, 85, 96, 4, 53, 97, 1, 192, 82, 96, 0, 51, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 1, 192, 163, 97, 1, 128, 81, 97, 1, 192, 82, 97, 1, 160, 81, 97, 1, 224, 82, 96, 64, 54, 97, 2, 0, 55, 97, 1, 96, 81, 96, 4, 53, 128, 130, 16, 21, 97, 43, 184, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 64, 82, 51, 127, 124, 54, 56, 84, 204, 247, 150, 35, 65, 31, 137, 149, 179, 98, 188, 229, 237, 223, 241, 140, 146, 126, 220, 111, 93, 187, 181, 224, 88, 25, 168, 44, 96, 160, 97, 1, 192, 162, 96, 0, 96, 24, 85, 96, 64, 97, 1, 128, 243, 91, 99, 227, 16, 50, 115, 129, 20, 21, 97, 44, 13, 87, 51, 97, 1, 64, 82, 97, 44, 62, 86, 91, 99, 82, 210, 207, 221, 129, 20, 21, 97, 44, 57, 87, 96, 100, 53, 96, 160, 28, 21, 97, 44, 41, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 44, 62, 86, 91, 97, 51, 103, 86, 91, 96, 24, 84, 21, 97, 44, 75, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 17, 84, 21, 97, 44, 93, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 97, 1, 64, 81, 97, 1, 96, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 128, 81, 97, 1, 96, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 128, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 80, 97, 1, 128, 81, 97, 1, 192, 82, 97, 1, 160, 81, 97, 1, 224, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 1, 128, 81, 97, 2, 32, 82, 97, 1, 160, 81, 97, 2, 64, 82, 97, 1, 96, 81, 97, 2, 96, 82, 97, 2, 96, 81, 97, 2, 64, 81, 97, 2, 32, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 192, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 192, 81, 97, 2, 0, 82, 97, 2, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 192, 97, 2, 32, 81, 96, 2, 129, 16, 97, 45, 89, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 96, 4, 97, 2, 32, 81, 96, 2, 129, 16, 97, 45, 114, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 128, 130, 16, 21, 97, 45, 132, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 45, 69, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 1, 192, 81, 97, 2, 64, 82, 97, 1, 224, 81, 97, 2, 96, 82, 97, 1, 96, 81, 97, 2, 128, 82, 97, 2, 128, 81, 97, 2, 96, 81, 97, 2, 64, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 2, 224, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 224, 81, 97, 2, 32, 82, 96, 2, 84, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 46, 51, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 4, 128, 130, 4, 144, 80, 144, 80, 97, 2, 64, 82, 96, 3, 84, 97, 2, 96, 82, 96, 64, 54, 97, 2, 128, 55, 97, 2, 192, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 2, 32, 81, 97, 1, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 46, 121, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 46, 146, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 128, 128, 97, 46, 168, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 96, 0, 97, 3, 0, 82, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 46, 204, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 224, 81, 17, 21, 97, 47, 21, 87, 97, 2, 224, 81, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 46, 243, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 47, 5, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 0, 82, 97, 47, 75, 86, 91, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 41, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 224, 81, 128, 130, 16, 21, 97, 47, 63, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 0, 82, 91, 97, 2, 64, 81, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 47, 103, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 143, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 168, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 47, 193, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 47, 222, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 47, 255, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 192, 81, 96, 2, 129, 16, 97, 48, 23, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 97, 1, 192, 97, 2, 192, 81, 96, 2, 129, 16, 97, 48, 55, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 97, 2, 128, 97, 2, 192, 81, 96, 2, 129, 16, 97, 48, 81, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 16, 21, 97, 48, 99, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 46, 97, 87, 91, 80, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 2, 192, 81, 97, 1, 192, 81, 97, 2, 224, 82, 97, 1, 224, 81, 97, 3, 0, 82, 97, 1, 96, 81, 97, 3, 32, 82, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 96, 6, 88, 1, 97, 74, 11, 86, 91, 97, 3, 128, 82, 97, 2, 192, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 128, 81, 97, 2, 192, 82, 96, 23, 84, 97, 2, 224, 82, 97, 2, 0, 81, 97, 2, 192, 81, 128, 130, 16, 21, 97, 49, 61, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 224, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 49, 92, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 128, 128, 97, 49, 114, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 1, 129, 129, 131, 1, 16, 21, 97, 49, 137, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 3, 0, 82, 96, 1, 97, 3, 0, 81, 17, 97, 49, 164, 87, 96, 0, 128, 253, 91, 96, 68, 53, 97, 3, 0, 81, 17, 21, 97, 49, 182, 87, 96, 0, 128, 253, 91, 97, 2, 224, 128, 81, 97, 3, 0, 81, 128, 130, 16, 21, 97, 49, 204, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 97, 2, 224, 81, 96, 23, 85, 96, 21, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 3, 0, 81, 128, 130, 16, 21, 97, 49, 254, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 97, 3, 0, 81, 97, 3, 32, 82, 96, 0, 51, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 3, 32, 163, 97, 3, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 0, 96, 4, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 91, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 24, 21, 97, 50, 237, 87, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 119, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 50, 141, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 3, 64, 82, 97, 1, 64, 81, 97, 3, 96, 82, 96, 4, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 183, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 53, 97, 3, 128, 82, 97, 3, 92, 96, 0, 97, 3, 32, 81, 96, 2, 129, 16, 97, 50, 214, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 50, 237, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 50, 70, 87, 91, 80, 80, 96, 4, 53, 97, 3, 32, 82, 96, 36, 53, 97, 3, 64, 82, 97, 2, 128, 81, 97, 3, 96, 82, 97, 2, 160, 81, 97, 3, 128, 82, 97, 2, 32, 81, 97, 3, 160, 82, 97, 2, 224, 81, 97, 3, 192, 82, 51, 127, 43, 85, 8, 55, 141, 126, 25, 224, 213, 250, 51, 132, 25, 3, 71, 49, 65, 108, 79, 91, 33, 154, 16, 55, 153, 86, 247, 100, 49, 127, 212, 126, 96, 192, 97, 3, 32, 162, 97, 3, 0, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 204, 43, 39, 215, 129, 20, 21, 97, 51, 126, 87, 96, 0, 97, 1, 64, 82, 97, 51, 175, 86, 91, 99, 197, 50, 167, 116, 129, 20, 21, 97, 51, 170, 87, 96, 68, 53, 96, 1, 28, 21, 97, 51, 154, 87, 96, 0, 128, 253, 91, 96, 32, 96, 68, 97, 1, 64, 55, 96, 0, 80, 97, 51, 175, 86, 91, 97, 52, 161, 86, 91, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 51, 191, 87, 25, 91, 96, 127, 28, 21, 97, 51, 204, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 97, 1, 64, 81, 21, 97, 52, 29, 87, 96, 4, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 96, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 128, 82, 80, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 96, 4, 53, 97, 1, 160, 82, 96, 36, 53, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 128, 81, 97, 2, 0, 82, 97, 2, 0, 81, 97, 1, 224, 81, 97, 1, 192, 81, 97, 1, 160, 81, 96, 6, 88, 1, 97, 86, 13, 86, 91, 97, 2, 96, 82, 97, 2, 128, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 96, 128, 128, 128, 128, 81, 97, 2, 160, 82, 80, 80, 96, 32, 129, 1, 144, 80, 128, 128, 128, 81, 97, 2, 192, 82, 80, 80, 80, 80, 97, 2, 160, 81, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 26, 77, 1, 210, 129, 20, 21, 97, 52, 183, 87, 51, 97, 1, 64, 82, 97, 52, 232, 86, 91, 99, 8, 21, 121, 165, 129, 20, 21, 97, 52, 227, 87, 96, 100, 53, 96, 160, 28, 21, 97, 52, 211, 87, 96, 0, 128, 253, 91, 96, 32, 96, 100, 97, 1, 64, 55, 96, 0, 80, 97, 52, 232, 86, 91, 97, 55, 175, 86, 91, 96, 24, 84, 21, 97, 52, 245, 87, 96, 0, 128, 253, 91, 96, 1, 96, 24, 85, 96, 36, 53, 128, 128, 96, 0, 129, 18, 21, 97, 53, 10, 87, 25, 91, 96, 127, 28, 21, 97, 53, 23, 87, 96, 0, 128, 253, 91, 144, 80, 80, 96, 17, 84, 21, 97, 53, 39, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 67, 78, 86, 91, 97, 1, 64, 82, 96, 0, 80, 96, 64, 54, 97, 1, 96, 55, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 96, 4, 53, 97, 1, 160, 82, 96, 36, 53, 97, 1, 192, 82, 96, 1, 128, 96, 192, 82, 96, 32, 96, 192, 32, 84, 97, 1, 224, 82, 96, 1, 129, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 2, 0, 82, 80, 97, 2, 0, 81, 97, 1, 224, 81, 97, 1, 192, 81, 97, 1, 160, 81, 96, 6, 88, 1, 97, 86, 13, 86, 91, 97, 2, 96, 82, 97, 2, 128, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 96, 128, 128, 128, 128, 81, 97, 2, 160, 82, 80, 80, 96, 32, 129, 1, 144, 80, 128, 128, 128, 81, 97, 2, 192, 82, 80, 80, 80, 80, 97, 2, 160, 128, 81, 97, 1, 96, 82, 128, 96, 32, 1, 81, 97, 1, 128, 82, 80, 96, 68, 53, 97, 1, 96, 81, 16, 21, 97, 53, 240, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 2, 129, 16, 97, 54, 0, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 128, 84, 97, 1, 96, 81, 97, 1, 128, 81, 96, 3, 84, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 54, 44, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 54, 79, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 128, 130, 16, 21, 97, 54, 99, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 96, 23, 84, 96, 4, 53, 128, 130, 16, 21, 97, 54, 128, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 160, 82, 97, 1, 160, 81, 96, 23, 85, 96, 21, 51, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 96, 4, 53, 128, 130, 16, 21, 97, 54, 178, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 96, 4, 53, 97, 1, 192, 82, 96, 0, 51, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 1, 192, 163, 96, 36, 53, 96, 2, 129, 16, 97, 54, 253, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 59, 97, 55, 19, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 1, 192, 82, 97, 1, 64, 81, 97, 1, 224, 82, 97, 1, 96, 81, 97, 2, 0, 82, 97, 1, 220, 96, 0, 96, 36, 53, 96, 2, 129, 16, 97, 55, 71, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 241, 97, 55, 94, 87, 96, 0, 128, 253, 91, 96, 4, 53, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 160, 81, 97, 2, 0, 82, 51, 127, 90, 208, 86, 242, 226, 138, 140, 236, 35, 32, 21, 64, 107, 132, 54, 104, 193, 227, 108, 218, 89, 129, 39, 236, 59, 140, 89, 184, 199, 39, 115, 160, 96, 96, 97, 1, 192, 162, 97, 1, 96, 81, 96, 0, 82, 96, 0, 96, 24, 85, 96, 32, 96, 0, 243, 91, 99, 60, 21, 126, 100, 129, 20, 21, 97, 57, 80, 87, 96, 7, 84, 51, 20, 97, 55, 201, 87, 96, 0, 128, 253, 91, 96, 10, 84, 98, 1, 81, 128, 129, 129, 131, 1, 16, 21, 97, 55, 223, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 66, 16, 21, 97, 55, 242, 87, 96, 0, 128, 253, 91, 66, 98, 1, 81, 128, 129, 129, 131, 1, 16, 21, 97, 56, 6, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 36, 53, 16, 21, 97, 56, 27, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 96, 81, 97, 1, 64, 82, 96, 4, 53, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 56, 81, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 82, 96, 0, 96, 4, 53, 17, 21, 97, 56, 116, 87, 98, 15, 66, 64, 96, 4, 53, 16, 97, 56, 119, 86, 91, 96, 0, 91, 97, 56, 128, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 97, 1, 96, 81, 16, 21, 97, 56, 195, 87, 97, 1, 64, 81, 97, 1, 96, 81, 96, 10, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 56, 172, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 16, 21, 97, 56, 190, 87, 96, 0, 128, 253, 91, 97, 56, 243, 86, 91, 97, 1, 64, 81, 96, 10, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 56, 221, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 17, 21, 97, 56, 243, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 8, 85, 97, 1, 96, 81, 96, 9, 85, 66, 96, 10, 85, 96, 36, 53, 96, 11, 85, 97, 1, 64, 81, 97, 1, 128, 82, 97, 1, 96, 81, 97, 1, 160, 82, 66, 97, 1, 192, 82, 96, 36, 53, 97, 1, 224, 82, 127, 162, 183, 30, 198, 223, 148, 147, 0, 181, 154, 171, 54, 181, 94, 24, 150, 151, 183, 80, 17, 157, 211, 73, 252, 250, 140, 15, 119, 158, 131, 194, 84, 96, 128, 97, 1, 128, 161, 0, 91, 99, 85, 26, 101, 136, 129, 20, 21, 97, 57, 211, 87, 96, 7, 84, 51, 20, 97, 57, 106, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 1, 96, 82, 97, 1, 64, 82, 97, 1, 96, 81, 97, 1, 64, 82, 97, 1, 64, 81, 96, 8, 85, 97, 1, 64, 81, 96, 9, 85, 66, 96, 10, 85, 66, 96, 11, 85, 97, 1, 64, 81, 97, 1, 96, 82, 66, 97, 1, 128, 82, 127, 70, 226, 47, 179, 112, 154, 210, 137, 246, 44, 230, 61, 70, 146, 72, 83, 109, 188, 120, 216, 43, 132, 163, 215, 231, 74, 214, 6, 220, 32, 25, 56, 96, 64, 97, 1, 96, 161, 0, 91, 99, 91, 90, 20, 103, 129, 20, 21, 97, 58, 143, 87, 96, 7, 84, 51, 20, 97, 57, 237, 87, 96, 0, 128, 253, 91, 96, 12, 84, 21, 97, 57, 250, 87, 96, 0, 128, 253, 91, 100, 1, 42, 5, 242, 0, 96, 4, 53, 17, 21, 97, 58, 14, 87, 96, 0, 128, 253, 91, 100, 2, 84, 11, 228, 0, 96, 36, 53, 17, 21, 97, 58, 34, 87, 96, 0, 128, 253, 91, 66, 98, 3, 244, 128, 129, 129, 131, 1, 16, 21, 97, 58, 54, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 1, 64, 82, 97, 1, 64, 81, 96, 12, 85, 96, 4, 53, 96, 14, 85, 96, 36, 53, 96, 15, 85, 96, 4, 53, 97, 1, 96, 82, 96, 36, 53, 97, 1, 128, 82, 97, 1, 64, 81, 127, 53, 31, 197, 218, 47, 191, 72, 15, 34, 37, 222, 191, 54, 100, 164, 188, 144, 250, 153, 35, 116, 58, 173, 88, 180, 96, 63, 100, 142, 147, 31, 224, 96, 64, 97, 1, 96, 162, 0, 91, 99, 79, 18, 254, 151, 129, 20, 21, 97, 59, 33, 87, 96, 7, 84, 51, 20, 97, 58, 169, 87, 96, 0, 128, 253, 91, 96, 12, 84, 66, 16, 21, 97, 58, 184, 87, 96, 0, 128, 253, 91, 96, 0, 96, 12, 84, 24, 97, 58, 199, 87, 96, 0, 128, 253, 91, 96, 0, 96, 12, 85, 96, 14, 84, 97, 1, 64, 82, 96, 15, 84, 97, 1, 96, 82, 97, 1, 64, 81, 96, 2, 85, 97, 1, 96, 81, 96, 3, 85, 97, 1, 64, 81, 97, 1, 128, 82, 97, 1, 96, 81, 97, 1, 160, 82, 127, 190, 18, 133, 155, 99, 106, 237, 96, 125, 82, 48, 178, 204, 39, 17, 246, 141, 112, 229, 16, 96, 230, 204, 161, 245, 117, 239, 93, 47, 204, 149, 209, 96, 64, 97, 1, 128, 161, 0, 91, 99, 34, 104, 64, 251, 129, 20, 21, 97, 59, 66, 87, 96, 7, 84, 51, 20, 97, 59, 59, 87, 96, 0, 128, 253, 91, 96, 0, 96, 12, 85, 0, 91, 99, 107, 68, 26, 64, 129, 20, 21, 97, 59, 212, 87, 96, 4, 53, 96, 160, 28, 21, 97, 59, 94, 87, 96, 0, 128, 253, 91, 96, 7, 84, 51, 20, 97, 59, 108, 87, 96, 0, 128, 253, 91, 96, 13, 84, 21, 97, 59, 121, 87, 96, 0, 128, 253, 91, 66, 98, 3, 244, 128, 129, 129, 131, 1, 16, 21, 97, 59, 141, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 1, 64, 82, 97, 1, 64, 81, 96, 13, 85, 96, 4, 53, 96, 16, 85, 96, 4, 53, 97, 1, 64, 81, 127, 24, 26, 163, 170, 23, 212, 203, 249, 146, 101, 221, 68, 67, 235, 160, 9, 67, 61, 60, 222, 121, 214, 1, 100, 253, 225, 209, 161, 146, 190, 185, 53, 96, 0, 96, 0, 163, 0, 91, 99, 106, 28, 5, 174, 129, 20, 21, 97, 60, 75, 87, 96, 7, 84, 51, 20, 97, 59, 238, 87, 96, 0, 128, 253, 91, 96, 13, 84, 66, 16, 21, 97, 59, 253, 87, 96, 0, 128, 253, 91, 96, 0, 96, 13, 84, 24, 97, 60, 12, 87, 96, 0, 128, 253, 91, 96, 0, 96, 13, 85, 96, 16, 84, 97, 1, 64, 82, 97, 1, 64, 81, 96, 7, 85, 97, 1, 64, 81, 127, 113, 97, 64, 113, 184, 141, 238, 94, 11, 42, 229, 120, 169, 221, 123, 46, 187, 233, 174, 131, 43, 164, 25, 220, 2, 66, 205, 6, 90, 41, 11, 108, 96, 0, 96, 0, 162, 0, 91, 99, 134, 251, 241, 147, 129, 20, 21, 97, 60, 108, 87, 96, 7, 84, 51, 20, 97, 60, 101, 87, 96, 0, 128, 253, 91, 96, 0, 96, 13, 85, 0, 91, 99, 226, 231, 210, 100, 129, 20, 21, 97, 61, 4, 87, 96, 32, 97, 1, 192, 96, 36, 99, 112, 160, 130, 49, 97, 1, 64, 82, 48, 97, 1, 96, 82, 97, 1, 92, 96, 4, 53, 96, 2, 129, 16, 97, 60, 160, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 250, 97, 60, 183, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 60, 196, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 1, 192, 81, 96, 4, 53, 96, 2, 129, 16, 97, 60, 219, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 128, 130, 16, 21, 97, 60, 244, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 48, 197, 64, 133, 129, 20, 21, 97, 62, 25, 87, 96, 7, 84, 51, 20, 97, 61, 30, 87, 96, 0, 128, 253, 91, 97, 1, 64, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 64, 81, 96, 2, 129, 16, 97, 61, 59, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 96, 82, 96, 32, 97, 2, 32, 96, 36, 99, 112, 160, 130, 49, 97, 1, 160, 82, 48, 97, 1, 192, 82, 97, 1, 188, 97, 1, 96, 81, 90, 250, 97, 61, 114, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 61, 127, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 32, 81, 97, 1, 64, 81, 96, 2, 129, 16, 97, 61, 151, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 128, 130, 16, 21, 97, 61, 176, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 128, 82, 96, 0, 97, 1, 128, 81, 17, 21, 97, 62, 5, 87, 97, 1, 96, 81, 59, 97, 61, 213, 87, 96, 0, 128, 253, 91, 96, 0, 96, 0, 96, 68, 99, 169, 5, 156, 187, 97, 1, 160, 82, 51, 97, 1, 192, 82, 97, 1, 128, 81, 97, 1, 224, 82, 97, 1, 188, 96, 0, 97, 1, 96, 81, 90, 241, 97, 62, 5, 87, 96, 0, 128, 253, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 61, 42, 87, 91, 80, 80, 0, 91, 99, 82, 76, 57, 1, 129, 20, 21, 97, 62, 197, 87, 96, 7, 84, 51, 20, 97, 62, 51, 87, 96, 0, 128, 253, 91, 97, 1, 64, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 32, 97, 1, 224, 96, 36, 99, 112, 160, 130, 49, 97, 1, 96, 82, 48, 97, 1, 128, 82, 97, 1, 124, 97, 1, 64, 81, 96, 2, 129, 16, 97, 62, 104, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 90, 250, 97, 62, 127, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 62, 140, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 1, 224, 81, 97, 1, 64, 81, 96, 2, 129, 16, 97, 62, 164, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 62, 63, 87, 91, 80, 80, 0, 91, 99, 227, 105, 136, 83, 129, 20, 21, 97, 62, 244, 87, 96, 7, 84, 51, 20, 97, 62, 223, 87, 96, 0, 128, 253, 91, 66, 96, 18, 84, 17, 97, 62, 237, 87, 96, 0, 128, 253, 91, 96, 1, 96, 17, 85, 0, 91, 99, 48, 70, 249, 114, 129, 20, 21, 97, 63, 21, 87, 96, 7, 84, 51, 20, 97, 63, 14, 87, 96, 0, 128, 253, 91, 96, 0, 96, 17, 85, 0, 91, 99, 198, 97, 6, 87, 129, 20, 21, 97, 63, 70, 87, 96, 4, 53, 96, 2, 129, 16, 97, 63, 49, 87, 96, 0, 128, 253, 91, 96, 0, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 73, 3, 176, 209, 129, 20, 21, 97, 63, 119, 87, 96, 4, 53, 96, 2, 129, 16, 97, 63, 98, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 221, 202, 63, 67, 129, 20, 21, 97, 63, 143, 87, 96, 2, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 254, 227, 247, 249, 129, 20, 21, 97, 63, 167, 87, 96, 3, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 99, 84, 63, 6, 129, 20, 21, 97, 63, 191, 87, 96, 6, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 141, 165, 203, 91, 129, 20, 21, 97, 63, 215, 87, 96, 7, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 84, 9, 73, 26, 129, 20, 21, 97, 63, 239, 87, 96, 8, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 180, 181, 119, 173, 129, 20, 21, 97, 64, 7, 87, 96, 9, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 32, 129, 6, 108, 129, 20, 21, 97, 64, 31, 87, 96, 10, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 20, 5, 34, 136, 129, 20, 21, 97, 64, 55, 87, 96, 11, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 64, 94, 40, 248, 129, 20, 21, 97, 64, 79, 87, 96, 12, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 224, 160, 181, 134, 129, 20, 21, 97, 64, 103, 87, 96, 13, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 88, 104, 13, 11, 129, 20, 21, 97, 64, 127, 87, 96, 14, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 227, 130, 68, 98, 129, 20, 21, 97, 64, 151, 87, 96, 15, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 30, 192, 205, 193, 129, 20, 21, 97, 64, 175, 87, 96, 16, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 6, 253, 222, 3, 129, 20, 21, 97, 65, 84, 87, 96, 19, 128, 96, 192, 82, 96, 32, 96, 192, 32, 97, 1, 128, 96, 32, 130, 84, 1, 97, 1, 32, 96, 0, 96, 3, 129, 131, 82, 1, 91, 130, 97, 1, 32, 81, 96, 32, 2, 17, 21, 97, 64, 237, 87, 97, 65, 15, 86, 91, 97, 1, 32, 81, 133, 1, 84, 97, 1, 32, 81, 96, 32, 2, 133, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 64, 218, 87, 91, 80, 80, 80, 80, 80, 80, 97, 1, 128, 81, 128, 97, 1, 160, 1, 129, 130, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 3, 54, 130, 55, 80, 80, 96, 32, 97, 1, 96, 82, 96, 64, 97, 1, 128, 81, 1, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 97, 1, 96, 243, 91, 99, 149, 216, 155, 65, 129, 20, 21, 97, 65, 249, 87, 96, 20, 128, 96, 192, 82, 96, 32, 96, 192, 32, 97, 1, 128, 96, 32, 130, 84, 1, 97, 1, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 130, 97, 1, 32, 81, 96, 32, 2, 17, 21, 97, 65, 146, 87, 97, 65, 180, 86, 91, 97, 1, 32, 81, 133, 1, 84, 97, 1, 32, 81, 96, 32, 2, 133, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 65, 127, 87, 91, 80, 80, 80, 80, 80, 80, 97, 1, 128, 81, 128, 97, 1, 160, 1, 129, 130, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 3, 54, 130, 55, 80, 80, 96, 32, 97, 1, 96, 82, 96, 64, 97, 1, 128, 81, 1, 96, 32, 96, 1, 130, 3, 6, 96, 31, 130, 1, 3, 144, 80, 97, 1, 96, 243, 91, 99, 112, 160, 130, 49, 129, 20, 21, 97, 66, 47, 87, 96, 4, 53, 96, 160, 28, 21, 97, 66, 21, 87, 96, 0, 128, 253, 91, 96, 21, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 221, 98, 237, 62, 129, 20, 21, 97, 66, 131, 87, 96, 4, 53, 96, 160, 28, 21, 97, 66, 75, 87, 96, 0, 128, 253, 91, 96, 36, 53, 96, 160, 28, 21, 97, 66, 91, 87, 96, 0, 128, 253, 91, 96, 22, 96, 4, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 96, 36, 53, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 99, 24, 22, 13, 221, 129, 20, 21, 97, 66, 155, 87, 96, 23, 84, 96, 0, 82, 96, 32, 96, 0, 243, 91, 80, 91, 96, 0, 96, 0, 253, 91, 97, 1, 160, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 96, 21, 97, 1, 64, 81, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 1, 128, 81, 128, 130, 16, 21, 97, 66, 215, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 85, 80, 96, 21, 97, 1, 96, 81, 96, 224, 82, 96, 192, 82, 96, 64, 96, 192, 32, 128, 84, 97, 1, 128, 81, 129, 129, 131, 1, 16, 21, 97, 67, 7, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 85, 80, 97, 1, 128, 81, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 64, 81, 127, 221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, 161, 22, 40, 245, 90, 77, 245, 35, 179, 239, 96, 32, 97, 1, 192, 163, 97, 1, 160, 81, 86, 91, 97, 1, 64, 82, 66, 96, 6, 84, 128, 130, 16, 21, 97, 67, 99, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 1, 96, 82, 96, 0, 97, 1, 96, 81, 17, 21, 97, 68, 58, 87, 97, 1, 128, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 128, 81, 96, 2, 129, 16, 97, 67, 151, 87, 96, 0, 128, 253, 91, 96, 1, 96, 192, 82, 96, 32, 96, 192, 32, 1, 84, 97, 1, 160, 82, 97, 1, 128, 81, 96, 2, 129, 16, 97, 67, 184, 87, 96, 0, 128, 253, 91, 96, 5, 96, 192, 82, 96, 32, 96, 192, 32, 1, 128, 84, 97, 1, 160, 81, 97, 1, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 67, 225, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 67, 247, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 85, 80, 97, 1, 160, 81, 97, 1, 128, 81, 96, 2, 129, 16, 97, 68, 22, 87, 96, 0, 128, 253, 91, 96, 4, 96, 192, 82, 96, 32, 96, 192, 32, 1, 85, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 67, 134, 87, 91, 80, 80, 66, 96, 6, 85, 91, 97, 1, 64, 81, 86, 91, 97, 1, 64, 82, 96, 11, 84, 97, 1, 96, 82, 96, 9, 84, 97, 1, 128, 82, 97, 1, 96, 81, 66, 16, 21, 97, 69, 198, 87, 96, 8, 84, 97, 1, 160, 82, 96, 10, 84, 97, 1, 192, 82, 97, 1, 160, 81, 97, 1, 128, 81, 17, 21, 97, 69, 32, 87, 97, 1, 160, 81, 97, 1, 128, 81, 97, 1, 160, 81, 128, 130, 16, 21, 97, 68, 146, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 66, 97, 1, 192, 81, 128, 130, 16, 21, 97, 68, 171, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 68, 198, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 68, 226, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 68, 244, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 69, 9, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 0, 82, 96, 0, 81, 97, 1, 64, 81, 86, 97, 69, 193, 86, 91, 97, 1, 160, 81, 97, 1, 160, 81, 97, 1, 128, 81, 128, 130, 16, 21, 97, 69, 57, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 66, 97, 1, 192, 81, 128, 130, 16, 21, 97, 69, 82, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 69, 109, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 1, 96, 81, 97, 1, 192, 81, 128, 130, 16, 21, 97, 69, 137, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 69, 155, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 69, 174, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 0, 82, 96, 0, 81, 97, 1, 64, 81, 86, 91, 97, 69, 214, 86, 91, 97, 1, 128, 81, 96, 0, 82, 96, 0, 81, 97, 1, 64, 81, 86, 91, 0, 91, 97, 1, 128, 82, 97, 1, 64, 82, 97, 1, 96, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 1, 160, 82, 96, 32, 97, 2, 64, 96, 4, 99, 187, 123, 139, 128, 97, 1, 224, 82, 97, 1, 252, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 70, 41, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 70, 54, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 64, 81, 97, 1, 192, 82, 97, 1, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 160, 97, 1, 224, 81, 96, 2, 129, 16, 97, 70, 97, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 64, 97, 1, 224, 81, 96, 2, 129, 16, 97, 70, 122, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 70, 147, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 97, 1, 160, 97, 1, 224, 81, 96, 2, 129, 16, 97, 70, 190, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 70, 77, 87, 91, 80, 80, 96, 64, 97, 1, 224, 82, 91, 96, 0, 97, 1, 224, 81, 17, 21, 21, 97, 70, 239, 87, 97, 71, 11, 86, 91, 96, 32, 97, 1, 224, 81, 3, 97, 1, 160, 1, 81, 96, 32, 97, 1, 224, 81, 3, 97, 1, 224, 82, 97, 70, 221, 86, 91, 97, 1, 128, 81, 86, 91, 97, 1, 160, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 96, 64, 54, 97, 1, 192, 55, 97, 2, 32, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 32, 97, 2, 32, 81, 2, 97, 1, 64, 1, 81, 97, 2, 0, 82, 97, 1, 192, 128, 81, 97, 2, 0, 81, 129, 129, 131, 1, 16, 21, 97, 71, 92, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 71, 52, 87, 91, 80, 80, 97, 1, 192, 81, 21, 21, 97, 71, 145, 87, 96, 0, 96, 0, 82, 96, 0, 81, 97, 1, 160, 81, 86, 91, 97, 1, 192, 81, 97, 2, 0, 82, 97, 1, 128, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 71, 179, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 32, 82, 97, 2, 64, 96, 0, 96, 255, 129, 131, 82, 1, 91, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 160, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 32, 97, 2, 160, 81, 2, 97, 1, 64, 1, 81, 97, 2, 128, 82, 97, 2, 96, 81, 97, 2, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 10, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 128, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 43, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 72, 61, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 71, 222, 87, 91, 80, 80, 97, 2, 0, 81, 97, 1, 224, 82, 97, 2, 32, 81, 97, 1, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 126, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 4, 144, 80, 144, 80, 97, 2, 96, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 168, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 72, 190, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 72, 221, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 32, 81, 96, 100, 128, 130, 16, 21, 97, 72, 247, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 73, 22, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 4, 144, 80, 144, 80, 96, 3, 97, 2, 96, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 73, 64, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 73, 86, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 128, 128, 97, 73, 104, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 0, 82, 97, 1, 224, 81, 97, 2, 0, 81, 17, 21, 97, 73, 188, 87, 96, 1, 97, 2, 0, 81, 97, 1, 224, 81, 128, 130, 16, 21, 97, 73, 151, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 73, 183, 87, 97, 2, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 160, 81, 86, 91, 97, 73, 243, 86, 91, 96, 1, 97, 1, 224, 81, 97, 2, 0, 81, 128, 130, 16, 21, 97, 73, 211, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 73, 243, 87, 97, 2, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 160, 81, 86, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 71, 202, 87, 91, 80, 80, 96, 0, 96, 0, 253, 91, 97, 1, 160, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 64, 81, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 224, 82, 97, 1, 224, 81, 97, 1, 192, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 64, 82, 97, 2, 96, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 64, 128, 81, 97, 2, 128, 82, 128, 96, 32, 1, 81, 97, 2, 160, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 192, 82, 97, 2, 160, 81, 97, 2, 224, 82, 97, 1, 128, 81, 97, 3, 0, 82, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 3, 96, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 96, 81, 96, 0, 82, 96, 0, 81, 97, 1, 160, 81, 86, 91, 97, 1, 224, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 192, 82, 97, 1, 96, 81, 97, 1, 64, 81, 24, 97, 75, 66, 87, 96, 0, 128, 253, 91, 96, 0, 97, 1, 96, 81, 18, 21, 97, 75, 83, 87, 96, 0, 128, 253, 91, 96, 2, 97, 1, 96, 81, 18, 97, 75, 99, 87, 96, 0, 128, 253, 91, 96, 0, 97, 1, 64, 81, 18, 21, 97, 75, 116, 87, 96, 0, 128, 253, 91, 96, 2, 97, 1, 64, 81, 18, 97, 75, 132, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 32, 81, 97, 2, 0, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 1, 160, 81, 97, 2, 64, 82, 97, 1, 192, 81, 97, 2, 96, 82, 97, 2, 0, 81, 97, 2, 128, 82, 97, 2, 128, 81, 97, 2, 96, 81, 97, 2, 64, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 2, 224, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 224, 81, 97, 2, 32, 82, 97, 2, 0, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 76, 100, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 82, 97, 2, 32, 81, 97, 2, 96, 82, 96, 96, 54, 97, 2, 128, 55, 97, 2, 224, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 64, 81, 97, 2, 224, 81, 20, 21, 97, 76, 165, 87, 97, 1, 128, 81, 97, 2, 160, 82, 97, 76, 218, 86, 91, 97, 1, 96, 81, 97, 2, 224, 81, 24, 21, 97, 76, 213, 87, 97, 1, 160, 97, 2, 224, 81, 96, 2, 129, 16, 97, 76, 199, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 82, 97, 76, 218, 86, 91, 97, 77, 86, 86, 91, 97, 2, 128, 128, 81, 97, 2, 160, 81, 129, 129, 131, 1, 16, 21, 97, 76, 242, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 2, 96, 81, 97, 2, 32, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 24, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 160, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 57, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 77, 75, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 76, 138, 87, 91, 80, 80, 97, 2, 96, 81, 97, 2, 32, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 132, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 161, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 194, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 77, 212, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 96, 82, 97, 2, 128, 81, 97, 2, 32, 81, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 77, 252, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 128, 128, 97, 78, 18, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 78, 39, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 224, 82, 97, 2, 32, 81, 97, 3, 0, 82, 97, 3, 32, 96, 0, 96, 255, 129, 131, 82, 1, 91, 97, 3, 0, 81, 97, 2, 192, 82, 97, 3, 0, 81, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 78, 106, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 96, 81, 129, 129, 131, 1, 16, 21, 97, 78, 132, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 2, 97, 3, 0, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 78, 165, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 224, 81, 129, 129, 131, 1, 16, 21, 97, 78, 191, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 32, 81, 128, 130, 16, 21, 97, 78, 215, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 78, 233, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 0, 82, 97, 2, 192, 81, 97, 3, 0, 81, 17, 21, 97, 79, 61, 87, 96, 1, 97, 3, 0, 81, 97, 2, 192, 81, 128, 130, 16, 21, 97, 79, 24, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 79, 56, 87, 97, 3, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 97, 79, 116, 86, 91, 96, 1, 97, 2, 192, 81, 97, 3, 0, 81, 128, 130, 16, 21, 97, 79, 84, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 79, 116, 87, 97, 3, 0, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 78, 70, 87, 91, 80, 80, 96, 0, 96, 0, 253, 91, 97, 1, 224, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 192, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 2, 0, 82, 96, 32, 97, 2, 160, 96, 4, 99, 187, 123, 139, 128, 97, 2, 64, 82, 97, 2, 92, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 79, 233, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 79, 246, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 2, 160, 81, 97, 2, 32, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 1, 160, 81, 97, 2, 128, 82, 97, 1, 192, 81, 97, 2, 160, 82, 97, 2, 160, 81, 97, 2, 128, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 3, 0, 82, 97, 3, 32, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 0, 128, 81, 97, 2, 64, 82, 128, 96, 32, 1, 81, 97, 2, 96, 82, 80, 97, 2, 64, 97, 1, 64, 81, 96, 2, 129, 16, 97, 80, 161, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 128, 81, 97, 2, 0, 97, 1, 64, 81, 96, 2, 129, 16, 97, 80, 190, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 80, 215, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 80, 253, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 128, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 1, 64, 81, 97, 2, 192, 82, 97, 1, 96, 81, 97, 2, 224, 82, 97, 2, 128, 81, 97, 3, 0, 82, 97, 2, 64, 81, 97, 3, 32, 82, 97, 2, 96, 81, 97, 3, 64, 82, 97, 3, 64, 81, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 75, 24, 86, 91, 97, 3, 160, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 160, 81, 97, 2, 160, 82, 97, 2, 64, 97, 1, 96, 81, 96, 2, 129, 16, 97, 81, 205, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 81, 128, 130, 16, 21, 97, 81, 227, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 96, 1, 128, 130, 16, 21, 97, 81, 249, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 192, 82, 96, 2, 84, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 82, 31, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 97, 2, 224, 82, 97, 2, 192, 81, 97, 2, 224, 81, 128, 130, 16, 21, 97, 82, 76, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 82, 112, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 97, 1, 96, 81, 96, 2, 129, 16, 97, 82, 139, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 82, 155, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 96, 0, 82, 96, 0, 81, 97, 1, 224, 81, 86, 91, 97, 1, 224, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 192, 82, 96, 0, 97, 1, 96, 81, 18, 21, 97, 82, 214, 87, 96, 0, 128, 253, 91, 96, 2, 97, 1, 96, 81, 18, 97, 82, 230, 87, 96, 0, 128, 253, 91, 97, 1, 64, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 83, 0, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 82, 97, 1, 192, 81, 97, 2, 32, 82, 96, 96, 54, 97, 2, 64, 55, 97, 2, 160, 96, 0, 96, 2, 129, 131, 82, 1, 91, 97, 1, 96, 81, 97, 2, 160, 81, 24, 21, 97, 83, 86, 87, 97, 1, 128, 97, 2, 160, 81, 96, 2, 129, 16, 97, 83, 72, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 96, 82, 97, 83, 91, 86, 91, 97, 83, 215, 86, 91, 97, 2, 64, 128, 81, 97, 2, 96, 81, 129, 129, 131, 1, 16, 21, 97, 83, 115, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 129, 82, 80, 97, 2, 32, 81, 97, 1, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 83, 153, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 96, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 83, 186, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 83, 204, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 32, 82, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 83, 38, 87, 91, 80, 80, 97, 2, 32, 81, 97, 1, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 5, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 34, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 67, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 128, 128, 97, 84, 85, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 32, 82, 97, 2, 64, 81, 97, 1, 192, 81, 96, 100, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 125, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 0, 81, 128, 128, 97, 84, 147, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 129, 129, 131, 1, 16, 21, 97, 84, 168, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 2, 160, 82, 97, 1, 192, 81, 97, 2, 192, 82, 97, 2, 224, 96, 0, 96, 255, 129, 131, 82, 1, 91, 97, 2, 192, 81, 97, 2, 128, 82, 97, 2, 192, 81, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 84, 235, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 32, 81, 129, 129, 131, 1, 16, 21, 97, 85, 5, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 96, 2, 97, 2, 192, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 85, 38, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 160, 81, 129, 129, 131, 1, 16, 21, 97, 85, 64, 87, 96, 0, 128, 253, 91, 128, 130, 1, 144, 80, 144, 80, 97, 1, 192, 81, 128, 130, 16, 21, 97, 85, 88, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 128, 128, 97, 85, 106, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 97, 2, 128, 81, 97, 2, 192, 81, 17, 21, 97, 85, 190, 87, 96, 1, 97, 2, 192, 81, 97, 2, 128, 81, 128, 130, 16, 21, 97, 85, 153, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 85, 185, 87, 97, 2, 192, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 97, 85, 245, 86, 91, 96, 1, 97, 2, 128, 81, 97, 2, 192, 81, 128, 130, 16, 21, 97, 85, 213, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 17, 21, 21, 97, 85, 245, 87, 97, 2, 192, 81, 96, 0, 82, 80, 80, 96, 0, 81, 97, 1, 224, 81, 86, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 84, 199, 87, 91, 80, 80, 96, 0, 96, 0, 253, 91, 97, 1, 192, 82, 97, 1, 64, 82, 97, 1, 96, 82, 97, 1, 128, 82, 97, 1, 160, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 96, 6, 88, 1, 97, 68, 64, 86, 91, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 0, 81, 97, 1, 224, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 1, 128, 81, 97, 2, 64, 82, 97, 1, 160, 81, 97, 2, 96, 82, 97, 2, 96, 81, 97, 2, 64, 81, 96, 6, 88, 1, 97, 69, 216, 86, 91, 97, 2, 192, 82, 97, 2, 224, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 2, 192, 128, 81, 97, 2, 0, 82, 128, 96, 32, 1, 81, 97, 2, 32, 82, 80, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 0, 81, 97, 2, 96, 82, 97, 2, 32, 81, 97, 2, 128, 82, 97, 1, 224, 81, 97, 2, 160, 82, 97, 2, 160, 81, 97, 2, 128, 81, 97, 2, 96, 81, 96, 6, 88, 1, 97, 71, 17, 86, 91, 97, 3, 0, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 0, 81, 97, 2, 64, 82, 96, 23, 84, 97, 2, 96, 82, 97, 2, 64, 81, 97, 1, 64, 81, 97, 2, 64, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 87, 138, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 96, 81, 128, 128, 97, 87, 160, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 87, 179, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 2, 128, 82, 97, 1, 64, 81, 97, 1, 96, 81, 97, 1, 128, 81, 97, 1, 160, 81, 97, 1, 192, 81, 97, 1, 224, 81, 97, 2, 0, 81, 97, 2, 32, 81, 97, 2, 64, 81, 97, 2, 96, 81, 97, 2, 128, 81, 97, 2, 160, 81, 97, 1, 224, 81, 97, 2, 192, 82, 97, 1, 96, 81, 97, 2, 224, 82, 97, 2, 0, 81, 97, 3, 0, 82, 97, 2, 32, 81, 97, 3, 32, 82, 97, 2, 128, 81, 97, 3, 64, 82, 97, 3, 64, 81, 97, 3, 32, 81, 97, 3, 0, 81, 97, 2, 224, 81, 97, 2, 192, 81, 96, 6, 88, 1, 97, 82, 173, 86, 91, 97, 3, 160, 82, 97, 2, 160, 82, 97, 2, 128, 82, 97, 2, 96, 82, 97, 2, 64, 82, 97, 2, 32, 82, 97, 2, 0, 82, 97, 1, 224, 82, 97, 1, 192, 82, 97, 1, 160, 82, 97, 1, 128, 82, 97, 1, 96, 82, 97, 1, 64, 82, 97, 3, 160, 81, 97, 2, 160, 82, 96, 2, 84, 96, 2, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 88, 136, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 96, 4, 128, 130, 4, 144, 80, 144, 80, 97, 2, 192, 82, 108, 12, 159, 44, 156, 208, 70, 116, 237, 234, 64, 0, 0, 0, 97, 2, 224, 82, 96, 32, 97, 3, 128, 96, 4, 99, 187, 123, 139, 128, 97, 3, 32, 82, 97, 3, 60, 115, 39, 230, 17, 253, 39, 178, 118, 172, 189, 95, 253, 99, 46, 94, 174, 190, 201, 118, 30, 64, 90, 250, 97, 88, 225, 87, 96, 0, 128, 253, 91, 96, 31, 61, 17, 97, 88, 238, 87, 96, 0, 128, 253, 91, 96, 0, 80, 97, 3, 128, 81, 97, 3, 0, 82, 97, 2, 0, 81, 97, 3, 32, 82, 97, 2, 32, 81, 97, 3, 64, 82, 97, 2, 0, 97, 1, 96, 81, 96, 2, 129, 16, 97, 89, 29, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 160, 81, 128, 130, 16, 21, 97, 89, 51, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 89, 87, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 224, 97, 1, 96, 81, 96, 2, 129, 16, 97, 89, 114, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 89, 130, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 96, 82, 97, 3, 128, 96, 0, 96, 2, 129, 131, 82, 1, 91, 96, 0, 97, 3, 160, 82, 97, 1, 96, 81, 97, 3, 128, 81, 20, 21, 97, 90, 26, 87, 97, 2, 0, 97, 3, 128, 81, 96, 2, 129, 16, 97, 89, 192, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 89, 221, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 128, 128, 97, 89, 243, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 2, 160, 81, 128, 130, 16, 21, 97, 90, 10, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 160, 82, 97, 90, 153, 86, 91, 97, 2, 0, 97, 3, 128, 81, 96, 2, 129, 16, 97, 90, 46, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 0, 97, 3, 128, 81, 96, 2, 129, 16, 97, 90, 71, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 2, 128, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 90, 100, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 64, 81, 128, 128, 97, 90, 122, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 90, 141, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 160, 82, 91, 97, 3, 32, 97, 3, 128, 81, 96, 2, 129, 16, 97, 90, 173, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 128, 81, 97, 2, 192, 81, 97, 3, 160, 81, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 90, 207, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 100, 2, 84, 11, 228, 0, 128, 130, 4, 144, 80, 144, 80, 128, 130, 16, 21, 97, 90, 240, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 82, 80, 91, 129, 81, 96, 1, 1, 128, 131, 82, 129, 20, 21, 97, 89, 152, 87, 91, 80, 80, 97, 3, 32, 97, 1, 96, 81, 96, 2, 129, 16, 97, 91, 33, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 97, 1, 64, 97, 3, 160, 82, 91, 97, 3, 160, 81, 81, 96, 32, 97, 3, 160, 81, 1, 97, 3, 160, 82, 97, 3, 160, 97, 3, 160, 81, 16, 21, 97, 91, 80, 87, 97, 91, 46, 86, 91, 97, 1, 224, 81, 97, 3, 192, 82, 97, 1, 96, 81, 97, 3, 224, 82, 97, 3, 32, 81, 97, 4, 0, 82, 97, 3, 64, 81, 97, 4, 32, 82, 97, 2, 128, 81, 97, 4, 64, 82, 97, 4, 64, 81, 97, 4, 32, 81, 97, 4, 0, 81, 97, 3, 224, 81, 97, 3, 192, 81, 96, 6, 88, 1, 97, 82, 173, 86, 91, 97, 4, 160, 82, 97, 3, 128, 97, 3, 160, 82, 91, 97, 3, 160, 81, 82, 96, 32, 97, 3, 160, 81, 3, 97, 3, 160, 82, 97, 1, 64, 97, 3, 160, 81, 16, 21, 21, 97, 91, 196, 87, 97, 91, 161, 86, 91, 97, 4, 160, 81, 128, 130, 16, 21, 97, 91, 213, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 97, 3, 128, 82, 97, 3, 128, 81, 96, 1, 128, 130, 16, 21, 97, 91, 243, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 103, 13, 224, 182, 179, 167, 100, 0, 0, 128, 130, 2, 130, 21, 130, 132, 131, 4, 20, 23, 97, 92, 23, 87, 96, 0, 128, 253, 91, 128, 144, 80, 144, 80, 144, 80, 97, 2, 224, 97, 1, 96, 81, 96, 2, 129, 16, 97, 92, 50, 87, 96, 0, 128, 253, 91, 96, 32, 2, 1, 81, 128, 128, 97, 92, 66, 87, 96, 0, 128, 253, 91, 130, 4, 144, 80, 144, 80, 97, 3, 128, 82, 97, 3, 224, 97, 3, 128, 81, 129, 82, 97, 3, 96, 81, 97, 3, 128, 81, 128, 130, 16, 21, 97, 92, 106, 87, 96, 0, 128, 253, 91, 128, 130, 3, 144, 80, 144, 80, 129, 96, 32, 1, 82, 80, 96, 64, 97, 4, 32, 82, 91, 96, 0, 97, 4, 32, 81, 17, 21, 21, 97, 92, 144, 87, 97, 92, 172, 86, 91, 96, 32, 97, 4, 32, 81, 3, 97, 3, 224, 1, 81, 96, 32, 97, 4, 32, 81, 3, 97, 4, 32, 82, 97, 92, 126, 86, 91, 97, 1, 192, 81, 86, -} diff --git a/go/interpreter/sfvm/gas.go b/go/interpreter/sfvm/gas.go index bc51e971..7b952bcc 100644 --- a/go/interpreter/sfvm/gas.go +++ b/go/interpreter/sfvm/gas.go @@ -12,6 +12,7 @@ package sfvm import ( "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" ) const ( @@ -35,28 +36,57 @@ const ( var static_gas_prices = newOpCodePropertyMap(getStaticGasPriceInternal) var static_gas_prices_berlin = newOpCodePropertyMap(getBerlinGasPriceInternal) -func getBerlinGasPriceInternal(op OpCode) tosca.Gas { +// numOpCodes is the number of opcodes in the EVM. +const numOpCodes = 256 + +// opCodePropertyMap is a generic property map for precomputed values. +// Its purpose is to provide a precomputed lookup table for OpCode properties +// that can be generated from a function that takes an OpCode as input. +// Using this type hides internal details of the opcode implementation. +type opCodePropertyMap[T any] struct { + lookup [numOpCodes]T +} + +// newOpCodePropertyMap creates a new OpCode property map. +// The property function shall be resilient to undefined OpCode values, and not +// panic. The zero values or a sentinel value shall be used in such cases. +func newOpCodePropertyMap[T any](property func(op vm.OpCode) T) opCodePropertyMap[T] { + lookup := [numOpCodes]T{} + for i := 0; i < numOpCodes; i++ { + lookup[i] = property(vm.OpCode(i)) + } + return opCodePropertyMap[T]{lookup} +} + +func (p *opCodePropertyMap[T]) get(op vm.OpCode) T { + // Index may be out of bounds. Nevertheless, bounds check carry a performance + // penalty. If the property map is initialized correctly, the index will be + // within bounds. + return p.lookup[op] +} + +func getBerlinGasPriceInternal(op vm.OpCode) tosca.Gas { gp := getStaticGasPriceInternal(op) // Changed static gas prices with EIP2929 switch op { - case SLOAD: + case vm.SLOAD: gp = 0 - case EXTCODECOPY: + case vm.EXTCODECOPY: gp = 0 - case EXTCODESIZE: + case vm.EXTCODESIZE: gp = 0 - case EXTCODEHASH: + case vm.EXTCODEHASH: gp = 0 - case BALANCE: + case vm.BALANCE: gp = 0 - case CALL: + case vm.CALL: gp = 0 - case CALLCODE: + case vm.CALLCODE: gp = 0 - case STATICCALL: + case vm.STATICCALL: gp = 0 - case DELEGATECALL: + case vm.DELEGATECALL: gp = 0 } return gp @@ -69,160 +99,158 @@ func getStaticGasPrices(revision tosca.Revision) *opCodePropertyMap[tosca.Gas] { return &static_gas_prices } -func getStaticGasPriceInternal(op OpCode) tosca.Gas { - if PUSH1 <= op && op <= PUSH32 { +func getStaticGasPriceInternal(op vm.OpCode) tosca.Gas { + if vm.PUSH1 <= op && op <= vm.PUSH32 { return 3 } - if DUP1 <= op && op <= DUP16 { + if vm.DUP1 <= op && op <= vm.DUP16 { return 3 } - if SWAP1 <= op && op <= SWAP16 { + if vm.SWAP1 <= op && op <= vm.SWAP16 { return 3 } // this range covers: LT, GT, SLT, SGT, EQ, ISZERO, // AND, OR, XOR, NOT, BYTE, SHL, SHR, SAR - if LT <= op && op <= SAR { + if vm.LT <= op && op <= vm.SAR { return 3 } // this range covers: COINBASE, TIMESTAMP, NUMBER, // DIFFICULTY/PREVRANDO, GAS, GASLIMIT, CHAINID - if COINBASE <= op && op <= CHAINID { + if vm.COINBASE <= op && op <= vm.CHAINID { return 2 } switch op { - case CLZ: + case vm.CLZ: return 5 - case POP: + case vm.POP: return 2 - case PUSH0: + case vm.PUSH0: return 2 - case ADD: + case vm.ADD: return 3 - case SUB: + case vm.SUB: return 3 - case MUL: + case vm.MUL: return 5 - case DIV: + case vm.DIV: return 5 - case SDIV: + case vm.SDIV: return 5 - case MOD: + case vm.MOD: return 5 - case SMOD: + case vm.SMOD: return 5 - case ADDMOD: + case vm.ADDMOD: return 8 - case MULMOD: + case vm.MULMOD: return 8 - case EXP: + case vm.EXP: return 10 - case SIGNEXTEND: + case vm.SIGNEXTEND: return 5 - case SHA3: + case vm.SHA3: return 30 - case ADDRESS: + case vm.ADDRESS: return 2 - case BALANCE: + case vm.BALANCE: return 700 // Should be 100 for warm access, 2600 for cold access - case ORIGIN: + case vm.ORIGIN: return 2 - case CALLER: + case vm.CALLER: return 2 - case CALLVALUE: + case vm.CALLVALUE: return 2 - case CALLDATALOAD: + case vm.CALLDATALOAD: return 3 - case CALLDATASIZE: + case vm.CALLDATASIZE: return 2 - case CALLDATACOPY: + case vm.CALLDATACOPY: return 3 - case CODESIZE: + case vm.CODESIZE: return 2 - case CODECOPY: + case vm.CODECOPY: return 3 - case GASPRICE: + case vm.GASPRICE: return 2 - case EXTCODESIZE: + case vm.EXTCODESIZE: return 700 // This seems to be different than documented on evm.codes (it should be 100) - case EXTCODECOPY: + case vm.EXTCODECOPY: return 700 // From EIP150 it is 700, was 20 - case RETURNDATASIZE: + case vm.RETURNDATASIZE: return 2 - case RETURNDATACOPY: + case vm.RETURNDATACOPY: return 3 - case EXTCODEHASH: + case vm.EXTCODEHASH: return 700 // Should be 100 for warm access, 2600 for cold access - case BLOCKHASH: + case vm.BLOCKHASH: return 20 - case SELFBALANCE: + case vm.SELFBALANCE: return 5 - case BASEFEE: + case vm.BASEFEE: return 2 - case BLOBHASH: + case vm.BLOBHASH: return 3 - case BLOBBASEFEE: + case vm.BLOBBASEFEE: return 2 - case MLOAD: + case vm.MLOAD: return 3 - case MSTORE: + case vm.MSTORE: return 3 - case MSTORE8: + case vm.MSTORE8: return 3 - case SLOAD: + case vm.SLOAD: return 800 // This is supposed to be 100 for warm and 2100 for cold accesses - case SSTORE: + case vm.SSTORE: return 0 // Costs are handled in gasSStore(..) function below - case JUMP: + case vm.JUMP: return 8 - case JUMPI: + case vm.JUMPI: return 10 - case JUMPDEST: + case vm.JUMPDEST: return 1 - case JUMP_TO: - return 0 - case TLOAD: + case vm.TLOAD: return 100 - case TSTORE: + case vm.TSTORE: return 100 - case PC: + case vm.PC: return 2 - case MSIZE: + case vm.MSIZE: return 2 - case MCOPY: + case vm.MCOPY: return 3 - case GAS: + case vm.GAS: return 2 - case LOG0: + case vm.LOG0: return 375 - case LOG1: + case vm.LOG1: return 750 - case LOG2: + case vm.LOG2: return 1125 - case LOG3: + case vm.LOG3: return 1500 - case LOG4: + case vm.LOG4: return 1875 - case CREATE: + case vm.CREATE: return 32000 - case CREATE2: + case vm.CREATE2: return 32000 - case CALL: + case vm.CALL: return 700 - case CALLCODE: + case vm.CALLCODE: return 700 - case STATICCALL: + case vm.STATICCALL: return 700 - case RETURN: + case vm.RETURN: return 0 - case STOP: + case vm.STOP: return 0 - case REVERT: + case vm.REVERT: return 0 - case INVALID: + case vm.INVALID: return 0 - case DELEGATECALL: + case vm.DELEGATECALL: return 700 - case SELFDESTRUCT: + case vm.SELFDESTRUCT: return 5000 } diff --git a/go/interpreter/sfvm/instruction.go b/go/interpreter/sfvm/instruction.go deleted file mode 100644 index 21b4c770..00000000 --- a/go/interpreter/sfvm/instruction.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "bytes" - "fmt" -) - -// Instruction encodes an instruction for the long-form virtual machine (SFVM). -type Instruction struct { - // The op-code of this instruction. - opcode OpCode - // An argument value for this instruction. - arg uint16 -} - -// Code for the SFVM is a slice of instructions. -type Code []Instruction - -func (i Instruction) String() string { - if i.opcode.HasArgument() { - return fmt.Sprintf("%v 0x%04x", i.opcode, i.arg) - } - return i.opcode.String() -} - -func (c Code) String() string { - var buffer bytes.Buffer - for i, instruction := range c { - buffer.WriteString(fmt.Sprintf("0x%04x: %v\n", i, instruction)) - } - return buffer.String() -} diff --git a/go/interpreter/sfvm/instruction_test.go b/go/interpreter/sfvm/instruction_test.go deleted file mode 100644 index 60dbdddd..00000000 --- a/go/interpreter/sfvm/instruction_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import "testing" - -func TestInstruction_String(t *testing.T) { - - tests := []struct { - instruction Instruction - want string - }{ - {Instruction{opcode: STOP, arg: 0x0000}, "STOP"}, - {Instruction{opcode: PUSH1, arg: 0x0001}, "PUSH1 0x0001"}, - {Instruction{opcode: PUSH2, arg: 0x0002}, "PUSH2 0x0002"}, - {Instruction{opcode: DATA, arg: 0x0002}, "DATA 0x0002"}, - {Instruction{opcode: JUMP_TO, arg: 0x0002}, "JUMP_TO 0x0002"}, - } - - for _, tt := range tests { - if got := tt.instruction.String(); got != tt.want { - t.Errorf("Instruction.String() = %q, want %q", got, tt.want) - } - } - -} - -func TestCode_String(t *testing.T) { - code := Code{ - Instruction{opcode: STOP, arg: 0x0000}, - Instruction{opcode: PUSH1, arg: 0x0001}, - } - if got, want := code.String(), "0x0000: STOP\n0x0001: PUSH1 0x0001\n"; got != want { - t.Errorf("Code.String() = %q, want %q", got, want) - } -} diff --git a/go/interpreter/sfvm/instructions.go b/go/interpreter/sfvm/instructions.go index 67b7f2a9..64f8c9e1 100644 --- a/go/interpreter/sfvm/instructions.go +++ b/go/interpreter/sfvm/instructions.go @@ -31,13 +31,11 @@ func opEndWithResult(c *context) error { } func opPc(c *context) { - c.stack.pushUndefined().SetUint64(uint64(c.code[c.pc].arg)) + c.stack.pushUndefined().SetUint64(uint64(c.pc)) } func checkJumpDest(c *context) error { - if int(c.pc+1) >= len(c.code) || c.code[c.pc+1].opcode != JUMPDEST { - return errInvalidJump - } + // TODO: use bitmap for jumpdest analysis return nil } @@ -67,31 +65,38 @@ func opJumpi(c *context) error { return nil } -func opJumpTo(c *context) { - // Update the PC to the jump destination -1 since interpreter will increase PC by 1 afterward. - c.pc = int32(c.code[c.pc].arg) - 1 -} - func opPop(c *context) { c.stack.pop() } func opPush(c *context, n int) { z := c.stack.pushUndefined() - num_instructions := int32(n/2 + n%2) - data := c.code[c.pc : c.pc+num_instructions] + z[3], z[2], z[1], z[0] = 0, 0, 0, 0 + if len(c.code) <= int(c.pc)+n { + c.pc += int32(n) + return + } - _ = data[num_instructions-1] - var value [32]byte - for i := 0; i < n; i++ { - if i%2 == 0 { - value[i] = byte(data[i/2].arg >> 8) - } else { - value[i] = byte(data[i/2].arg) + // check how many uint64 (8 bytes) will be filled + fills := n / 8 + if n%8 != 0 { + fills++ + } + valueOffset := fills - 1 + + // Calculate the initial shiftOffset for the first filled uint64 + shiftOffset := ((n - 1) % 8) * 8 + for i := range n { + z[valueOffset] |= uint64(c.code[int(c.pc)+1+i]) << shiftOffset + shiftOffset -= 8 + + // if the shiftOffset is negative, we need to fill the next uint64 + if shiftOffset < 0 { + valueOffset-- + shiftOffset = 56 } } - z.SetBytes(value[0:n]) - c.pc += num_instructions - 1 + c.pc += int32(n) } func opPush0(c *context) error { @@ -106,44 +111,62 @@ func opPush0(c *context) error { func opPush1(c *context) { z := c.stack.pushUndefined() z[3], z[2], z[1] = 0, 0, 0 - z[0] = uint64(c.code[c.pc].arg >> 8) + if len(c.code) <= int(c.pc)+1 { + z[0] = 0 + } else { + z[0] = uint64(c.code[c.pc+1]) + } + c.pc += 1 } func opPush2(c *context) { z := c.stack.pushUndefined() z[3], z[2], z[1] = 0, 0, 0 - z[0] = uint64(c.code[c.pc].arg) + if len(c.code) <= int(c.pc)+2 { + z[0] = 0 + } else { + z[0] = uint64(c.code[c.pc+1])<<8 | uint64(c.code[c.pc+2]) + } + c.pc += 2 } func opPush3(c *context) { z := c.stack.pushUndefined() z[3], z[2], z[1] = 0, 0, 0 - data := c.code[c.pc : c.pc+2] - _ = data[1] - z[0] = uint64(data[0].arg)<<8 | uint64(data[1].arg>>8) - c.pc += 1 + if len(c.code) <= int(c.pc)+3 { + z[0] = 0 + } else { + z[0] = uint64(c.code[c.pc+1])<<16 | uint64(c.code[c.pc+2])<<8 | uint64(c.code[c.pc+3]) + } + c.pc += 3 } func opPush4(c *context) { z := c.stack.pushUndefined() z[3], z[2], z[1] = 0, 0, 0 - - data := c.code[c.pc : c.pc+2] - _ = data[1] // causes bound check to be performed only once (may become unneeded in the future) - z[0] = (uint64(data[0].arg) << 16) | uint64(data[1].arg) - c.pc += 1 + if len(c.code) <= int(c.pc)+4 { + z[0] = 0 + } else { + z[0] = uint64(c.code[c.pc+1])<<24 | uint64(c.code[c.pc+2])<<16 | uint64(c.code[c.pc+3])<<8 | uint64(c.code[c.pc+4]) + } + c.pc += 4 } func opPush32(c *context) { z := c.stack.pushUndefined() - - data := c.code[c.pc : c.pc+16] - _ = data[15] // causes bound check to be performed only once (may become unneeded in the future) - z[3] = (uint64(data[0].arg) << 48) | (uint64(data[1].arg) << 32) | (uint64(data[2].arg) << 16) | uint64(data[3].arg) - z[2] = (uint64(data[4].arg) << 48) | (uint64(data[5].arg) << 32) | (uint64(data[6].arg) << 16) | uint64(data[7].arg) - z[1] = (uint64(data[8].arg) << 48) | (uint64(data[9].arg) << 32) | (uint64(data[10].arg) << 16) | uint64(data[11].arg) - z[0] = (uint64(data[12].arg) << 48) | (uint64(data[13].arg) << 32) | (uint64(data[14].arg) << 16) | uint64(data[15].arg) - c.pc += 15 + if len(c.code) <= int(c.pc)+32 { + z[3], z[2], z[1], z[0] = 0, 0, 0, 0 + } else { + z[3] = uint64(c.code[c.pc+1])<<56 | uint64(c.code[c.pc+2])<<48 | uint64(c.code[c.pc+3])<<40 | uint64(c.code[c.pc+4])<<32 | + uint64(c.code[c.pc+5])<<24 | uint64(c.code[c.pc+6])<<16 | uint64(c.code[c.pc+7])<<8 | uint64(c.code[c.pc+8]) + z[2] = uint64(c.code[c.pc+9])<<56 | uint64(c.code[c.pc+10])<<48 | uint64(c.code[c.pc+11])<<40 | uint64(c.code[c.pc+12])<<32 | + uint64(c.code[c.pc+13])<<24 | uint64(c.code[c.pc+14])<<16 | uint64(c.code[c.pc+15])<<8 | uint64(c.code[c.pc+16]) + z[1] = uint64(c.code[c.pc+17])<<56 | uint64(c.code[c.pc+18])<<48 | uint64(c.code[c.pc+19])<<40 | uint64(c.code[c.pc+20])<<32 | + uint64(c.code[c.pc+21])<<24 | uint64(c.code[c.pc+22])<<16 | uint64(c.code[c.pc+23])<<8 | uint64(c.code[c.pc+24]) + z[0] = uint64(c.code[c.pc+25])<<56 | uint64(c.code[c.pc+26])<<48 | uint64(c.code[c.pc+27])<<40 | uint64(c.code[c.pc+28])<<32 | + uint64(c.code[c.pc+29])<<24 | uint64(c.code[c.pc+30])<<16 | uint64(c.code[c.pc+31])<<8 | uint64(c.code[c.pc+32]) + } + c.pc += 32 } func opDup(c *context, pos int) { diff --git a/go/interpreter/sfvm/instructions_test.go b/go/interpreter/sfvm/instructions_test.go index 3580a942..3a3bb7f3 100644 --- a/go/interpreter/sfvm/instructions_test.go +++ b/go/interpreter/sfvm/instructions_test.go @@ -20,196 +20,11 @@ import ( "testing" "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" "github.com/holiman/uint256" "go.uber.org/mock/gomock" ) -func TestPushN(t *testing.T) { - data := make([]byte, 32) - for i := range data { - data[i] = byte(i + 1) - } - - code := make([]Instruction, 16) - for i := 0; i < 32; i++ { - code[i/2].arg = code[i/2].arg<<8 | uint16(data[i]) - } - - for n := 1; n <= 32; n++ { - ctxt := context{ - code: code, - stack: NewStack(), - } - - opPush(&ctxt, n) - ctxt.pc++ - - if ctxt.stack.len() != 1 { - t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) - return - } - - if int(ctxt.pc) != n/2+n%2 { - t.Errorf("for PUSH%d program counter did not progress to %d, got %d", n, n/2+n%2, ctxt.pc) - } - - got := ctxt.stack.peek().Bytes() - if len(got) != n { - t.Errorf("expected %d bytes on the stack, got %d with values %v", n, len(got), got) - } - - for i := range got { - if data[i] != got[i] { - t.Errorf("for PUSH%d expected value %d to be %d, got %d", n, i, data[i], got[i]) - } - } - } -} - -func TestPush1(t *testing.T) { - code := []Instruction{ - {opcode: PUSH1, arg: 0x1234}, - } - - ctxt := context{ - code: code, - stack: NewStack(), - } - - opPush1(&ctxt) - ctxt.pc++ - - if ctxt.stack.len() != 1 { - t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) - return - } - - if int(ctxt.pc) != 1 { - t.Errorf("program counter did not progress to %d, got %d", 1, ctxt.pc) - } - - got := ctxt.stack.peek().Bytes() - if len(got) != 1 { - t.Errorf("expected 1 byte on the stack, got %d with values %v", len(got), got) - } - if got[0] != 0x12 { - t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) - } -} - -func TestPush2(t *testing.T) { - code := []Instruction{ - {opcode: PUSH2, arg: 0x1234}, - } - - ctxt := context{ - code: code, - stack: NewStack(), - } - - opPush2(&ctxt) - ctxt.pc++ - - if ctxt.stack.len() != 1 { - t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) - return - } - - if int(ctxt.pc) != 1 { - t.Errorf("program counter did not progress to %d, got %d", 1, ctxt.pc) - } - - got := ctxt.stack.peek().Bytes() - if len(got) != 2 { - t.Errorf("expected 2 byte on the stack, got %d with values %v", len(got), got) - } - if got[0] != 0x12 { - t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) - } - if got[1] != 0x34 { - t.Errorf("expected %d for second byte, got %d", 0x34, got[1]) - } -} - -func TestPush3(t *testing.T) { - code := []Instruction{ - {opcode: PUSH2, arg: 0x1234}, - {opcode: DATA, arg: 0x5678}, - } - - ctxt := context{ - code: code, - stack: NewStack(), - } - - opPush3(&ctxt) - ctxt.pc++ - - if ctxt.stack.len() != 1 { - t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) - return - } - - if int(ctxt.pc) != 2 { - t.Errorf("program counter did not progress to %d, got %d", 2, ctxt.pc) - } - - got := ctxt.stack.peek().Bytes() - if len(got) != 3 { - t.Errorf("expected 3 byte on the stack, got %d with values %v", len(got), got) - } - if got[0] != 0x12 { - t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) - } - if got[1] != 0x34 { - t.Errorf("expected %d for second byte, got %d", 0x34, got[1]) - } - if got[2] != 0x56 { - t.Errorf("expected %d for third byte, got %d", 0x56, got[2]) - } -} - -func TestPush4(t *testing.T) { - code := []Instruction{ - {opcode: PUSH2, arg: 0x1234}, - {opcode: DATA, arg: 0x5678}, - } - - ctxt := context{ - code: code, - stack: NewStack(), - } - - opPush4(&ctxt) - ctxt.pc++ - - if ctxt.stack.len() != 1 { - t.Errorf("expected stack size of 1, got %d", ctxt.stack.len()) - return - } - - if int(ctxt.pc) != 2 { - t.Errorf("program counter did not progress to %d, got %d", 2, ctxt.pc) - } - - got := ctxt.stack.peek().Bytes() - if len(got) != 4 { - t.Errorf("expected 3 byte on the stack, got %d with values %v", len(got), got) - } - if got[0] != 0x12 { - t.Errorf("expected %d for first byte, got %d", 0x12, got[0]) - } - if got[1] != 0x34 { - t.Errorf("expected %d for second byte, got %d", 0x34, got[1]) - } - if got[2] != 0x56 { - t.Errorf("expected %d for third byte, got %d", 0x56, got[2]) - } - if got[3] != 0x78 { - t.Errorf("expected %d for 4th byte, got %d", 0x78, got[3]) - } -} - func TestCallChecksBalances(t *testing.T) { ctrl := gomock.NewController(t) runContext := tosca.NewMockRunContext(ctrl) @@ -283,6 +98,27 @@ func TestCreateChecksBalance(t *testing.T) { } } +func TestPush_ReadingDataLongerThanCodePushesZero(t *testing.T) { + nonSpecializedPush := make([]func(*context), 0, 27) + for i := 5; i <= 31; i++ { + nonSpecializedPush = append(nonSpecializedPush, func(c *context) { opPush(c, i) }) + } + + pushes := []func(c *context){opPush1, opPush2, opPush3, opPush4, opPush32} + pushes = append(pushes, nonSpecializedPush...) + for _, op := range pushes { + ctxt := context{ + stack: NewStack(), + } + + op(&ctxt) + + if !ctxt.stack.peek().Eq(uint256.NewInt(0)) { + t.Fatalf("unexpected value on top of stack, wanted %v, got %v", uint256.NewInt(0), ctxt.stack.peek()) + } + } +} + func TestBlobHash_PushesCorrectValueOnStack(t *testing.T) { hash := tosca.Hash{1} @@ -1023,7 +859,9 @@ func TestOpEndWithResult_ReportOverflow(t *testing.T) { } func TestInstructions_EIP2929_staticGasCostIsZero(t *testing.T) { - ops := []OpCode{BALANCE, EXTCODECOPY, EXTCODEHASH, EXTCODESIZE, CALL, CALLCODE, DELEGATECALL, STATICCALL} + ops := []vm.OpCode{ + vm.BALANCE, vm.EXTCODECOPY, vm.EXTCODEHASH, vm.EXTCODESIZE, vm.CALL, vm.CALLCODE, vm.DELEGATECALL, vm.STATICCALL, + } for _, op := range ops { if getBerlinGasPriceInternal(op) != 0 { t.Errorf("expected zero gas cost for %v", op) @@ -1037,26 +875,26 @@ func TestInstructions_EIP2929_dynamicGasCostReportsOutOfGas(t *testing.T) { cold tosca.Gas } - var eip2929AccessCost = newOpCodePropertyMap(func(op OpCode) accessCost { + var eip2929AccessCost = newOpCodePropertyMap(func(op vm.OpCode) accessCost { switch op { - case SLOAD: + case vm.SLOAD: return accessCost{warm: 100, cold: 2100} - case SSTORE: + case vm.SSTORE: return accessCost{warm: 100, cold: 2100 + 100} } return accessCost{warm: 100, cold: 2600} }) - tests := map[OpCode]func(*context) error{ - BALANCE: opBalance, - EXTCODECOPY: opExtCodeCopy, - EXTCODEHASH: opExtcodehash, - EXTCODESIZE: opExtcodesize, - CALL: opCall, - CALLCODE: opCallCode, - DELEGATECALL: opDelegateCall, - STATICCALL: opStaticCall, - SLOAD: opSload, + tests := map[vm.OpCode]func(*context) error{ + vm.BALANCE: opBalance, + vm.EXTCODECOPY: opExtCodeCopy, + vm.EXTCODEHASH: opExtcodehash, + vm.EXTCODESIZE: opExtcodesize, + vm.CALL: opCall, + vm.CALLCODE: opCallCode, + vm.DELEGATECALL: opDelegateCall, + vm.STATICCALL: opStaticCall, + vm.SLOAD: opSload, } for op, implementation := range tests { @@ -1111,7 +949,7 @@ func TestInstructions_EIP2929_SSTOREReportsOutOfGas(t *testing.T) { for _, storageStatus := range failsForDynamicGas { for revision := tosca.R09_Berlin; revision <= newestSupportedRevision; revision++ { for _, access := range []tosca.AccessStatus{tosca.WarmAccess, tosca.ColdAccess} { - t.Run(fmt.Sprintf("%v/%v/%v/%v", SSTORE, revision, access, storageStatus), func(t *testing.T) { + t.Run(fmt.Sprintf("%v/%v/%v/%v", vm.SSTORE, revision, access, storageStatus), func(t *testing.T) { ctxt := context{ params: tosca.Parameters{ @@ -1148,17 +986,17 @@ func TestInstructions_StorageOps_CallStorageContext(t *testing.T) { value := tosca.Word{} _, _ = rand.Read(value[:]) - tests := map[OpCode]struct { + tests := map[vm.OpCode]struct { implementation func(*context) error stack []uint256.Int }{ - SLOAD: { + vm.SLOAD: { implementation: opSload, stack: []uint256.Int{ *new(uint256.Int).SetBytes(key[:]), }, }, - SSTORE: { + vm.SSTORE: { implementation: opSstore, stack: []uint256.Int{ *new(uint256.Int).SetBytes(key[:]), @@ -1179,10 +1017,10 @@ func TestInstructions_StorageOps_CallStorageContext(t *testing.T) { if revision >= tosca.R09_Berlin { runContext.EXPECT().AccessStorage(address, key).Return(tosca.WarmAccess) } - if op == SLOAD { + if op == vm.SLOAD { runContext.EXPECT().GetStorage(address, key).Return(value) } - if op == SSTORE { + if op == vm.SSTORE { runContext.EXPECT().SetStorage(address, key, value) } ctxt.context = runContext @@ -1192,7 +1030,7 @@ func TestInstructions_StorageOps_CallStorageContext(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - if op == SLOAD { + if op == vm.SLOAD { if got := ctxt.stack.peek(); got.Cmp(new(uint256.Int).SetBytes(value[:])) != 0 { t.Errorf("unexpected return value, wanted %v, got %v", value, got) } @@ -1202,16 +1040,17 @@ func TestInstructions_StorageOps_CallStorageContext(t *testing.T) { } } -func TestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { - tests := map[OpCode]struct { +// TODO reenable once SFVM supports JUMPDEST marking +func DisTestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { + tests := map[vm.OpCode]struct { implementation func(*context) error stack []uint64 }{ - JUMP: { + vm.JUMP: { implementation: opJump, stack: []uint64{1}, }, - JUMPI: { + vm.JUMPI: { implementation: opJumpi, stack: []uint64{1, 1}, }, @@ -1227,7 +1066,7 @@ func TestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { for op, test := range tests { t.Run(op.String(), func(t *testing.T) { ctxt := getEmptyContext() - ctxt.code = Code{{op, 0}} + ctxt.code = tosca.Code{byte(op)} for _, v := range test.stack { ctxt.stack.push(uint256.NewInt(v)) } @@ -1244,11 +1083,11 @@ func TestInstructions_ConditionalJumpOpsIgnoreDestinationWhenJumpNotTaken(t *tes zero := *uint256.NewInt(0) maxUint256 := *uint256.NewInt(0).Sub(uint256.NewInt(0), uint256.NewInt(1)) - tests := map[OpCode]struct { + tests := map[vm.OpCode]struct { implementation func(*context) error stack []uint256.Int }{ - JUMPI: { + vm.JUMPI: { implementation: opJumpi, // ignores destination, even if it would overflow stack: []uint256.Int{maxUint256, zero}, @@ -1258,7 +1097,7 @@ func TestInstructions_ConditionalJumpOpsIgnoreDestinationWhenJumpNotTaken(t *tes for op, test := range tests { t.Run(op.String(), func(t *testing.T) { ctxt := getEmptyContext() - ctxt.code = Code{{op, 0}} + ctxt.code = tosca.Code{byte(op)} ctxt.stack = fillStack(test.stack...) err := test.implementation(&ctxt) @@ -1270,17 +1109,17 @@ func TestInstructions_ConditionalJumpOpsIgnoreDestinationWhenJumpNotTaken(t *tes } func TestInstructions_JumpOpsReturnErrorWithJumpDestinationOutOfBounds(t *testing.T) { - tests := map[OpCode]struct { + tests := map[vm.OpCode]struct { implementation func(*context) error stack []uint256.Int }{ - JUMP: { + vm.JUMP: { implementation: opJump, stack: []uint256.Int{ *uint256.NewInt(math.MaxInt32 + 1), }, }, - JUMPI: { + vm.JUMPI: { implementation: opJumpi, stack: []uint256.Int{ *uint256.NewInt(math.MaxInt32 + 1), @@ -1292,7 +1131,7 @@ func TestInstructions_JumpOpsReturnErrorWithJumpDestinationOutOfBounds(t *testin for op, test := range tests { t.Run(op.String(), func(t *testing.T) { ctxt := getEmptyContext() - ctxt.code = Code{{op, 0}} + ctxt.code = tosca.Code{byte(op)} ctxt.stack = fillStack(test.stack...) err := test.implementation(&ctxt) @@ -2371,3 +2210,18 @@ func fillStack(values ...uint256.Int) *stack { } return s } + +func allOpCodesWhere(predicate func(op vm.OpCode) bool) []vm.OpCode { + res := []vm.OpCode{} + for idx := range numOpCodes { + op := vm.OpCode(idx) + if predicate(op) { + res = append(res, op) + } + } + return res +} + +func allOpCodes() []vm.OpCode { + return allOpCodesWhere(func(op vm.OpCode) bool { return true }) +} diff --git a/go/interpreter/sfvm/interpreter.go b/go/interpreter/sfvm/interpreter.go index 223b2e5c..176345ba 100644 --- a/go/interpreter/sfvm/interpreter.go +++ b/go/interpreter/sfvm/interpreter.go @@ -14,10 +14,9 @@ import ( "fmt" "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" ) -//go:generate mockgen -source interpreter.go -destination interpreter_mock.go -package sfvm - // status is enumeration of the execution state of an interpreter run. type status byte @@ -38,7 +37,7 @@ type context struct { // Inputs params tosca.Parameters context tosca.RunContext - code Code // the contract code in SFVM format + code tosca.Code // Execution state pc int32 @@ -72,24 +71,12 @@ func (c *context) isAtLeast(revision tosca.Revision) bool { return c.params.Revision >= revision } -// --- Interpreter --- - -type runner interface { - // run executes the contract code in the given context. - // It returns the status of the execution: - // - Any logical error in the contract execution shall return statusFailed. - // - error is reserved to return runtime errors, which are not valid states - // and may not be recoverable. - run(*context) (status, error) -} - func run( config config, params tosca.Parameters, - code Code, ) (tosca.Result, error) { // Don't bother with the execution if there's no code. - if len(code) == 0 { + if len(params.Code) == 0 { return tosca.Result{ Output: nil, GasLeft: params.Gas, @@ -104,19 +91,12 @@ func run( gas: params.Gas, stack: NewStack(), memory: NewMemory(), - code: code, + code: params.Code, withShaCache: config.WithShaCache, } defer ReturnStack(ctxt.stack) - if config.runner == nil { - config.runner = vanillaRunner{} - } - status, err := config.runner.run(&ctxt) - if err != nil { - return tosca.Result{}, err - } - + status := execute(&ctxt, false) return generateResult(status, &ctxt) } @@ -151,16 +131,6 @@ func generateResult(status status, ctxt *context) (tosca.Result, error) { } } -// --- Runners --- - -// vanillaRunner is the default runner that executes the contract code without -// any additional features. -type vanillaRunner struct{} - -func (r vanillaRunner) run(c *context) (status, error) { - return execute(c, false), nil -} - // --- Execution --- // execute runs the contract code in the given context. If oneStepOnly is true, @@ -189,7 +159,7 @@ func steps(c *context, oneStepOnly bool) (status, error) { return statusStopped, nil } - op := c.code[c.pc].opcode + op := vm.OpCode(c.code[c.pc]) // Check stack boundary for every instruction if err := checkStackLimits(c.stack.len(), op); err != nil { @@ -205,307 +175,305 @@ func steps(c *context, oneStepOnly bool) (status, error) { // Execute instruction switch op { - case POP: + case vm.POP: opPop(c) - case PUSH0: + case vm.PUSH0: err = opPush0(c) - case PUSH1: + case vm.PUSH1: opPush1(c) - case PUSH2: + case vm.PUSH2: opPush2(c) - case PUSH3: + case vm.PUSH3: opPush3(c) - case PUSH4: + case vm.PUSH4: opPush4(c) - case PUSH5: + case vm.PUSH5: opPush(c, 5) - case PUSH31: + case vm.PUSH31: opPush(c, 31) - case PUSH32: + case vm.PUSH32: opPush32(c) - case JUMP: + case vm.JUMP: err = opJump(c) - case JUMPDEST: + case vm.JUMPDEST: // nothing - case SWAP1: + case vm.SWAP1: opSwap(c, 1) - case SWAP2: + case vm.SWAP2: opSwap(c, 2) - case DUP3: + case vm.DUP3: opDup(c, 3) - case AND: + case vm.AND: opAnd(c) - case SWAP3: + case vm.SWAP3: opSwap(c, 3) - case JUMPI: + case vm.JUMPI: err = opJumpi(c) - case GT: + case vm.GT: opGt(c) - case DUP4: + case vm.DUP4: opDup(c, 4) - case DUP2: + case vm.DUP2: opDup(c, 2) - case ISZERO: + case vm.ISZERO: opIszero(c) - case ADD: + case vm.ADD: opAdd(c) - case OR: + case vm.OR: opOr(c) - case XOR: + case vm.XOR: opXor(c) - case NOT: + case vm.NOT: opNot(c) - case SUB: + case vm.SUB: opSub(c) - case MUL: + case vm.MUL: opMul(c) - case MULMOD: + case vm.MULMOD: opMulMod(c) - case DIV: + case vm.DIV: opDiv(c) - case SDIV: + case vm.SDIV: opSDiv(c) - case MOD: + case vm.MOD: opMod(c) - case SMOD: + case vm.SMOD: opSMod(c) - case ADDMOD: + case vm.ADDMOD: opAddMod(c) - case EXP: + case vm.EXP: err = opExp(c) - case DUP5: + case vm.DUP5: opDup(c, 5) - case DUP1: + case vm.DUP1: opDup(c, 1) - case EQ: + case vm.EQ: opEq(c) - case PC: + case vm.PC: opPc(c) - case CALLER: + case vm.CALLER: opCaller(c) - case CALLDATALOAD: + case vm.CALLDATALOAD: opCallDataload(c) - case CALLDATASIZE: + case vm.CALLDATASIZE: opCallDatasize(c) - case CALLDATACOPY: + case vm.CALLDATACOPY: err = genericDataCopy(c, c.params.Input) - case MLOAD: + case vm.MLOAD: err = opMload(c) - case MSTORE: + case vm.MSTORE: err = opMstore(c) - case MSTORE8: + case vm.MSTORE8: err = opMstore8(c) - case MSIZE: + case vm.MSIZE: opMsize(c) - case MCOPY: + case vm.MCOPY: err = opMcopy(c) - case LT: + case vm.LT: opLt(c) - case SLT: + case vm.SLT: opSlt(c) - case SGT: + case vm.SGT: opSgt(c) - case SHR: + case vm.SHR: opShr(c) - case SHL: + case vm.SHL: opShl(c) - case SAR: + case vm.SAR: opSar(c) - case CLZ: + case vm.CLZ: err = opClz(c) - case SIGNEXTEND: + case vm.SIGNEXTEND: opSignExtend(c) - case BYTE: + case vm.BYTE: opByte(c) - case SHA3: + case vm.SHA3: err = opSha3(c) - case CALLVALUE: + case vm.CALLVALUE: opCallvalue(c) - case PUSH6: + case vm.PUSH6: opPush(c, 6) - case PUSH7: + case vm.PUSH7: opPush(c, 7) - case PUSH8: + case vm.PUSH8: opPush(c, 8) - case PUSH9: + case vm.PUSH9: opPush(c, 9) - case PUSH10: + case vm.PUSH10: opPush(c, 10) - case PUSH11: + case vm.PUSH11: opPush(c, 11) - case PUSH12: + case vm.PUSH12: opPush(c, 12) - case PUSH13: + case vm.PUSH13: opPush(c, 13) - case PUSH14: + case vm.PUSH14: opPush(c, 14) - case PUSH15: + case vm.PUSH15: opPush(c, 15) - case PUSH16: + case vm.PUSH16: opPush(c, 16) - case PUSH17: + case vm.PUSH17: opPush(c, 17) - case PUSH18: + case vm.PUSH18: opPush(c, 18) - case PUSH19: + case vm.PUSH19: opPush(c, 19) - case PUSH20: + case vm.PUSH20: opPush(c, 20) - case PUSH21: + case vm.PUSH21: opPush(c, 21) - case PUSH22: + case vm.PUSH22: opPush(c, 22) - case PUSH23: + case vm.PUSH23: opPush(c, 23) - case PUSH24: + case vm.PUSH24: opPush(c, 24) - case PUSH25: + case vm.PUSH25: opPush(c, 25) - case PUSH26: + case vm.PUSH26: opPush(c, 26) - case PUSH27: + case vm.PUSH27: opPush(c, 27) - case PUSH28: + case vm.PUSH28: opPush(c, 28) - case PUSH29: + case vm.PUSH29: opPush(c, 29) - case PUSH30: + case vm.PUSH30: opPush(c, 30) - case SWAP4: + case vm.SWAP4: opSwap(c, 4) - case SWAP5: + case vm.SWAP5: opSwap(c, 5) - case SWAP6: + case vm.SWAP6: opSwap(c, 6) - case SWAP7: + case vm.SWAP7: opSwap(c, 7) - case SWAP8: + case vm.SWAP8: opSwap(c, 8) - case SWAP9: + case vm.SWAP9: opSwap(c, 9) - case SWAP10: + case vm.SWAP10: opSwap(c, 10) - case SWAP11: + case vm.SWAP11: opSwap(c, 11) - case SWAP12: + case vm.SWAP12: opSwap(c, 12) - case SWAP13: + case vm.SWAP13: opSwap(c, 13) - case SWAP14: + case vm.SWAP14: opSwap(c, 14) - case SWAP15: + case vm.SWAP15: opSwap(c, 15) - case SWAP16: + case vm.SWAP16: opSwap(c, 16) - case DUP6: + case vm.DUP6: opDup(c, 6) - case DUP7: + case vm.DUP7: opDup(c, 7) - case DUP8: + case vm.DUP8: opDup(c, 8) - case DUP9: + case vm.DUP9: opDup(c, 9) - case DUP10: + case vm.DUP10: opDup(c, 10) - case DUP11: + case vm.DUP11: opDup(c, 11) - case DUP12: + case vm.DUP12: opDup(c, 12) - case DUP13: + case vm.DUP13: opDup(c, 13) - case DUP14: + case vm.DUP14: opDup(c, 14) - case DUP15: + case vm.DUP15: opDup(c, 15) - case DUP16: + case vm.DUP16: opDup(c, 16) - case RETURN: + case vm.RETURN: err = opEndWithResult(c) status = statusReturned - case REVERT: + case vm.REVERT: status = statusReverted err = opEndWithResult(c) - case JUMP_TO: - opJumpTo(c) - case SLOAD: + case vm.SLOAD: err = opSload(c) - case SSTORE: + case vm.SSTORE: err = opSstore(c) - case TLOAD: + case vm.TLOAD: err = opTload(c) - case TSTORE: + case vm.TSTORE: err = opTstore(c) - case CODESIZE: + case vm.CODESIZE: opCodeSize(c) - case CODECOPY: + case vm.CODECOPY: err = genericDataCopy(c, c.params.Code) - case EXTCODESIZE: + case vm.EXTCODESIZE: err = opExtcodesize(c) - case EXTCODEHASH: + case vm.EXTCODEHASH: err = opExtcodehash(c) - case EXTCODECOPY: + case vm.EXTCODECOPY: err = opExtCodeCopy(c) - case BALANCE: + case vm.BALANCE: err = opBalance(c) - case SELFBALANCE: + case vm.SELFBALANCE: opSelfbalance(c) - case BASEFEE: + case vm.BASEFEE: err = opBaseFee(c) - case BLOBHASH: + case vm.BLOBHASH: err = opBlobHash(c) - case BLOBBASEFEE: + case vm.BLOBBASEFEE: err = opBlobBaseFee(c) - case SELFDESTRUCT: + case vm.SELFDESTRUCT: status, err = opSelfdestruct(c) - case CHAINID: + case vm.CHAINID: opChainId(c) - case GAS: + case vm.GAS: opGas(c) - case PREVRANDAO: + case vm.PREVRANDAO: opPrevRandao(c) - case TIMESTAMP: + case vm.TIMESTAMP: opTimestamp(c) - case NUMBER: + case vm.NUMBER: opNumber(c) - case GASLIMIT: + case vm.GASLIMIT: opGasLimit(c) - case GASPRICE: + case vm.GASPRICE: opGasPrice(c) - case CALL: + case vm.CALL: err = opCall(c) - case CALLCODE: + case vm.CALLCODE: err = opCallCode(c) - case STATICCALL: + case vm.STATICCALL: err = opStaticCall(c) - case DELEGATECALL: + case vm.DELEGATECALL: err = opDelegateCall(c) - case RETURNDATASIZE: + case vm.RETURNDATASIZE: opReturnDataSize(c) - case RETURNDATACOPY: + case vm.RETURNDATACOPY: err = opReturnDataCopy(c) - case BLOCKHASH: + case vm.BLOCKHASH: opBlockhash(c) - case COINBASE: + case vm.COINBASE: opCoinbase(c) - case ORIGIN: + case vm.ORIGIN: opOrigin(c) - case ADDRESS: + case vm.ADDRESS: opAddress(c) - case STOP: + case vm.STOP: status = opStop() - case CREATE: + case vm.CREATE: err = genericCreate(c, tosca.Create) - case CREATE2: + case vm.CREATE2: err = genericCreate(c, tosca.Create2) - case LOG0: + case vm.LOG0: err = opLog(c, 0) - case LOG1: + case vm.LOG1: err = opLog(c, 1) - case LOG2: + case vm.LOG2: err = opLog(c, 2) - case LOG3: + case vm.LOG3: err = opLog(c, 3) - case LOG4: + case vm.LOG4: err = opLog(c, 4) default: err = errInvalidOpCode @@ -526,7 +494,7 @@ func steps(c *context, oneStepOnly bool) (status, error) { // checkStackLimits checks that the opCode will not make an out of bounds access // with the current stack size. -func checkStackLimits(stackLen int, op OpCode) error { +func checkStackLimits(stackLen int, op vm.OpCode) error { limits := _precomputedStackLimits.get(op) if stackLen < limits.min { return errStackUnderflow @@ -543,7 +511,7 @@ type stackLimits struct { max int // The maximum stack size allowed before running an OpCode. } -var _precomputedStackLimits = newOpCodePropertyMap(func(op OpCode) stackLimits { +var _precomputedStackLimits = newOpCodePropertyMap(func(op vm.OpCode) stackLimits { usage := computeStackUsage(op) return stackLimits{ min: -usage.from, diff --git a/go/interpreter/sfvm/interpreter_mock.go b/go/interpreter/sfvm/interpreter_mock.go deleted file mode 100644 index 9a87586a..00000000 --- a/go/interpreter/sfvm/interpreter_mock.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -// Code generated by MockGen. DO NOT EDIT. -// Source: interpreter.go -// -// Generated by this command: -// -// mockgen -source interpreter.go -destination interpreter_mock.go -package sfvm -// - -// Package sfvm is a generated GoMock package. -package sfvm - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// Mockrunner is a mock of runner interface. -type Mockrunner struct { - ctrl *gomock.Controller - recorder *MockrunnerMockRecorder -} - -// MockrunnerMockRecorder is the mock recorder for Mockrunner. -type MockrunnerMockRecorder struct { - mock *Mockrunner -} - -// NewMockrunner creates a new mock instance. -func NewMockrunner(ctrl *gomock.Controller) *Mockrunner { - mock := &Mockrunner{ctrl: ctrl} - mock.recorder = &MockrunnerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *Mockrunner) EXPECT() *MockrunnerMockRecorder { - return m.recorder -} - -// run mocks base method. -func (m *Mockrunner) run(arg0 *context) (status, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "run", arg0) - ret0, _ := ret[0].(status) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// run indicates an expected call of run. -func (mr *MockrunnerMockRecorder) run(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "run", reflect.TypeOf((*Mockrunner)(nil).run), arg0) -} diff --git a/go/interpreter/sfvm/interpreter_test.go b/go/interpreter/sfvm/interpreter_test.go index fb19e3fa..25985ab6 100644 --- a/go/interpreter/sfvm/interpreter_test.go +++ b/go/interpreter/sfvm/interpreter_test.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" "github.com/holiman/uint256" "go.uber.org/mock/gomock" ) @@ -105,12 +106,12 @@ const MAX_STACK_SIZE int = 1024 const GAS_START tosca.Gas = 1 << 32 func getEmptyContext() context { - code := make([]Instruction, 0) + code := make([]byte, 0) data := make([]byte, 0) return getContext(code, data, nil, 0, GAS_START, tosca.R07_Istanbul) } -func getContext(code Code, data []byte, runContext tosca.RunContext, stackPtr int, gas tosca.Gas, revision tosca.Revision) context { +func getContext(code tosca.Code, data []byte, runContext tosca.RunContext, stackPtr int, gas tosca.Gas, revision tosca.Revision) context { // Create execution context. ctxt := context{ @@ -148,7 +149,7 @@ func TestInterpreter_step_DetectsLowerStackLimitViolation(t *testing.T) { } ctxt := getEmptyContext() - ctxt.code = []Instruction{{op, 0}} + ctxt.code = []byte{byte(op)} _, err := steps(&ctxt, false) if want, got := errStackUnderflow, err; want != got { @@ -167,7 +168,7 @@ func TestInterpreter_step_DetectsUpperStackLimitViolation(t *testing.T) { } ctxt := getEmptyContext() - ctxt.code = []Instruction{{op, 0}} + ctxt.code = []byte{byte(op)} ctxt.stack.stackPointer = maxStackSize _, err := steps(&ctxt, false) @@ -177,8 +178,8 @@ func TestInterpreter_step_DetectsUpperStackLimitViolation(t *testing.T) { } } -func TestInterpreter_CanDispatchExecutableInstructions(t *testing.T) { - +// TODO reenable once SFVM supports JUMPDEST marking +func DisTestInterpreter_CanDispatchExecutableInstructions(t *testing.T) { for _, op := range allOpCodesWhere(isExecutable) { t.Run(op.String(), func(t *testing.T) { forEachRevision(t, op, func(t *testing.T, revision tosca.Revision) { @@ -219,9 +220,13 @@ func TestInterpreter_CanDispatchExecutableInstructions(t *testing.T) { t.Fatalf("unexpected creating stack: %v", err) } - _, err = vanillaRunner{}.run(&ctx) - if err != nil { - t.Errorf("execution failed: %v", err) + _, err = steps(&ctx, false) + if op == vm.JUMP || op == vm.JUMPI { + if !errors.Is(err, errInvalidJump) { + t.Errorf("expected invalid jump error for %v, got %v", op, err) + } + } else if err != nil { + t.Errorf("unexpected error: %v", err) } }) }) @@ -231,14 +236,14 @@ func TestInterpreter_CanDispatchExecutableInstructions(t *testing.T) { func TestInterpreter_ExecutionTerminates(t *testing.T) { tests := map[string]struct { - code []Instruction + code tosca.Code }{ - "empty code": {code: []Instruction{}}, - "single stop": {code: []Instruction{{STOP, 0}}}, - "pc bigger than code": {code: []Instruction{{PUSH1, 0}}}, - "revert": {code: []Instruction{{REVERT, 0}}}, - "return": {code: []Instruction{{RETURN, 0}}}, - "selfdestruct": {code: []Instruction{{SELFDESTRUCT, 0}}}, + "empty code": {code: tosca.Code{}}, + "single stop": {code: tosca.Code{byte(vm.STOP)}}, + "pc bigger than code": {code: tosca.Code{byte(vm.PUSH1)}}, + "revert": {code: tosca.Code{byte(vm.REVERT)}}, + "return": {code: tosca.Code{byte(vm.RETURN)}}, + "selfdestruct": {code: tosca.Code{byte(vm.SELFDESTRUCT)}}, } for name, test := range tests { @@ -269,16 +274,16 @@ func TestInterpreter_ExecutionTerminates(t *testing.T) { func TestInterpreter_Vanilla_RunsWithoutOutput(t *testing.T) { - code := []Instruction{ - {PUSH1, 1}, - {STOP, 0}, + code := tosca.Code{ + byte(vm.PUSH1), byte(1), + byte(vm.STOP), } params := tosca.Parameters{ Input: []byte{}, Static: true, Gas: 10, - Code: []byte{0x0}, + Code: code, } // redirect stdout @@ -287,7 +292,7 @@ func TestInterpreter_Vanilla_RunsWithoutOutput(t *testing.T) { os.Stdout = w // Run testing code - _, err := run(config{}, params, code) + _, err := run(config{}, params) // read the output _ = w.Close() // ignore error in test out, _ := io.ReadAll(r) @@ -303,13 +308,11 @@ func TestInterpreter_Vanilla_RunsWithoutOutput(t *testing.T) { } func TestInterpreter_EmptyCodeBypassesRunnerAndSucceeds(t *testing.T) { - code := []Instruction{} - params := tosca.Parameters{} - config := config{ - runner: NewMockrunner(gomock.NewController(t)), - } + code := tosca.Code{} + params := tosca.Parameters{Code: code} + config := config{} - result, err := run(config, params, code) + result, err := run(config, params) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -319,23 +322,6 @@ func TestInterpreter_EmptyCodeBypassesRunnerAndSucceeds(t *testing.T) { } } -func TestInterpreter_run_ReturnsErrorOnRuntimeError(t *testing.T) { - - runner := NewMockrunner(gomock.NewController(t)) - code := []Instruction{{JUMPDEST, 0}} - params := tosca.Parameters{Gas: 20} - config := config{runner: runner} - - expectedError := fmt.Errorf("runtime error") - - runner.EXPECT().run(gomock.Any()).Return(statusFailed, expectedError) - - _, err := run(config, params, code) - if !errors.Is(err, expectedError) { - t.Errorf("unexpected error: %v", err) - } -} - func TestRun_GenerateResult(t *testing.T) { baseOutput := []byte{0x1, 0x2, 0x3} @@ -416,41 +402,14 @@ func TestRun_GenerateResult(t *testing.T) { } } -func TestStepsProperlyHandlesJUMP_TO(t *testing.T) { - ctxt := getEmptyContext() - instructions := []Instruction{ - {JUMP_TO, 0x02}, - {RETURN, 0}, - {STOP, 0}, - } - - ctxt.params = tosca.Parameters{ - Input: []byte{}, - Static: false, - Gas: 10, - Code: []byte{0x0}, - } - ctxt.code = instructions - - status, err := steps(&ctxt, false) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if status != statusStopped { - t.Errorf("unexpected status: want STOPPED, got %v", status) - } -} - func TestSteps_DetectsNonExecutableCode(t *testing.T) { - nonExecutableOpCodes := []OpCode{ - INVALID, - NOOP, - DATA, + nonExecutableOpCodes := []vm.OpCode{ + vm.INVALID, } undefinedOpCodeRegex := regexp.MustCompile(`^op\(0x[0-9a-fA-F]+\)`) isUndefined := - func(op OpCode) bool { + func(op vm.OpCode) bool { return undefinedOpCodeRegex.MatchString(op.String()) } nonExecutableOpCodes = append(nonExecutableOpCodes, allOpCodesWhere(isUndefined)...) @@ -458,7 +417,7 @@ func TestSteps_DetectsNonExecutableCode(t *testing.T) { for _, opCode := range nonExecutableOpCodes { t.Run(opCode.String(), func(t *testing.T) { ctxt := getEmptyContext() - ctxt.code = []Instruction{{opCode, 0}} + ctxt.code = tosca.Code{byte(opCode)} _, err := steps(&ctxt, false) if want, got := errInvalidOpCode, err; want != got { @@ -470,25 +429,25 @@ func TestSteps_DetectsNonExecutableCode(t *testing.T) { func TestSteps_StaticContextViolation(t *testing.T) { tests := []struct { - op OpCode + op vm.OpCode stack []uint256.Int minRevision tosca.Revision }{ - {op: SSTORE}, - {op: LOG0}, - {op: LOG1}, - {op: LOG2}, - {op: LOG3}, - {op: LOG4}, - {op: CREATE}, - {op: CREATE2}, - {op: SELFDESTRUCT}, + {op: vm.SSTORE}, + {op: vm.LOG0}, + {op: vm.LOG1}, + {op: vm.LOG2}, + {op: vm.LOG3}, + {op: vm.LOG4}, + {op: vm.CREATE}, + {op: vm.CREATE2}, + {op: vm.SELFDESTRUCT}, { - op: TSTORE, + op: vm.TSTORE, minRevision: tosca.R13_Cancun, }, { - op: CALL, + op: vm.CALL, stack: []uint256.Int{ {}, {}, {}, {}, *uint256.NewInt(1), // value != 0: static violation @@ -507,7 +466,7 @@ func TestSteps_StaticContextViolation(t *testing.T) { Gas: 10, Code: []byte{0x0}, } - ctxt.code = []Instruction{{test.op, 0}} + ctxt.code = tosca.Code{byte(test.op)} ctxt.params.Revision = test.minRevision if len(test.stack) == 0 { @@ -528,7 +487,6 @@ func TestSteps_StaticContextViolation(t *testing.T) { } func TestSteps_FailsWithLessGasThanStaticCost(t *testing.T) { - for _, op := range allOpCodes() { t.Run(op.String(), func(t *testing.T) { forEachRevision(t, op, func(t *testing.T, revision tosca.Revision) { @@ -539,7 +497,7 @@ func TestSteps_FailsWithLessGasThanStaticCost(t *testing.T) { } ctxt := getEmptyContext() - ctxt.code = []Instruction{{op, 0}} + ctxt.code = tosca.Code{byte(op)} ctxt.stack.stackPointer = 20 ctxt.gas = expectedGas - 1 @@ -558,7 +516,7 @@ func TestInterpreter_InstructionsFailWhenExecutedInRevisionsEarlierThanIntroduce for revision := tosca.R07_Istanbul; revision < introducedIn; revision++ { t.Run(fmt.Sprintf("%v/%v", op, revision), func(t *testing.T) { ctxt := getEmptyContext() - ctxt.code = []Instruction{{op, 0}} + ctxt.code = tosca.Code{byte(op)} ctxt.params.Revision = revision ctxt.stack.stackPointer = 20 @@ -574,7 +532,7 @@ func TestInterpreter_InstructionsFailWhenExecutedInRevisionsEarlierThanIntroduce func TestInterpreter_ExecuteReturnsFailureOnExecutionError(t *testing.T) { ctxt := context{ - code: generateCodeFor(INVALID), + code: generateCodeFor(vm.INVALID), stack: NewStack(), } @@ -588,7 +546,11 @@ func TestInterpreter_ExecuteReturnsFailureOnExecutionError(t *testing.T) { // Benchmarks func BenchmarkFib10(b *testing.B) { - benchmarkFib(b, 10) + benchmarkFib(b, 10, false) +} + +func BenchmarkFib10_SI(b *testing.B) { + benchmarkFib(b, 10, true) } func BenchmarkSatisfiesStackRequirements(b *testing.B) { @@ -606,15 +568,15 @@ func BenchmarkSatisfiesStackRequirements(b *testing.B) { // test utilities func Test_generateCodeForOps(t *testing.T) { - tests := map[OpCode]int{ - PUSH1: 1, - PUSH2: 1, - PUSH3: 2, - PUSH4: 2, - PUSH5: 3, - PUSH6: 3, - PUSH31: 16, - PUSH32: 16, + tests := map[vm.OpCode]int{ + vm.PUSH1: 2, + vm.PUSH2: 3, + vm.PUSH3: 4, + vm.PUSH4: 5, + vm.PUSH5: 6, + vm.PUSH6: 7, + vm.PUSH31: 32, + vm.PUSH32: 33, } for op, test := range tests { t.Run(op.String(), func(t *testing.T) { @@ -629,27 +591,18 @@ func Test_generateCodeForOps(t *testing.T) { // generateCodeFor generates valid SFVM code for one instruction. // Appends necessary DATA instructions to the code to satisfy stack requirements. // Adds JUMPDEST instruction after JUMP instructions. -func generateCodeFor(op OpCode) Code { - - var code []Instruction - code = append(code, Instruction{op, 0}) +func generateCodeFor(op vm.OpCode) tosca.Code { + var code tosca.Code + code = append(code, byte(op)) - if PUSH3 <= op && op <= PUSH32 { - n := int(op) - int(PUSH3) + 3 - numInstructions := n/2 + n%2 - for i := 0; i < numInstructions-1; i++ { - code = append(code, Instruction{DATA, 0}) + if op >= vm.PUSH1 && op <= vm.PUSH32 { + for i := 0; i < int(op-vm.PUSH1+1); i++ { + code = append(code, 0x42) } } if isJump(op) { - code = append(code, Instruction{JUMPDEST, 0}) - } - - if op == JUMP_TO { - // prevent endless loop by having jump to itself - code[0].arg = uint16(len(code)) - code = append(code, Instruction{JUMPDEST, 0}) + code = append(code, byte(vm.JUMPDEST)) } return code @@ -657,14 +610,14 @@ func generateCodeFor(op OpCode) Code { // fillStackFor fills the stack with the required number of elements for the given opcode. // For Jump instructions, it also encodes the PC for the the first jump destination found in code -func fillStackFor(op OpCode, stack *stack, code Code) error { +func fillStackFor(op vm.OpCode, stack *stack, code tosca.Code) error { limits := _precomputedStackLimits.get(op) stack.stackPointer = limits.min // jump instructions need a valid jump destination if isJump(op) { - counter := slices.IndexFunc(code, func(v Instruction) bool { - return v.opcode == JUMPDEST + counter := slices.IndexFunc(code, func(v byte) bool { + return v == byte(vm.JUMPDEST) }) if counter == -1 { return fmt.Errorf("missing JUMPDEST instruction") @@ -680,25 +633,19 @@ func fillStackFor(op OpCode, stack *stack, code Code) error { var _isUndefinedOpCodeRegex = regexp.MustCompile(`^op\(0x[0-9A-Fa-f]+\)$`) -func isExecutable(op OpCode) bool { - if slices.Contains([]OpCode{INVALID, NOOP, DATA}, op) { +func isExecutable(op vm.OpCode) bool { + if slices.Contains([]vm.OpCode{vm.INVALID}, op) { return false } return !_isUndefinedOpCodeRegex.MatchString(op.String()) } -func isJump(op OpCode) bool { - return op == JUMP || op == JUMPI +func isJump(op vm.OpCode) bool { + return op == vm.JUMP || op == vm.JUMPI } -func benchmarkFib(b *testing.B, arg int) { +func benchmarkFib(b *testing.B, arg int, with_super_instructions bool) { example := getFibExample() - - // Convert example to SFVM format. - converted := convert(example.code, ConversionConfig{}) - - // Create input data. - // See details of argument encoding: t.ly/kBl6 data := make([]byte, 4+32) // < the parameter is padded up to 32 bytes @@ -721,7 +668,7 @@ func benchmarkFib(b *testing.B, arg int) { Static: true, }, gas: 1 << 62, - code: converted, + code: example.code, stack: NewStack(), memory: NewMemory(), } @@ -736,11 +683,7 @@ func benchmarkFib(b *testing.B, arg int) { ctxt.stack.stackPointer = 0 // Run the code (actual benchmark). - status, err := vanillaRunner{}.run(&ctxt) - if err != nil { - b.Fatalf("execution failed: %v", err) - } - + status := execute(&ctxt, false) if status != statusReturned { b.Fatalf("execution failed: status is %v", status) } @@ -775,24 +718,24 @@ func fib(x int) int { return fib(x-1) + fib(x-2) } -var _introducedIn = newOpCodePropertyMap(func(op OpCode) tosca.Revision { +var _introducedIn = newOpCodePropertyMap(func(op vm.OpCode) tosca.Revision { switch op { - case CLZ: - return tosca.R15_Osaka - case BASEFEE: + case vm.BASEFEE: return tosca.R10_London - case PUSH0: + case vm.PUSH0: return tosca.R12_Shanghai - case BLOBHASH: + case vm.BLOBHASH: return tosca.R13_Cancun - case BLOBBASEFEE: + case vm.BLOBBASEFEE: return tosca.R13_Cancun - case TLOAD: + case vm.TLOAD: return tosca.R13_Cancun - case TSTORE: + case vm.TSTORE: return tosca.R13_Cancun - case MCOPY: + case vm.MCOPY: return tosca.R13_Cancun + case vm.CLZ: + return tosca.R15_Osaka } return tosca.R07_Istanbul }) @@ -801,7 +744,7 @@ var _introducedIn = newOpCodePropertyMap(func(op OpCode) tosca.Revision { // where the operation was introduced. // It creates a new testing scope to name the test after the revision. func forEachRevision( - t *testing.T, op OpCode, + t *testing.T, op vm.OpCode, f func(t *testing.T, revision tosca.Revision)) { for revision := tosca.R07_Istanbul; revision <= newestSupportedRevision; revision++ { diff --git a/go/interpreter/sfvm/opcode.go b/go/interpreter/sfvm/opcode.go deleted file mode 100644 index 876d10f4..00000000 --- a/go/interpreter/sfvm/opcode.go +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "fmt" - - "github.com/0xsoniclabs/tosca/go/tosca/vm" -) - -type OpCode uint16 - -// opCodeMask defines the relevant trailing bits of an OpCode. Any two OpCodes -// with the same value when masked with opCodeMask are considered equal. -// -// The motivation for this is that the long-form EVM has a number of OpCodes -// that are not part of the original EVM. For those, values beyond the range -// [0-255] of the EVM's single-byte OpCodes are used. To that end, the OpCode -// data type in the SFVM is increased to 16 bits. However, in several places -// maps from SFVM OpCodes to properties are required to provide efficient -// lookup tables for properties. To avoid the need to maintain tables of -// 2^16 entries, the number of relevant bits is reduced to 9. Any leading bits -// are ignored when comparing OpCodes. -const opCodeMask = 0x1ff - -// numOpCodes is the maximum number of OpCodes that can be defined. -const numOpCodes = opCodeMask + 1 - -// The following constants define the original EVM OpCodes, in the sfvm OpCode space. -const ( - // Stack operations - POP = OpCode(vm.POP) - PUSH0 = OpCode(vm.PUSH0) - PUSH1 = OpCode(vm.PUSH1) - PUSH2 = OpCode(vm.PUSH2) - PUSH3 = OpCode(vm.PUSH3) - PUSH4 = OpCode(vm.PUSH4) - PUSH5 = OpCode(vm.PUSH5) - PUSH6 = OpCode(vm.PUSH6) - PUSH7 = OpCode(vm.PUSH7) - PUSH8 = OpCode(vm.PUSH8) - PUSH9 = OpCode(vm.PUSH9) - PUSH10 = OpCode(vm.PUSH10) - PUSH11 = OpCode(vm.PUSH11) - PUSH12 = OpCode(vm.PUSH12) - PUSH13 = OpCode(vm.PUSH13) - PUSH14 = OpCode(vm.PUSH14) - PUSH15 = OpCode(vm.PUSH15) - PUSH16 = OpCode(vm.PUSH16) - PUSH17 = OpCode(vm.PUSH17) - PUSH18 = OpCode(vm.PUSH18) - PUSH19 = OpCode(vm.PUSH19) - PUSH20 = OpCode(vm.PUSH20) - PUSH21 = OpCode(vm.PUSH21) - PUSH22 = OpCode(vm.PUSH22) - PUSH23 = OpCode(vm.PUSH23) - PUSH24 = OpCode(vm.PUSH24) - PUSH25 = OpCode(vm.PUSH25) - PUSH26 = OpCode(vm.PUSH26) - PUSH27 = OpCode(vm.PUSH27) - PUSH28 = OpCode(vm.PUSH28) - PUSH29 = OpCode(vm.PUSH29) - PUSH30 = OpCode(vm.PUSH30) - PUSH31 = OpCode(vm.PUSH31) - PUSH32 = OpCode(vm.PUSH32) - - DUP1 = OpCode(vm.DUP1) - DUP2 = OpCode(vm.DUP2) - DUP3 = OpCode(vm.DUP3) - DUP4 = OpCode(vm.DUP4) - DUP5 = OpCode(vm.DUP5) - DUP6 = OpCode(vm.DUP6) - DUP7 = OpCode(vm.DUP7) - DUP8 = OpCode(vm.DUP8) - DUP9 = OpCode(vm.DUP9) - DUP10 = OpCode(vm.DUP10) - DUP11 = OpCode(vm.DUP11) - DUP12 = OpCode(vm.DUP12) - DUP13 = OpCode(vm.DUP13) - DUP14 = OpCode(vm.DUP14) - DUP15 = OpCode(vm.DUP15) - DUP16 = OpCode(vm.DUP16) - - SWAP1 = OpCode(vm.SWAP1) - SWAP2 = OpCode(vm.SWAP2) - SWAP3 = OpCode(vm.SWAP3) - SWAP4 = OpCode(vm.SWAP4) - SWAP5 = OpCode(vm.SWAP5) - SWAP6 = OpCode(vm.SWAP6) - SWAP7 = OpCode(vm.SWAP7) - SWAP8 = OpCode(vm.SWAP8) - SWAP9 = OpCode(vm.SWAP9) - SWAP10 = OpCode(vm.SWAP10) - SWAP11 = OpCode(vm.SWAP11) - SWAP12 = OpCode(vm.SWAP12) - SWAP13 = OpCode(vm.SWAP13) - SWAP14 = OpCode(vm.SWAP14) - SWAP15 = OpCode(vm.SWAP15) - SWAP16 = OpCode(vm.SWAP16) - - // Control flow - JUMP = OpCode(vm.JUMP) - JUMPI = OpCode(vm.JUMPI) - JUMPDEST = OpCode(vm.JUMPDEST) - RETURN = OpCode(vm.RETURN) - REVERT = OpCode(vm.REVERT) - PC = OpCode(vm.PC) - STOP = OpCode(vm.STOP) - - // Arithmetic - ADD = OpCode(vm.ADD) - SUB = OpCode(vm.SUB) - MUL = OpCode(vm.MUL) - DIV = OpCode(vm.DIV) - SDIV = OpCode(vm.SDIV) - MOD = OpCode(vm.MOD) - SMOD = OpCode(vm.SMOD) - ADDMOD = OpCode(vm.ADDMOD) - MULMOD = OpCode(vm.MULMOD) - EXP = OpCode(vm.EXP) - SIGNEXTEND = OpCode(vm.SIGNEXTEND) - - // Complex function - SHA3 = OpCode(vm.SHA3) - - // Comparison operations - LT = OpCode(vm.LT) - GT = OpCode(vm.GT) - SLT = OpCode(vm.SLT) - SGT = OpCode(vm.SGT) - EQ = OpCode(vm.EQ) - ISZERO = OpCode(vm.ISZERO) - - // Bit-pattern operations - AND = OpCode(vm.AND) - OR = OpCode(vm.OR) - XOR = OpCode(vm.XOR) - NOT = OpCode(vm.NOT) - BYTE = OpCode(vm.BYTE) - SHL = OpCode(vm.SHL) - SHR = OpCode(vm.SHR) - SAR = OpCode(vm.SAR) - CLZ = OpCode(vm.CLZ) - - // Memory - MSTORE = OpCode(vm.MSTORE) - MSTORE8 = OpCode(vm.MSTORE8) - MLOAD = OpCode(vm.MLOAD) - MSIZE = OpCode(vm.MSIZE) - MCOPY = OpCode(vm.MCOPY) - - // Storage - SLOAD = OpCode(vm.SLOAD) - SSTORE = OpCode(vm.SSTORE) - TLOAD = OpCode(vm.TLOAD) - TSTORE = OpCode(vm.TSTORE) - - // LOG - LOG0 = OpCode(vm.LOG0) - LOG1 = OpCode(vm.LOG1) - LOG2 = OpCode(vm.LOG2) - LOG3 = OpCode(vm.LOG3) - LOG4 = OpCode(vm.LOG4) - - // System level instructions. - ADDRESS = OpCode(vm.ADDRESS) - BALANCE = OpCode(vm.BALANCE) - ORIGIN = OpCode(vm.ORIGIN) - CALLER = OpCode(vm.CALLER) - CALLVALUE = OpCode(vm.CALLVALUE) - CALLDATALOAD = OpCode(vm.CALLDATALOAD) - CALLDATASIZE = OpCode(vm.CALLDATASIZE) - CALLDATACOPY = OpCode(vm.CALLDATACOPY) - CODESIZE = OpCode(vm.CODESIZE) - CODECOPY = OpCode(vm.CODECOPY) - GASPRICE = OpCode(vm.GASPRICE) - EXTCODESIZE = OpCode(vm.EXTCODESIZE) - EXTCODECOPY = OpCode(vm.EXTCODECOPY) - RETURNDATASIZE = OpCode(vm.RETURNDATASIZE) - RETURNDATACOPY = OpCode(vm.RETURNDATACOPY) - EXTCODEHASH = OpCode(vm.EXTCODEHASH) - CREATE = OpCode(vm.CREATE) - CALL = OpCode(vm.CALL) - CALLCODE = OpCode(vm.CALLCODE) - DELEGATECALL = OpCode(vm.DELEGATECALL) - CREATE2 = OpCode(vm.CREATE2) - STATICCALL = OpCode(vm.STATICCALL) - SELFDESTRUCT = OpCode(vm.SELFDESTRUCT) - - // Blockchain instructions - BLOCKHASH = OpCode(vm.BLOCKHASH) - COINBASE = OpCode(vm.COINBASE) - TIMESTAMP = OpCode(vm.TIMESTAMP) - NUMBER = OpCode(vm.NUMBER) - PREVRANDAO = OpCode(vm.PREVRANDAO) - GAS = OpCode(vm.GAS) - GASLIMIT = OpCode(vm.GASLIMIT) - CHAINID = OpCode(vm.CHAINID) - SELFBALANCE = OpCode(vm.SELFBALANCE) - BASEFEE = OpCode(vm.BASEFEE) - BLOBHASH = OpCode(vm.BLOBHASH) - BLOBBASEFEE = OpCode(vm.BLOBBASEFEE) - - // Invalid instruction - INVALID = OpCode(vm.INVALID) -) - -// The following constants define the extended set of OpCodes for the long-form -// EVM. -// These opcodes are specific to the long-form EVM and are not part of the -// original EVM. -const ( - // long-form EVM special instructions - - // JUMP_TO is a special instruction that is used to jump to the end of the - // current basic block. - // - // Since due to the usage of immediate arguments in instructions like PUSH2 - // the code size of basic blocks can shrink compared to the original EVM, - // gaps can appear between the end of a basic block and the beginning of the - // next one indicated by a JUMPDEST instruction. Since all JUMPDEST - // instructions have to remain at the same position in the code as in the - // original EVM code, since jump-destinations of JUMP and JUMPI operations - // are computed dynamically, these gaps have to be filled with NOOP - // instructions. To avoid having to process long sequences of NOOPs, - // JUMP_TO instructions are used to skip them in a single step. - // - // The following restrictions are imposed on JUMP_TO instructions: - // - they must target the immediate succeeding JUMPDEST instruction - // - all instructions between the JUMP_TO and the JUMPDEST must be NOOPs - // - // These restrictions are enforced during the EVM to SFVM code conversion. - JUMP_TO OpCode = iota + 0x100 - - // NOOP is a special instruction that does nothing. It is used as a filler - // instruction to pad basic blocks to the correct size. - NOOP - - // DATA is a special instruction that is used to extend the size of OpCodes - // that require more than the available 2-byte immediate arguments. - // For instance, [PUSH4, 1, 2, 3, 4] in the original EVM code gets converted - // to [(PUSH4, 1<<8 | 2),(DATA, 3<<8 | 4)]. - // Since DATA is marked explicitly as such, jump-destination checks can be - // conducted in O(1) by checking the OpCode of an instruction. In the - // implicit data encoding of EVM byte code, this would require a linear - // search (which could be cached to amortize costs). - DATA - - // _highestOpCode is an alias for the OpCode with the highest defined - // numeric value. It is only intended to be used in the unit tests - // associated to this OpCode definition file to verify that the OpCode - // bit mask limit has not been exceeded. - _highestOpCode = DATA -) - -var toString = map[OpCode]string{ - DATA: "DATA", - NOOP: "NOOP", - JUMP_TO: "JUMP_TO", -} - -// String returns the string representation of the OpCode. -func (o OpCode) String() string { - if o <= 0xFF { - return vm.OpCode(o).String() - } - - if str, ok := toString[o]; ok { - return str - } - return fmt.Sprintf("op(0x%04X)", int16(o)) -} - -// HasArgument returns true if the second 16-bit word of the instruction is -// argument data. -func (o OpCode) HasArgument() bool { - if PUSH1 <= o && o <= PUSH32 { - return true - } - switch o { - case DATA: - return true - case JUMP_TO: - return true - } - return false -} - -func (o OpCode) isBaseInstruction() bool { - return o < 0x100 -} - -// opCodePropertyMap is a generic property map for precomputed values. -// Its purpose is to provide a precomputed lookup table for OpCode properties -// that can be generated from a function that takes an OpCode as input. -// Using this type hides internal details of the opcode implementation. -type opCodePropertyMap[T any] struct { - lookup [numOpCodes]T -} - -// newOpCodePropertyMap creates a new OpCode property map. -// The property function shall be resilient to undefined OpCode values, and not -// panic. The zero values or a sentinel value shall be used in such cases. -func newOpCodePropertyMap[T any](property func(op OpCode) T) opCodePropertyMap[T] { - lookup := [numOpCodes]T{} - for i := 0; i < numOpCodes; i++ { - lookup[i] = property(OpCode(i)) - } - return opCodePropertyMap[T]{lookup} -} - -func (p *opCodePropertyMap[T]) get(op OpCode) T { - // Index may be out of bounds. Nevertheless, bounds check carry a performance - // penalty. If the property map is initialized correctly, the index will be - // within bounds. - return p.lookup[op&opCodeMask] -} diff --git a/go/interpreter/sfvm/opcode_test.go b/go/interpreter/sfvm/opcode_test.go deleted file mode 100644 index 835d4013..00000000 --- a/go/interpreter/sfvm/opcode_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2025 Sonic Operations Ltd -// -// Use of this software is governed by the Business Source License included -// in the LICENSE file and at soniclabs.com/bsl11. -// -// Change Date: 2028-4-16 -// -// On the date above, in accordance with the Business Source License, use of -// this software will be governed by the GNU Lesser General Public License v3. - -package sfvm - -import ( - "math" - "testing" -) - -func TestOpCode_String(t *testing.T) { - tests := []struct { - op OpCode - want string - }{ - {STOP, "STOP"}, - {0x0c, "op(0x0C)"}, - {0x0200, "op(0x0200)"}, - } - for _, tt := range tests { - if got := tt.op.String(); got != tt.want { - t.Errorf("got = %q, want %q", got, tt.want) - } - } -} - -func TestOpCode_AllOpCodesAreSmallerThanTheOpCodeCapacity(t *testing.T) { - if want, get := numOpCodes, opCodeMask+1; want != get { - t.Errorf("opCodeMask+1 = %d, want %d", get, want) - } - if _highestOpCode >= numOpCodes { - t.Errorf( - "highest op code %d exceeds the current OpCode type capacity of %d", - _highestOpCode, - numOpCodes, - ) - } -} - -func TestOpcodeProperty_DoesNotOverflow(t *testing.T) { - identity := newOpCodePropertyMap(func(op OpCode) OpCode { return op }) - for i := OpCode(0); i < OpCode(math.MaxInt16); i++ { - if got, want := identity.get(i), i%numOpCodes; got != want { - t.Errorf("got %d, want %d", got, want) - } - } -} - -func allOpCodesWhere(predicate func(op OpCode) bool) []OpCode { - res := []OpCode{} - for op := OpCode(0); op < numOpCodes; op++ { - if predicate(op) { - res = append(res, op) - } - } - return res -} - -func allOpCodes() []OpCode { - return allOpCodesWhere(func(op OpCode) bool { return true }) -} diff --git a/go/interpreter/sfvm/sfvm.go b/go/interpreter/sfvm/sfvm.go index 0e2cd6a2..5adae5db 100644 --- a/go/interpreter/sfvm/sfvm.go +++ b/go/interpreter/sfvm/sfvm.go @@ -11,8 +11,6 @@ package sfvm import ( - "fmt" - "github.com/0xsoniclabs/tosca/go/tosca" ) @@ -24,8 +22,7 @@ type Config struct { // configuration for production purposes. func NewInterpreter(Config) (*sfvm, error) { return newVm(config{ - ConversionConfig: ConversionConfig{}, - WithShaCache: true, + WithShaCache: true, }) } @@ -36,49 +33,16 @@ func init() { }) } -// RegisterExperimentalInterpreterConfigurations registers all experimental -// SFVM interpreter configurations to Tosca's interpreter registry. This -// function should not be called in production code, as the resulting VMs are -// not officially supported. -func RegisterExperimentalInterpreterConfigurations() error { - - configs := map[string]config{} - configs["sfvm-no-code-cache"] = config{ - ConversionConfig: ConversionConfig{CacheSize: -1}, - } - - for name, config := range configs { - err := tosca.RegisterInterpreterFactory( - name, - func(any) (tosca.Interpreter, error) { - return newVm(config) - }, - ) - if err != nil { - return fmt.Errorf("failed to register interpreter %q: %v", name, err) - } - } - - return nil -} - type config struct { - ConversionConfig WithShaCache bool - runner runner } type sfvm struct { - config config - converter *Converter + config config } func newVm(config config) (*sfvm, error) { - converter, err := NewConverter(config.ConversionConfig) - if err != nil { - return nil, fmt.Errorf("failed to create converter: %v", err) - } - return &sfvm{config: config, converter: converter}, nil + return &sfvm{config: config}, nil } // Defines the newest supported revision for this interpreter implementation @@ -89,13 +53,5 @@ func (e *sfvm) Run(params tosca.Parameters) (tosca.Result, error) { return tosca.Result{}, &tosca.ErrUnsupportedRevision{Revision: params.Revision} } - converted, err := e.converter.Convert( - params.Code, - params.CodeHash, - ) - if err != nil { - return tosca.Result{}, fmt.Errorf("failed to convert code: %w", err) - } - - return run(e.config, params, converted) + return run(e.config, params) } diff --git a/go/interpreter/sfvm/sfvm_interface_test.go b/go/interpreter/sfvm/sfvm_interface_test.go index a532dfba..dd780aac 100644 --- a/go/interpreter/sfvm/sfvm_interface_test.go +++ b/go/interpreter/sfvm/sfvm_interface_test.go @@ -15,6 +15,7 @@ import ( "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" ) // TestSfvm_RegisterExperimentalConfigurations tests the registration of @@ -25,19 +26,6 @@ import ( // - It tests different properties, in one single function. The reason is that the // order of different functions may change, invalidating the test. func TestSfvm_RegisterExperimentalConfigurations(t *testing.T) { - - // Fist registration must succeed. - err := sfvm.RegisterExperimentalInterpreterConfigurations() - if err != nil { - t.Fatalf("failed to register experimental configurations: %v", err) - } - - // Registering a second time must fail. - err = sfvm.RegisterExperimentalInterpreterConfigurations() - if err == nil { - t.Fatalf("expected error when registering experimental configurations twice") - } - // Check that sfvm is registered by default, in addition to experimental configurations if _, ok := tosca.GetAllRegisteredInterpreters()["sfvm"]; !ok { t.Fatalf("sfvm is not registered") @@ -46,7 +34,7 @@ func TestSfvm_RegisterExperimentalConfigurations(t *testing.T) { // Construct all registered interpreter configurations for name, factory := range tosca.GetAllRegisteredInterpreters() { t.Run(name, func(t *testing.T) { - vm, err := factory(sfvm.Config{}) + interpreter, err := factory(sfvm.Config{}) if err != nil { t.Fatalf("failed to create interpreter: %v", err) } @@ -54,9 +42,9 @@ func TestSfvm_RegisterExperimentalConfigurations(t *testing.T) { // Vms are opaque, we can't check their configuration directly. // We can only check that they do execute some basic code. params := tosca.Parameters{} - params.Code = []byte{byte(sfvm.PUSH1), 0xff, byte(sfvm.POP), byte(sfvm.STOP)} + params.Code = []byte{byte(vm.PUSH1), 0xff, byte(vm.POP), byte(vm.STOP)} params.Gas = 5 - res, err := vm.Run(params) + res, err := interpreter.Run(params) if err != nil { t.Fatalf("failed to run interpreter: %v", err) } diff --git a/go/interpreter/sfvm/sfvm_test.go b/go/interpreter/sfvm/sfvm_test.go index 798fe991..b1bb8094 100644 --- a/go/interpreter/sfvm/sfvm_test.go +++ b/go/interpreter/sfvm/sfvm_test.go @@ -55,13 +55,3 @@ func TestSfvm_InterpreterReturnsErrorWhenExecutingUnsupportedRevision(t *testing t.Fatalf("unexpected error: want %q, got %q", want, got) } } - -func TestSfvm_newVm_returnsErrorWithWrongConfiguration(t *testing.T) { - config := config{ - ConversionConfig: ConversionConfig{CacheSize: maxCachedCodeLength / 2}, - } - _, err := newVm(config) - if err == nil { - t.Fatalf("expected error, got nil") - } -} diff --git a/go/interpreter/sfvm/stack_usage.go b/go/interpreter/sfvm/stack_usage.go index eeae6070..29acdc3f 100644 --- a/go/interpreter/sfvm/stack_usage.go +++ b/go/interpreter/sfvm/stack_usage.go @@ -10,6 +10,8 @@ package sfvm +import "github.com/0xsoniclabs/tosca/go/tosca/vm" + // stackUsage defines the combined effect of an instruction on the stack. Each // instruction is accessing a range of elements on the stack relative to the // stack pointer. The range is given by the interval [from, to) where from is @@ -22,7 +24,7 @@ type stackUsage struct { // computeStackUsage computes the stack usage of the given opcode. The result // is a stackUsage struct that defines the combined effect of the instruction // on the stack. If the opcode is not known, zero stack usage is reported. -func computeStackUsage(op OpCode) stackUsage { +func computeStackUsage(op vm.OpCode) stackUsage { // For single instructions it is easiest to define the stack usage based on // the opcode's pops and pushes. @@ -35,49 +37,49 @@ func computeStackUsage(op OpCode) stackUsage { return stackUsage{from: -pops, to: to, delta: delta} } - if PUSH1 <= op && op <= PUSH32 { + if vm.PUSH1 <= op && op <= vm.PUSH32 { return makeUsage(0, 1) } - if DUP1 <= op && op <= DUP16 { - return makeUsage(int(op-DUP1+1), int(op-DUP1+2)) + if vm.DUP1 <= op && op <= vm.DUP16 { + return makeUsage(int(op-vm.DUP1+1), int(op-vm.DUP1+2)) } - if SWAP1 <= op && op <= SWAP16 { - return makeUsage(int(op-SWAP1+2), int(op-SWAP1+2)) + if vm.SWAP1 <= op && op <= vm.SWAP16 { + return makeUsage(int(op-vm.SWAP1+2), int(op-vm.SWAP1+2)) } - if LOG0 <= op && op <= LOG4 { - return makeUsage(int(op-LOG0+2), 0) + if vm.LOG0 <= op && op <= vm.LOG4 { + return makeUsage(int(op-vm.LOG0+2), 0) } switch op { - case JUMPDEST, JUMP_TO, STOP: + case vm.JUMPDEST, vm.STOP: return makeUsage(0, 0) - case PUSH0, MSIZE, ADDRESS, ORIGIN, CALLER, CALLVALUE, CALLDATASIZE, - CODESIZE, GASPRICE, COINBASE, TIMESTAMP, NUMBER, - PREVRANDAO, GASLIMIT, PC, GAS, RETURNDATASIZE, - SELFBALANCE, CHAINID, BASEFEE, BLOBBASEFEE: + case vm.PUSH0, vm.MSIZE, vm.ADDRESS, vm.ORIGIN, vm.CALLER, vm.CALLVALUE, vm.CALLDATASIZE, + vm.CODESIZE, vm.GASPRICE, vm.COINBASE, vm.TIMESTAMP, vm.NUMBER, + vm.PREVRANDAO, vm.GASLIMIT, vm.PC, vm.GAS, vm.RETURNDATASIZE, + vm.SELFBALANCE, vm.CHAINID, vm.BASEFEE, vm.BLOBBASEFEE: return makeUsage(0, 1) - case POP, JUMP, SELFDESTRUCT: + case vm.POP, vm.JUMP, vm.SELFDESTRUCT: return makeUsage(1, 0) - case ISZERO, NOT, BALANCE, CALLDATALOAD, EXTCODESIZE, - BLOCKHASH, MLOAD, SLOAD, TLOAD, EXTCODEHASH, BLOBHASH, CLZ: + case vm.ISZERO, vm.NOT, vm.BALANCE, vm.CALLDATALOAD, vm.EXTCODESIZE, + vm.BLOCKHASH, vm.MLOAD, vm.SLOAD, vm.TLOAD, vm.EXTCODEHASH, vm.BLOBHASH, vm.CLZ: return makeUsage(1, 1) - case MSTORE, MSTORE8, SSTORE, TSTORE, JUMPI, RETURN, REVERT: + case vm.MSTORE, vm.MSTORE8, vm.SSTORE, vm.TSTORE, vm.JUMPI, vm.RETURN, vm.REVERT: return makeUsage(2, 0) - case ADD, SUB, MUL, DIV, SDIV, MOD, SMOD, EXP, SIGNEXTEND, - SHA3, LT, GT, SLT, SGT, EQ, AND, XOR, OR, BYTE, - SHL, SHR, SAR: + case vm.ADD, vm.SUB, vm.MUL, vm.DIV, vm.SDIV, vm.MOD, vm.SMOD, vm.EXP, vm.SIGNEXTEND, + vm.SHA3, vm.LT, vm.GT, vm.SLT, vm.SGT, vm.EQ, vm.AND, vm.XOR, vm.OR, vm.BYTE, + vm.SHL, vm.SHR, vm.SAR: return makeUsage(2, 1) - case CALLDATACOPY, CODECOPY, RETURNDATACOPY, MCOPY: + case vm.CALLDATACOPY, vm.CODECOPY, vm.RETURNDATACOPY, vm.MCOPY: return makeUsage(3, 0) - case ADDMOD, MULMOD, CREATE: + case vm.ADDMOD, vm.MULMOD, vm.CREATE: return makeUsage(3, 1) - case EXTCODECOPY: + case vm.EXTCODECOPY: return makeUsage(4, 0) - case CREATE2: + case vm.CREATE2: return makeUsage(4, 1) - case STATICCALL, DELEGATECALL: + case vm.STATICCALL, vm.DELEGATECALL: return makeUsage(6, 1) - case CALL, CALLCODE: + case vm.CALL, vm.CALLCODE: return makeUsage(7, 1) } diff --git a/go/interpreter/sfvm/stack_usage_test.go b/go/interpreter/sfvm/stack_usage_test.go index 620b7e47..74a57a52 100644 --- a/go/interpreter/sfvm/stack_usage_test.go +++ b/go/interpreter/sfvm/stack_usage_test.go @@ -12,22 +12,24 @@ package sfvm import ( "testing" + + "github.com/0xsoniclabs/tosca/go/tosca/vm" ) func TestComputeStackUsage_ProducesValidResultsForSingleOps(t *testing.T) { tests := []struct { - op OpCode + op vm.OpCode usage stackUsage }{ - {STOP, stackUsage{from: 0, to: 0, delta: 0}}, - {ADD, stackUsage{from: -2, to: 0, delta: -1}}, - {POP, stackUsage{from: -1, to: 0, delta: -1}}, - {PUSH5, stackUsage{from: 0, to: 1, delta: 1}}, - {SWAP1, stackUsage{from: -2, to: 0, delta: 0}}, - {SWAP10, stackUsage{from: -11, to: 0, delta: 0}}, - {DUP1, stackUsage{from: -1, to: 1, delta: 1}}, - {DUP12, stackUsage{from: -12, to: 1, delta: 1}}, - {LOG3, stackUsage{from: -5, to: 0, delta: -5}}, + {vm.STOP, stackUsage{from: 0, to: 0, delta: 0}}, + {vm.ADD, stackUsage{from: -2, to: 0, delta: -1}}, + {vm.POP, stackUsage{from: -1, to: 0, delta: -1}}, + {vm.PUSH5, stackUsage{from: 0, to: 1, delta: 1}}, + {vm.SWAP1, stackUsage{from: -2, to: 0, delta: 0}}, + {vm.SWAP10, stackUsage{from: -11, to: 0, delta: 0}}, + {vm.DUP1, stackUsage{from: -1, to: 1, delta: 1}}, + {vm.DUP12, stackUsage{from: -12, to: 1, delta: 1}}, + {vm.LOG3, stackUsage{from: -5, to: 0, delta: -5}}, } for _, test := range tests { From 063d0e8b436a48e33e3807289eb194b8bfc72c1f Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Nov 2025 14:03:10 +0100 Subject: [PATCH 6/7] Add jumpdest analysis --- go/interpreter/sfvm/analysis.go | 97 ++++++++++++++++++++ go/interpreter/sfvm/analysis_test.go | 108 +++++++++++++++++++++++ go/interpreter/sfvm/ct.go | 3 +- go/interpreter/sfvm/instructions.go | 4 +- go/interpreter/sfvm/instructions_test.go | 30 ++++++- go/interpreter/sfvm/interpreter.go | 11 ++- go/interpreter/sfvm/interpreter_test.go | 7 +- go/interpreter/sfvm/sfvm.go | 26 ++++-- go/interpreter/sfvm/sfvm_test.go | 4 +- 9 files changed, 270 insertions(+), 20 deletions(-) create mode 100644 go/interpreter/sfvm/analysis.go create mode 100644 go/interpreter/sfvm/analysis_test.go diff --git a/go/interpreter/sfvm/analysis.go b/go/interpreter/sfvm/analysis.go new file mode 100644 index 00000000..e37081cd --- /dev/null +++ b/go/interpreter/sfvm/analysis.go @@ -0,0 +1,97 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package sfvm + +import ( + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" + lru "github.com/hashicorp/golang-lru/v2" +) + +type analysis struct { + cache *lru.Cache[tosca.Hash, *jumpDestMap] +} + +func newAnalysis(size int) analysis { + cache, err := lru.New[tosca.Hash, *jumpDestMap](size) + if err != nil { + panic("failed to create analysis cache: " + err.Error()) + } + return analysis{cache: cache} +} + +func (a *analysis) analyzeJumpDest(code tosca.Code, codehash *tosca.Hash) *jumpDestMap { + if a == nil || a.cache == nil || codehash == nil { + return jumpDestAnalysisInternal(code) + } + + if analysis, ok := a.cache.Get(*codehash); ok { + return analysis + } + + jumpDests := jumpDestAnalysisInternal(code) + a.cache.Add(*codehash, jumpDests) + return jumpDests +} + +type jumpDestMap struct { + bitmap []uint64 + codeSize uint64 +} + +func newJumpDestMap(size uint64) *jumpDestMap { + analysisSize := size/64 + 1 + analysis := &jumpDestMap{ + bitmap: make([]uint64, analysisSize), + codeSize: size, + } + return analysis +} + +func jumpDestAnalysisInternal(code tosca.Code) *jumpDestMap { + analysis := newJumpDestMap(uint64(len(code))) + for idx := 0; idx < len(code); idx++ { + op := vm.OpCode(code[idx]) + if op >= vm.PUSH1 && op <= vm.PUSH32 { + // PUSH1 to PUSH32 + dataSize := int(op) - int(vm.PUSH1) + 1 + idx += dataSize // Skip the pushed data + continue + } + if op == vm.JUMPDEST { + analysis.markJumpDest(uint64(idx)) + } + } + return analysis +} + +func (a *jumpDestMap) isJumpDest(idx uint64) bool { + if a == nil { + return false + } + if idx >= a.codeSize { + return false + } + uintIdx, mask := idxToAnalysisIdxAndMask(idx) + return a.bitmap[uintIdx]&mask != 0 +} + +func (a *jumpDestMap) markJumpDest(idx uint64) { + if idx >= uint64(a.codeSize) { + return + } + uintIdx, mask := idxToAnalysisIdxAndMask(idx) + a.bitmap[uintIdx] |= mask +} + +func idxToAnalysisIdxAndMask(idx uint64) (uint64, uint64) { + return idx / 64, 1 << (idx % 64) +} diff --git a/go/interpreter/sfvm/analysis_test.go b/go/interpreter/sfvm/analysis_test.go new file mode 100644 index 00000000..3f47a182 --- /dev/null +++ b/go/interpreter/sfvm/analysis_test.go @@ -0,0 +1,108 @@ +// Copyright (c) 2025 Sonic Operations Ltd +// +// Use of this software is governed by the Business Source License included +// in the LICENSE file and at soniclabs.com/bsl11. +// +// Change Date: 2028-4-16 +// +// On the date above, in accordance with the Business Source License, use of +// this software will be governed by the GNU Lesser General Public License v3. + +package sfvm + +import ( + "testing" + + "github.com/0xsoniclabs/tosca/go/tosca" + "github.com/0xsoniclabs/tosca/go/tosca/vm" + "github.com/stretchr/testify/require" +) + +func TestAnalysisCache_PanicsOnNegativeSize(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("expected createAnalysisCache to panic on negative size") + } + }() + _ = newAnalysis(-1) +} + +func TestAnalysis_NewAnalysisIsNonEmpty(t *testing.T) { + a := newJumpDestMap(10) + if a.codeSize == 0 { + t.Error("expected newAnalysis to return a non-empty Analysis") + } + if len(a.bitmap) == 0 { + t.Error("expected newAnalysis to return a non-empty data slice") + } +} + +func TestAnalysis_MarkJumpDestAndIsJumpDest(t *testing.T) { + size := 10 + a := newJumpDestMap(uint64(size)) + a.markJumpDest(2) + a.markJumpDest(18) + // Check that the jump destination is marked correctly over boundaries + for i := 0; i < 2*size; i++ { + if i == 2 && !a.isJumpDest(uint64(i)) { + t.Errorf("expected index %d to be marked as jump destination", i) + } + if i != 2 && a.isJumpDest(uint64(i)) { + t.Errorf("expected index %d to not be marked as jump destination", i) + } + } +} + +func TestAnalysis_MarksJumpDestAtCorrectIndex(t *testing.T) { + code := tosca.Code{byte(vm.JUMPDEST), byte(vm.PUSH1), byte(vm.JUMPDEST), byte(vm.JUMPDEST)} + analysis := jumpDestAnalysisInternal(code) + if !analysis.isJumpDest(0) { + t.Errorf("expected index 0 to be jump destination") + } + if analysis.isJumpDest(1) { + t.Errorf("expected index 1 to not be jump destination") + } + if analysis.isJumpDest(2) { + t.Errorf("expected index 2 to not be jump destination") + } + if !analysis.isJumpDest(3) { + t.Errorf("expected index 3 to be jump destination") + } +} + +func TestAnalysis_PushDataIsSkipped(t *testing.T) { + code := tosca.Code{ + byte(vm.PUSH9), byte(vm.JUMPDEST), byte(vm.JUMPDEST), byte(vm.JUMPDEST), byte(vm.JUMPDEST), + byte(vm.JUMPDEST), byte(vm.JUMPDEST), byte(vm.JUMPDEST), byte(vm.JUMPDEST), byte(vm.JUMPDEST), + byte(vm.JUMPDEST), + byte(vm.PUSH2), byte(vm.JUMPDEST), byte(vm.JUMPDEST), + byte(vm.JUMPDEST), + } + analysis := jumpDestAnalysisInternal(code) + for i := range code { + if analysis.isJumpDest(uint64(i)) && (i != 10 && i != 14) { + t.Errorf("expected index %d to be jump destination", i) + } + if !analysis.isJumpDest(uint64(i)) && (i == 10 || i == 14) { + t.Errorf("expected index %d to not be jump destination", i) + } + } +} + +func TestAnalysis_InputsAreCachedUsingCodeHashAsKey(t *testing.T) { + analysis := newAnalysis(1 << 2) + + code := []byte{byte(vm.STOP)} + hash := tosca.Hash{byte(1)} + + want := analysis.analyzeJumpDest(code, &hash) + got := analysis.analyzeJumpDest(code, &hash) + if &want.bitmap != &got.bitmap { // < needs to be the same slice + t.Errorf("cached conversion result not returned") + } +} + +func TestAnalysis_IsJumpDestCanHandleUninitializedMap(t *testing.T) { + var jumpDestMap *jumpDestMap + require.False(t, jumpDestMap.isJumpDest(uint64(0)), "expected isJumpDest to return false for uninitialized map") +} diff --git a/go/interpreter/sfvm/ct.go b/go/interpreter/sfvm/ct.go index bb6f7f72..a227eef5 100644 --- a/go/interpreter/sfvm/ct.go +++ b/go/interpreter/sfvm/ct.go @@ -55,8 +55,9 @@ func (a *ctAdapter) StepN(state *st.State, numSteps int) (*st.State, error) { stack: convertCtStackToSfvmStack(state.Stack), memory: memory, code: params.Code, + analysis: *a.vm.analysis.analyzeJumpDest(params.Code, params.CodeHash), returnData: state.LastCallReturnData.ToBytes(), - withShaCache: a.vm.config.WithShaCache, + withShaCache: a.vm.config.withShaCache, } defer func() { diff --git a/go/interpreter/sfvm/instructions.go b/go/interpreter/sfvm/instructions.go index 64f8c9e1..55b73dc0 100644 --- a/go/interpreter/sfvm/instructions.go +++ b/go/interpreter/sfvm/instructions.go @@ -35,7 +35,9 @@ func opPc(c *context) { } func checkJumpDest(c *context) error { - // TODO: use bitmap for jumpdest analysis + if int(c.pc+1) >= len(c.code) || !c.analysis.isJumpDest(uint64(c.pc+1)) { + return errInvalidJump + } return nil } diff --git a/go/interpreter/sfvm/instructions_test.go b/go/interpreter/sfvm/instructions_test.go index 3a3bb7f3..ef0dbe90 100644 --- a/go/interpreter/sfvm/instructions_test.go +++ b/go/interpreter/sfvm/instructions_test.go @@ -1040,8 +1040,34 @@ func TestInstructions_StorageOps_CallStorageContext(t *testing.T) { } } -// TODO reenable once SFVM supports JUMPDEST marking -func DisTestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { +func TestJumps_checkJumpDestOnlyContainsValidDestinations(t *testing.T) { + codeHash := tosca.Hash{0x42} + context := getEmptyContext() + context.code = tosca.Code{ + byte(vm.STOP), + byte(vm.ADD), + byte(vm.JUMPDEST), + byte(vm.PUSH1), + byte(0), + byte(vm.JUMPDEST), + } + var analysis analysis + + context.analysis = *analysis.analyzeJumpDest(context.code, &codeHash) + + for pc := range 5 { + context.pc = int32(pc) + err := checkJumpDest(&context) + if context.code[context.pc+1] == byte(vm.JUMPDEST) && err != nil { + t.Errorf("unexpected error: %v", err) + } + if context.code[context.pc+1] != byte(vm.JUMPDEST) && err != errInvalidJump { + t.Errorf("expected errInvalidJump, but got: %v", err) + } + } +} + +func TestInstructions_JumpOpsCheckJUMPDEST(t *testing.T) { tests := map[vm.OpCode]struct { implementation func(*context) error stack []uint64 diff --git a/go/interpreter/sfvm/interpreter.go b/go/interpreter/sfvm/interpreter.go index 176345ba..f85b721b 100644 --- a/go/interpreter/sfvm/interpreter.go +++ b/go/interpreter/sfvm/interpreter.go @@ -35,9 +35,10 @@ const ( // stack, and memory. For each contract execution, a new context is created. type context struct { // Inputs - params tosca.Parameters - context tosca.RunContext - code tosca.Code + params tosca.Parameters + context tosca.RunContext + code tosca.Code + analysis jumpDestMap // Execution state pc int32 @@ -72,6 +73,7 @@ func (c *context) isAtLeast(revision tosca.Revision) bool { } func run( + analysis analysis, config config, params tosca.Parameters, ) (tosca.Result, error) { @@ -92,7 +94,8 @@ func run( stack: NewStack(), memory: NewMemory(), code: params.Code, - withShaCache: config.WithShaCache, + analysis: *analysis.analyzeJumpDest(params.Code, params.CodeHash), + withShaCache: config.withShaCache, } defer ReturnStack(ctxt.stack) diff --git a/go/interpreter/sfvm/interpreter_test.go b/go/interpreter/sfvm/interpreter_test.go index 25985ab6..4f119290 100644 --- a/go/interpreter/sfvm/interpreter_test.go +++ b/go/interpreter/sfvm/interpreter_test.go @@ -178,8 +178,7 @@ func TestInterpreter_step_DetectsUpperStackLimitViolation(t *testing.T) { } } -// TODO reenable once SFVM supports JUMPDEST marking -func DisTestInterpreter_CanDispatchExecutableInstructions(t *testing.T) { +func TestInterpreter_CanDispatchExecutableInstructions(t *testing.T) { for _, op := range allOpCodesWhere(isExecutable) { t.Run(op.String(), func(t *testing.T) { forEachRevision(t, op, func(t *testing.T, revision tosca.Revision) { @@ -292,7 +291,7 @@ func TestInterpreter_Vanilla_RunsWithoutOutput(t *testing.T) { os.Stdout = w // Run testing code - _, err := run(config{}, params) + _, err := run(analysis{}, config{}, params) // read the output _ = w.Close() // ignore error in test out, _ := io.ReadAll(r) @@ -312,7 +311,7 @@ func TestInterpreter_EmptyCodeBypassesRunnerAndSucceeds(t *testing.T) { params := tosca.Parameters{Code: code} config := config{} - result, err := run(config, params) + result, err := run(analysis{}, config, params) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/go/interpreter/sfvm/sfvm.go b/go/interpreter/sfvm/sfvm.go index 5adae5db..d9756772 100644 --- a/go/interpreter/sfvm/sfvm.go +++ b/go/interpreter/sfvm/sfvm.go @@ -22,7 +22,8 @@ type Config struct { // configuration for production purposes. func NewInterpreter(Config) (*sfvm, error) { return newVm(config{ - WithShaCache: true, + withShaCache: true, + withAnalysisCache: true, }) } @@ -34,24 +35,37 @@ func init() { } type config struct { - WithShaCache bool + withShaCache bool + withAnalysisCache bool } type sfvm struct { - config config + config config + analysis analysis } func newVm(config config) (*sfvm, error) { - return &sfvm{config: config}, nil + var analysis analysis + if config.withAnalysisCache { + if config.withAnalysisCache { + analysis = newAnalysis(1 << 30) // = 1GiB + } + } + + sfvm := &sfvm{ + config: config, + analysis: analysis, + } + return sfvm, nil } // Defines the newest supported revision for this interpreter implementation const newestSupportedRevision = tosca.R15_Osaka -func (e *sfvm) Run(params tosca.Parameters) (tosca.Result, error) { +func (s *sfvm) Run(params tosca.Parameters) (tosca.Result, error) { if params.Revision > newestSupportedRevision { return tosca.Result{}, &tosca.ErrUnsupportedRevision{Revision: params.Revision} } - return run(e.config, params) + return run(s.analysis, s.config, params) } diff --git a/go/interpreter/sfvm/sfvm_test.go b/go/interpreter/sfvm/sfvm_test.go index b1bb8094..e17d90d7 100644 --- a/go/interpreter/sfvm/sfvm_test.go +++ b/go/interpreter/sfvm/sfvm_test.go @@ -22,7 +22,7 @@ func TestNewInterpreter_ProducesInstanceWithSanctionedProperties(t *testing.T) { if err != nil { t.Fatalf("failed to create SFVM instance: %v", err) } - if sfvm.config.WithShaCache != true { + if sfvm.config.withShaCache != true { t.Fatalf("SFVM is not configured with sha cache") } } @@ -36,7 +36,7 @@ func TestSfvm_OfficialConfigurationHasSanctionedProperties(t *testing.T) { if !ok { t.Fatalf("unexpected interpreter implementation, got %T", vm) } - if sfvm.config.WithShaCache != true { + if sfvm.config.withShaCache != true { t.Fatalf("sfvm is not configured with sha cache") } } From 5c1fd00acf1a3981f3e6a1eeb8eb2d9cae42f7a2 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 12 Nov 2025 14:18:18 +0100 Subject: [PATCH 7/7] Add sfvm to CT and tosca tests --- go/ct/driver/run.go | 2 ++ go/ct/evm_fuzz_test.go | 6 ++++++ go/ct/evm_test.go | 2 ++ go/integration_test/interpreter/test_config.go | 1 + go/integration_test/interpreter/test_config_test.go | 2 +- go/integration_test/interpreter/test_evm.go | 1 + go/integration_test/processor/processor_test.go | 7 ++++--- 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/go/ct/driver/run.go b/go/ct/driver/run.go index 474fc7f6..e2b0bdac 100644 --- a/go/ct/driver/run.go +++ b/go/ct/driver/run.go @@ -29,6 +29,7 @@ import ( "github.com/0xsoniclabs/tosca/go/interpreter/evmzero" "github.com/0xsoniclabs/tosca/go/interpreter/geth" "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" + "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" "github.com/0xsoniclabs/tosca/go/lib/cpp" "github.com/0xsoniclabs/tosca/go/lib/rust" "github.com/0xsoniclabs/tosca/go/tosca" @@ -57,6 +58,7 @@ var RunCmd = cliUtils.AddCommonFlags(cli.Command{ var evms = map[string]ct.Evm{ "lfvm": lfvm.NewConformanceTestingTarget(), + "sfvm": sfvm.NewConformanceTestingTarget(), "geth": geth.NewConformanceTestingTarget(), "evmzero": evmzero.NewConformanceTestingTarget(), "evmrs": evmrs.NewConformanceTestingTarget(), diff --git a/go/ct/evm_fuzz_test.go b/go/ct/evm_fuzz_test.go index c433428a..3b5eca06 100644 --- a/go/ct/evm_fuzz_test.go +++ b/go/ct/evm_fuzz_test.go @@ -22,6 +22,7 @@ import ( "github.com/0xsoniclabs/tosca/go/interpreter/evmzero" "github.com/0xsoniclabs/tosca/go/interpreter/geth" "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" + "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" "github.com/0xsoniclabs/tosca/go/tosca" ) @@ -38,6 +39,11 @@ func FuzzLfvm(f *testing.F) { fuzzVm(lfvm.NewConformanceTestingTarget(), f) } +// FuzzSfvm is a fuzzing test for sfvm +func FuzzSfvm(f *testing.F) { + fuzzVm(sfvm.NewConformanceTestingTarget(), f) +} + // FuzzLfvm is a fuzzing test for evmzero func FuzzEvmzero(f *testing.F) { fuzzVm(evmzero.NewConformanceTestingTarget(), f) diff --git a/go/ct/evm_test.go b/go/ct/evm_test.go index 7f097f81..3993056e 100644 --- a/go/ct/evm_test.go +++ b/go/ct/evm_test.go @@ -24,6 +24,7 @@ import ( "github.com/0xsoniclabs/tosca/go/interpreter/evmzero" "github.com/0xsoniclabs/tosca/go/interpreter/geth" "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" + "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" "github.com/0xsoniclabs/tosca/go/tosca" "github.com/0xsoniclabs/tosca/go/tosca/vm" ) @@ -31,6 +32,7 @@ import ( var evms = map[string]ct.Evm{ "geth": geth.NewConformanceTestingTarget(), "lfvm": lfvm.NewConformanceTestingTarget(), + "sfvm": sfvm.NewConformanceTestingTarget(), "evmzero": evmzero.NewConformanceTestingTarget(), } diff --git a/go/integration_test/interpreter/test_config.go b/go/integration_test/interpreter/test_config.go index 65ccbd43..9163be85 100644 --- a/go/integration_test/interpreter/test_config.go +++ b/go/integration_test/interpreter/test_config.go @@ -20,6 +20,7 @@ import ( _ "github.com/0xsoniclabs/tosca/go/interpreter/evmzero" _ "github.com/0xsoniclabs/tosca/go/interpreter/geth" "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" + _ "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" "github.com/0xsoniclabs/tosca/go/tosca" "golang.org/x/exp/maps" ) diff --git a/go/integration_test/interpreter/test_config_test.go b/go/integration_test/interpreter/test_config_test.go index d9540ba2..df26cfac 100644 --- a/go/integration_test/interpreter/test_config_test.go +++ b/go/integration_test/interpreter/test_config_test.go @@ -18,7 +18,7 @@ import ( func TestCoveredVariants_ContainsMainConfigurations(t *testing.T) { all := getAllInterpreterVariantsForTests() wanted := []string{ - "geth", "lfvm", "lfvm-si", "evmzero", "evmone", + "geth", "lfvm", "lfvm-si", "evmzero", "evmone", "sfvm", } for _, n := range wanted { if !slices.Contains(all, n) { diff --git a/go/integration_test/interpreter/test_evm.go b/go/integration_test/interpreter/test_evm.go index 1229831d..60d996bd 100644 --- a/go/integration_test/interpreter/test_evm.go +++ b/go/integration_test/interpreter/test_evm.go @@ -14,6 +14,7 @@ import ( _ "github.com/0xsoniclabs/tosca/go/interpreter/evmone" _ "github.com/0xsoniclabs/tosca/go/interpreter/evmzero" _ "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" + _ "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" "github.com/0xsoniclabs/tosca/go/tosca" ) diff --git a/go/integration_test/processor/processor_test.go b/go/integration_test/processor/processor_test.go index 986651ed..28ecc45a 100644 --- a/go/integration_test/processor/processor_test.go +++ b/go/integration_test/processor/processor_test.go @@ -28,6 +28,7 @@ import ( _ "github.com/0xsoniclabs/tosca/go/interpreter/evmzero" // < registers evmzero interpreter for testing _ "github.com/0xsoniclabs/tosca/go/interpreter/lfvm" // < registers lfvm interpreter for testing + _ "github.com/0xsoniclabs/tosca/go/interpreter/sfvm" // < registers sfvm interpreter for testing ) // This file contains a few initial shake-down tests or a Processor implementation. @@ -220,9 +221,9 @@ func TestGetProcessors_ContainsMainConfigurations(t *testing.T) { // and interpreters are registered and available for testing. all := maps.Keys(getProcessors()) wanted := []string{ - "opera/geth", "opera/lfvm", "opera/evmzero", - "floria/geth", "floria/lfvm", "floria/evmzero", - "geth-sonic/geth", "geth-sonic/lfvm", "geth-sonic/evmzero", + "opera/geth", "opera/lfvm", "opera/sfvm", "opera/evmzero", + "floria/geth", "floria/lfvm", "floria/sfvm", "floria/evmzero", + "geth-sonic/geth", "geth-sonic/lfvm", "geth-sonic/sfvm", "geth-sonic/evmzero", } for _, n := range wanted { if !slices.Contains(all, n) {