core/vm: add optional containerSections

This commit is contained in:
Marius van der Wijden 2024-04-12 16:06:56 +02:00
parent 9354ef082b
commit 1dccd90224
7 changed files with 97 additions and 41 deletions

View file

@ -31,7 +31,7 @@ import (
) )
func init() { func init() {
jt = vm.NewShanghaiEOFInstructionSetForTesting() jt = vm.NewPragueEOFInstructionSetForTesting()
} }
var ( var (

View file

@ -299,7 +299,7 @@ func applyEOFChecks(prestate *Prestate, chainConfig *params.ChainConfig) error {
) )
err = c.UnmarshalBinary(acc.Code) err = c.UnmarshalBinary(acc.Code)
if err == nil { if err == nil {
jt := vm.NewShanghaiEOFInstructionSetForTesting() jt := vm.NewPragueEOFInstructionSetForTesting()
err = c.ValidateCode(&jt) err = c.ValidateCode(&jt)
} }
if err != nil { if err != nil {

View file

@ -31,7 +31,8 @@ const (
kindTypes = 1 kindTypes = 1
kindCode = 2 kindCode = 2
kindData = 3 kindContainer = 3
kindData = 4
eofFormatByte = 0xef eofFormatByte = 0xef
eof1Version = 1 eof1Version = 1
@ -39,6 +40,7 @@ const (
maxInputItems = 127 maxInputItems = 127
maxOutputItems = 127 maxOutputItems = 127
maxStackHeight = 1023 maxStackHeight = 1023
maxContainerSections = 256
) )
var ( var (
@ -49,6 +51,7 @@ var (
ErrMissingCodeHeader = errors.New("missing code header") ErrMissingCodeHeader = errors.New("missing code header")
ErrInvalidCodeHeader = errors.New("invalid code header") ErrInvalidCodeHeader = errors.New("invalid code header")
ErrInvalidCodeSize = errors.New("invalid code size") ErrInvalidCodeSize = errors.New("invalid code size")
ErrInvalidContainerSectionSize = errors.New("invalid container section size")
ErrMissingDataHeader = errors.New("missing data header") ErrMissingDataHeader = errors.New("missing data header")
ErrMissingTerminator = errors.New("missing header terminator") ErrMissingTerminator = errors.New("missing header terminator")
ErrTooManyInputs = errors.New("invalid type content, too many inputs") ErrTooManyInputs = errors.New("invalid type content, too many inputs")
@ -80,6 +83,7 @@ func isEOFVersion1(code []byte) bool {
type Container struct { type Container struct {
Types []*FunctionMetadata Types []*FunctionMetadata
Code [][]byte Code [][]byte
ContainerSections [][]byte
Data []byte Data []byte
} }
@ -105,6 +109,13 @@ func (c *Container) MarshalBinary() []byte {
for _, code := range c.Code { for _, code := range c.Code {
b = binary.BigEndian.AppendUint16(b, uint16(len(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 = append(b, kindData)
b = binary.BigEndian.AppendUint16(b, uint16(len(c.Data))) b = binary.BigEndian.AppendUint16(b, uint16(len(c.Data)))
b = append(b, 0) // terminator b = append(b, 0) // terminator
@ -116,6 +127,9 @@ func (c *Container) MarshalBinary() []byte {
for _, code := range c.Code { for _, code := range c.Code {
b = append(b, code...) b = append(b, code...)
} }
for _, section := range c.ContainerSections {
b = append(b, section...)
}
b = append(b, c.Data...) b = append(b, c.Data...)
return b 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)) 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. // Parse data section header.
offsetDataKind := offsetCodeKind + 2 + 2*len(codeSizes) + 1 kind, dataSize, err = parseSection(b, offset)
kind, dataSize, err = parseSection(b, offsetDataKind)
if err != nil { if err != nil {
return err return err
} }
@ -177,7 +206,7 @@ func (c *Container) UnmarshalBinary(b []byte) error {
} }
// Check for terminator. // Check for terminator.
offsetTerminator := offsetDataKind + 3 offsetTerminator := offset + 3
if len(b) < offsetTerminator { if len(b) < offsetTerminator {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
@ -187,6 +216,9 @@ func (c *Container) UnmarshalBinary(b []byte) error {
// Verify overall container size. // Verify overall container size.
expectedSize := offsetTerminator + typesSize + sum(codeSizes) + dataSize + 1 expectedSize := offsetTerminator + typesSize + sum(codeSizes) + dataSize + 1
if len(containerSizes) != 0 {
expectedSize += sum(containerSizes)
}
if len(b) != expectedSize { if len(b) != expectedSize {
return fmt.Errorf("%w: have %d, want %d", ErrInvalidContainerSize, 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 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. // Parse data section.
c.Data = b[idx : idx+dataSize] c.Data = b[idx : idx+dataSize]

View file

@ -35,6 +35,14 @@ func TestEOFMarshaling(t *testing.T) {
Data: []byte{0x01, 0x02, 0x03}, 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{ want: Container{
Types: []*FunctionMetadata{ Types: []*FunctionMetadata{

View file

@ -152,7 +152,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
} }
} }
evm.Config.ExtraEips = extraEips 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 // Run loops and evaluates the contract's code with the given input data and returns

View file

@ -64,7 +64,7 @@ var (
shanghaiInstructionSet = newShanghaiInstructionSet() shanghaiInstructionSet = newShanghaiInstructionSet()
cancunInstructionSet = newCancunInstructionSet() cancunInstructionSet = newCancunInstructionSet()
verkleInstructionSet = newVerkleInstructionSet() verkleInstructionSet = newVerkleInstructionSet()
shanghaiEOFInstructionSet = newShanghaiEOFInstructionSet() pragueEOFInstructionSet = newPragueEOFInstructionSet()
) )
// JumpTable contains the EVM opcodes supported at a given fork. // JumpTable contains the EVM opcodes supported at a given fork.
@ -94,6 +94,16 @@ func newVerkleInstructionSet() JumpTable {
return validate(instructionSet) return validate(instructionSet)
} }
func NewPragueEOFInstructionSetForTesting() JumpTable {
return newPragueEOFInstructionSet()
}
func newPragueEOFInstructionSet() JumpTable {
instructionSet := newCancunInstructionSet()
enableEOF(&instructionSet)
return validate(instructionSet)
}
func newCancunInstructionSet() JumpTable { func newCancunInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet() instructionSet := newShanghaiInstructionSet()
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode) enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
@ -105,10 +115,6 @@ func newCancunInstructionSet() JumpTable {
return validate(instructionSet) return validate(instructionSet)
} }
func NewShanghaiEOFInstructionSetForTesting() JumpTable {
return newShanghaiEOFInstructionSet()
}
func newShanghaiInstructionSet() JumpTable { func newShanghaiInstructionSet() JumpTable {
instructionSet := newMergeInstructionSet() instructionSet := newMergeInstructionSet()
enable3855(&instructionSet) // PUSH0 instruction enable3855(&instructionSet) // PUSH0 instruction
@ -117,12 +123,6 @@ func newShanghaiInstructionSet() JumpTable {
return validate(instructionSet) return validate(instructionSet)
} }
func newShanghaiEOFInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
enableEOF(&instructionSet)
return validate(instructionSet)
}
func newMergeInstructionSet() JumpTable { func newMergeInstructionSet() JumpTable {
instructionSet := newLondonInstructionSet() instructionSet := newLondonInstructionSet()
instructionSet[PREVRANDAO] = &operation{ instructionSet[PREVRANDAO] = &operation{

View file

@ -242,7 +242,7 @@ func TestValidateCode(t *testing.T) {
metadata: []*FunctionMetadata{{Input: 0, Output: 0, MaxStackHeight: 2}, {Input: 2, Output: 1, MaxStackHeight: 2}}, 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) { 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) t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err)
} }