From 1dccd90224557b95e341f7e533542bb347a416a7 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 12 Apr 2024 16:06:56 +0200 Subject: [PATCH] core/vm: add optional containerSections --- cmd/evm/eofparser.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 2 +- core/vm/eof.go | 100 ++++++++++++++++++------- core/vm/eof_test.go | 8 ++ core/vm/interpreter.go | 2 +- core/vm/jump_table.go | 22 +++--- core/vm/validate_test.go | 2 +- 7 files changed, 97 insertions(+), 41 deletions(-) diff --git a/cmd/evm/eofparser.go b/cmd/evm/eofparser.go index e0a1140064..7cc27c0b34 100644 --- a/cmd/evm/eofparser.go +++ b/cmd/evm/eofparser.go @@ -31,7 +31,7 @@ import ( ) func init() { - jt = vm.NewShanghaiEOFInstructionSetForTesting() + jt = vm.NewPragueEOFInstructionSetForTesting() } var ( diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index c801b10d31..d4946d2545 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -299,7 +299,7 @@ func applyEOFChecks(prestate *Prestate, chainConfig *params.ChainConfig) error { ) err = c.UnmarshalBinary(acc.Code) if err == nil { - jt := vm.NewShanghaiEOFInstructionSetForTesting() + jt := vm.NewPragueEOFInstructionSetForTesting() err = c.ValidateCode(&jt) } if err != nil { diff --git a/core/vm/eof.go b/core/vm/eof.go index 23de82b5f6..269721e7e2 100644 --- a/core/vm/eof.go +++ b/core/vm/eof.go @@ -29,33 +29,36 @@ const ( offsetTypesKind = 3 offsetCodeKind = 6 - kindTypes = 1 - kindCode = 2 - kindData = 3 + kindTypes = 1 + kindCode = 2 + kindContainer = 3 + kindData = 4 eofFormatByte = 0xef eof1Version = 1 - maxInputItems = 127 - maxOutputItems = 127 - maxStackHeight = 1023 + maxInputItems = 127 + maxOutputItems = 127 + maxStackHeight = 1023 + maxContainerSections = 256 ) var ( - ErrInvalidMagic = errors.New("invalid magic") - ErrInvalidVersion = errors.New("invalid version") - ErrMissingTypeHeader = errors.New("missing type header") - ErrInvalidTypeSize = errors.New("invalid type section size") - ErrMissingCodeHeader = errors.New("missing code header") - ErrInvalidCodeHeader = errors.New("invalid code header") - ErrInvalidCodeSize = errors.New("invalid code size") - ErrMissingDataHeader = errors.New("missing data header") - ErrMissingTerminator = errors.New("missing header terminator") - ErrTooManyInputs = errors.New("invalid type content, too many inputs") - ErrTooManyOutputs = errors.New("invalid type content, too many inputs") - ErrInvalidSection0Type = errors.New("invalid section 0 type, input and output should be zero") - ErrTooLargeMaxStackHeight = errors.New("invalid type content, max stack height exceeds limit") - ErrInvalidContainerSize = errors.New("invalid container size") + ErrInvalidMagic = errors.New("invalid magic") + ErrInvalidVersion = errors.New("invalid version") + ErrMissingTypeHeader = errors.New("missing type header") + ErrInvalidTypeSize = errors.New("invalid type section size") + ErrMissingCodeHeader = errors.New("missing code header") + ErrInvalidCodeHeader = errors.New("invalid code header") + ErrInvalidCodeSize = errors.New("invalid code size") + ErrInvalidContainerSectionSize = errors.New("invalid container section size") + ErrMissingDataHeader = errors.New("missing data header") + ErrMissingTerminator = errors.New("missing header terminator") + ErrTooManyInputs = errors.New("invalid type content, too many inputs") + ErrTooManyOutputs = errors.New("invalid type content, too many inputs") + ErrInvalidSection0Type = errors.New("invalid section 0 type, input and output should be zero") + ErrTooLargeMaxStackHeight = errors.New("invalid type content, max stack height exceeds limit") + ErrInvalidContainerSize = errors.New("invalid container size") ) var eofMagic = []byte{0xef, 0x00} @@ -78,9 +81,10 @@ func isEOFVersion1(code []byte) bool { // Container is an EOF container object. type Container struct { - Types []*FunctionMetadata - Code [][]byte - Data []byte + Types []*FunctionMetadata + Code [][]byte + ContainerSections [][]byte + Data []byte } // FunctionMetadata is an EOF function signature. @@ -105,6 +109,13 @@ func (c *Container) MarshalBinary() []byte { for _, code := range c.Code { b = binary.BigEndian.AppendUint16(b, uint16(len(code))) } + if len(c.ContainerSections) != 0 { + b = append(b, kindContainer) + b = binary.BigEndian.AppendUint16(b, uint16(len(c.ContainerSections))) + for _, section := range c.ContainerSections { + b = binary.BigEndian.AppendUint16(b, uint16(len(section))) + } + } b = append(b, kindData) b = binary.BigEndian.AppendUint16(b, uint16(len(c.Data))) b = append(b, 0) // terminator @@ -116,6 +127,9 @@ func (c *Container) MarshalBinary() []byte { for _, code := range c.Code { b = append(b, code...) } + for _, section := range c.ContainerSections { + b = append(b, section...) + } b = append(b, c.Data...) return b @@ -166,9 +180,24 @@ func (c *Container) UnmarshalBinary(b []byte) error { return fmt.Errorf("%w: mismatch of code sections cound and type signatures, types %d, code %d", ErrInvalidCodeSize, typesSize/4, len(codeSizes)) } + // Parse container section header. + offset := offsetCodeKind + 2 + 2*len(codeSizes) + 1 + kind, containerSizes, err := parseSectionList(b, offset) + if err != nil { + return err + } + // The container section is optional, only unmarshal if container section is set. + if kind == kindContainer { + offset = offset + 2 + 2*len(containerSizes) + 1 + } else { + // empty out falsly parsed container sizes + // TODO (MariusVanDerWijden): clean this up, read the kind first before parsing the section list + // and if the kind is not KindContainer, just ignore it. + containerSizes = make([]int, 0) + } + // Parse data section header. - offsetDataKind := offsetCodeKind + 2 + 2*len(codeSizes) + 1 - kind, dataSize, err = parseSection(b, offsetDataKind) + kind, dataSize, err = parseSection(b, offset) if err != nil { return err } @@ -177,7 +206,7 @@ func (c *Container) UnmarshalBinary(b []byte) error { } // Check for terminator. - offsetTerminator := offsetDataKind + 3 + offsetTerminator := offset + 3 if len(b) < offsetTerminator { return io.ErrUnexpectedEOF } @@ -187,6 +216,9 @@ func (c *Container) UnmarshalBinary(b []byte) error { // Verify overall container size. expectedSize := offsetTerminator + typesSize + sum(codeSizes) + dataSize + 1 + if len(containerSizes) != 0 { + expectedSize += sum(containerSizes) + } if len(b) != expectedSize { return fmt.Errorf("%w: have %d, want %d", ErrInvalidContainerSize, len(b), expectedSize) } @@ -228,6 +260,22 @@ func (c *Container) UnmarshalBinary(b []byte) error { } c.Code = code + // Parse the optional container sizes. + if len(containerSizes) != 0 { + if len(containerSizes) > maxContainerSections { + return fmt.Errorf("%w number of container section exceed: %v: have %v", ErrInvalidContainerSectionSize, maxContainerSections, len(containerSizes)) + } + container := make([][]byte, len(containerSizes)) + for i, size := range containerSizes { + if size == 0 { + return fmt.Errorf("%w for section %d: size must not be 0", ErrInvalidContainerSectionSize, i) + } + container[i] = b[idx : idx+size] + idx += size + } + c.ContainerSections = container + } + // Parse data section. c.Data = b[idx : idx+dataSize] diff --git a/core/vm/eof_test.go b/core/vm/eof_test.go index e8638128c2..6adf0450b1 100644 --- a/core/vm/eof_test.go +++ b/core/vm/eof_test.go @@ -35,6 +35,14 @@ func TestEOFMarshaling(t *testing.T) { Data: []byte{0x01, 0x02, 0x03}, }, }, + { + want: Container{ + Types: []*FunctionMetadata{{Input: 0, Output: 0, MaxStackHeight: 1}}, + Code: [][]byte{common.Hex2Bytes("604200")}, + ContainerSections: [][]byte{common.Hex2Bytes("604200")}, + Data: []byte{0x01, 0x02, 0x03}, + }, + }, { want: Container{ Types: []*FunctionMetadata{ diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index f2c02ea90a..13867ca072 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -152,7 +152,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { } } evm.Config.ExtraEips = extraEips - return &EVMInterpreter{evm: evm, table: table, tableEOF: &shanghaiEOFInstructionSet} + return &EVMInterpreter{evm: evm, table: table, tableEOF: &pragueEOFInstructionSet} } // Run loops and evaluates the contract's code with the given input data and returns diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index ad23ca33ae..2d01dfaeac 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -64,7 +64,7 @@ var ( shanghaiInstructionSet = newShanghaiInstructionSet() cancunInstructionSet = newCancunInstructionSet() verkleInstructionSet = newVerkleInstructionSet() - shanghaiEOFInstructionSet = newShanghaiEOFInstructionSet() + pragueEOFInstructionSet = newPragueEOFInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -94,6 +94,16 @@ func newVerkleInstructionSet() JumpTable { return validate(instructionSet) } +func NewPragueEOFInstructionSetForTesting() JumpTable { + return newPragueEOFInstructionSet() +} + +func newPragueEOFInstructionSet() JumpTable { + instructionSet := newCancunInstructionSet() + enableEOF(&instructionSet) + return validate(instructionSet) +} + func newCancunInstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode) @@ -105,10 +115,6 @@ func newCancunInstructionSet() JumpTable { return validate(instructionSet) } -func NewShanghaiEOFInstructionSetForTesting() JumpTable { - return newShanghaiEOFInstructionSet() -} - func newShanghaiInstructionSet() JumpTable { instructionSet := newMergeInstructionSet() enable3855(&instructionSet) // PUSH0 instruction @@ -117,12 +123,6 @@ func newShanghaiInstructionSet() JumpTable { return validate(instructionSet) } -func newShanghaiEOFInstructionSet() JumpTable { - instructionSet := newShanghaiInstructionSet() - enableEOF(&instructionSet) - return validate(instructionSet) -} - func newMergeInstructionSet() JumpTable { instructionSet := newLondonInstructionSet() instructionSet[PREVRANDAO] = &operation{ diff --git a/core/vm/validate_test.go b/core/vm/validate_test.go index 57114196e7..b2e706e61d 100644 --- a/core/vm/validate_test.go +++ b/core/vm/validate_test.go @@ -242,7 +242,7 @@ func TestValidateCode(t *testing.T) { metadata: []*FunctionMetadata{{Input: 0, Output: 0, MaxStackHeight: 2}, {Input: 2, Output: 1, MaxStackHeight: 2}}, }, } { - err := validateCode(test.code, test.section, test.metadata, &shanghaiEOFInstructionSet) + err := validateCode(test.code, test.section, test.metadata, &pragueEOFInstructionSet) if !errors.Is(err, test.err) { t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err) }