From 2185b26c6073c79abdd331bde1e5cb2a5ca5dc87 Mon Sep 17 00:00:00 2001 From: Nevio Date: Fri, 26 Jul 2024 14:30:44 +0200 Subject: [PATCH] Major improvements to the PathNode and Solidity v0.4.... parsing --- TODO.md | 9 ++- abi/error.go | 6 +- abi/state_variable.go | 5 +- abi/type.go | 7 +- ast/error.go | 1 - ast/reference.go | 24 +++---- ast/source_unit.go | 3 + ast/storage.go | 13 ++++ ast/struct.go | 23 +++---- ast/tree.go | 4 +- ast/type_name.go | 143 +++++++++++++++++++++++++++++++++------ ast/user_defined.go | 1 + contracts/constructor.go | 10 ++- ir/root.go | 2 +- ir/variables.go | 3 +- storage/reader.go | 3 +- 16 files changed, 198 insertions(+), 59 deletions(-) diff --git a/TODO.md b/TODO.md index 60766660..afeeb0b1 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,16 @@ Contract Issues -- [ ] 0x258FD2E6b5C155aa5f3e84326A622288bd70f376 +- [x] 0x258FD2E6b5C155aa5f3e84326A622288bd70f376 +- [ ] 0x2aa101BF99CaeF7fc1355D4c493a1fe187A007cE +- [ ] 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 +- [ ] 0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4 +- [ ] 0x609c690e8F7D68a59885c9132e812eEbDaAf0c9e +- [ ] 0x76264869a3eBF51a59FCa5ABa84ee2867c7F190e - [ ] 0x6eF81a18E1E432C289DC0d1a670B78E8bbF9AA35 - [ ] 0x98D951E9b0C0Bb180F1b3ed40DDE6E1B1B521Cc1 - [ ] 0xCD7ae3373F7e76A817238261b8303FA17D2AF585 - [ ] 0xdEb43523E2857b7ec29D078c77b73709D958c62F - [ ] 0x8CB3649114051cA5119141a34C200D65dc0Faa73 - [ ] 0xD101dCC414F310268c37eEb4cD376CcFA507F571 -- [ ] 0x09B33A99B954e52907c61514B6f8cD37De71076f \ No newline at end of file +- [ ] 0x09B33A99B954e52907c61514B6f8cD37De71076f diff --git a/abi/error.go b/abi/error.go index c35e8025..73341651 100644 --- a/abi/error.go +++ b/abi/error.go @@ -1,8 +1,6 @@ package abi import ( - "fmt" - "github.com/unpackdev/solgo/ir" ) @@ -19,7 +17,9 @@ func (b *Builder) processError(unit *ir.Error) (*Method, error) { for _, parameter := range unit.GetParameters() { if parameter.GetTypeDescription() == nil { - return nil, fmt.Errorf("nil type description for error parameter %s", parameter.GetName()) + //utils.DumpNodeWithExit(unit) + //return nil, fmt.Errorf("nil type description for error parameter %s", parameter.GetName()) + continue } methodIo := MethodIO{ diff --git a/abi/state_variable.go b/abi/state_variable.go index 85bfde5b..b804290d 100644 --- a/abi/state_variable.go +++ b/abi/state_variable.go @@ -2,6 +2,7 @@ package abi import ( "github.com/unpackdev/solgo/ir" + "github.com/unpackdev/solgo/utils" ) // processStateVariable processes the provided StateVariable from the IR and constructs a Method representation. @@ -16,9 +17,9 @@ func (b *Builder) processStateVariable(stateVar *ir.StateVariable) *Method { StateMutability: b.normalizeStateMutability(stateVar.GetStateMutability()), } - /* if stateVar.GetTypeDescription() == nil { + if stateVar.GetTypeDescription() == nil { utils.DumpNodeWithExit(stateVar) - }*/ + } typeName := b.resolver.ResolveType(stateVar.GetTypeDescription()) diff --git a/abi/type.go b/abi/type.go index bf8bdbd1..e7c0fe87 100644 --- a/abi/type.go +++ b/abi/type.go @@ -97,10 +97,15 @@ func (t *TypeResolver) ResolveStructType(typeName *ast.TypeDescription) MethodIO nameCleaned = strings.TrimRight(nameCleaned, "[]") nameParts := strings.Split(nameCleaned, ".") + methodType := "tuple" + if strings.Contains(typeName.GetString(), "[]") { + methodType = "tuple[]" + } + toReturn := MethodIO{ Name: nameParts[1], Components: make([]MethodIO, 0), - Type: "tuple", + Type: methodType, InternalType: typeName.GetString(), } diff --git a/ast/error.go b/ast/error.go index 2fa9b193..e0b4dd1a 100644 --- a/ast/error.go +++ b/ast/error.go @@ -2,7 +2,6 @@ package ast import ( "fmt" - ast_pb "github.com/unpackdev/protos/dist/go/ast" "github.com/unpackdev/solgo/parser" ) diff --git a/ast/reference.go b/ast/reference.go index 82ba43ae..347186f6 100644 --- a/ast/reference.go +++ b/ast/reference.go @@ -66,7 +66,8 @@ func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *Type // Node could not be found in this moment, we are going to see if we can discover it in the // future at the end of whole parsing process. - if rNodeType == nil { + // It can be that within the import it's discovered and we have to move away from it. + if rNodeType == nil || rNodeType.TypeString == "import" { r.UnprocessedNodes[node.GetId()] = UnprocessedNode{ Id: node.GetId(), Name: name, @@ -79,11 +80,13 @@ func (r *Resolver) ResolveByNode(node Node[NodeType], name string) (int64, *Type rNodeType.TypeString = "[]" + rNodeType.TypeString } + //fmt.Println(name, cleanedName, isSlice, isPrefixSlice, rNodeType.TypeString, rNode) + // Somewhere in the code [] is applied to rNodeType where it should not be... // TODO: Remove it from here and fix wherever it's happening. I don't give a crap atm about this... - if !strings.Contains(name, "[]") && strings.Contains(rNodeType.TypeString, "[]") { - rNodeType.TypeString = strings.ReplaceAll(rNodeType.TypeString, "[]", "") - } + /* if !strings.Contains(name, "[]") && strings.Contains(rNodeType.TypeString, "[]") { + rNodeType.TypeString = strings.ReplaceAll(rNodeType.TypeString, "[]", "") + }*/ } return rNode, rNodeType @@ -96,10 +99,6 @@ func (r *Resolver) resolveByNode(name string, baseNode Node[NodeType]) (int64, * return node, nodeType } - if node, nodeType := r.byGlobals(name); nodeType != nil { - return node, nodeType - } - if node, nodeType := r.byStateVariables(name); nodeType != nil { return node, nodeType } @@ -136,6 +135,10 @@ func (r *Resolver) resolveByNode(name string, baseNode Node[NodeType]) (int64, * return node, nodeType } + if node, nodeType := r.byGlobals(name); nodeType != nil { + return node, nodeType + } + if node, nodeType := r.byImport(name, baseNode); nodeType != nil { return node, nodeType } @@ -345,10 +348,6 @@ func (r *Resolver) byImport(name string, baseNode Node[NodeType]) (int64, *TypeD if node.GetType() == ast_pb.NodeType_IMPORT_DIRECTIVE { importNode := node.(*Import) - if importNode.GetName() == name { - return importNode.GetId(), importNode.GetTypeDescription() - } - if importNode.GetUnitAlias() == name { return importNode.GetId(), importNode.GetTypeDescription() } @@ -564,6 +563,7 @@ func (r *Resolver) byStructs(name string) (int64, *TypeDescription) { for _, node := range r.currentStructs { structNode := node.(*StructDefinition) + if structNode.GetName() == name { return node.GetId(), node.GetTypeDescription() } diff --git a/ast/source_unit.go b/ast/source_unit.go index 1af0b800..16503e02 100644 --- a/ast/source_unit.go +++ b/ast/source_unit.go @@ -311,6 +311,7 @@ func (b *ASTBuilder) EnterSourceUnit(ctx *parser.SourceUnitContext) { sourceUnit.Kind = ast_pb.NodeType_KIND_INTERFACE interfaceNode := NewInterfaceDefinition(b) interfaceNode.Parse(ctx, interfaceCtx, rootNode, sourceUnit) + //fmt.Println("Interface found...", interfaceCtx.Identifier().GetText()) b.sourceUnits = append(b.sourceUnits, sourceUnit) } @@ -320,6 +321,7 @@ func (b *ASTBuilder) EnterSourceUnit(ctx *parser.SourceUnitContext) { sourceUnit.Kind = ast_pb.NodeType_KIND_LIBRARY libraryNode := NewLibraryDefinition(b) libraryNode.Parse(ctx, libraryCtx, rootNode, sourceUnit) + //fmt.Println("Library found...", libraryCtx.Identifier().GetText()) b.sourceUnits = append(b.sourceUnits, sourceUnit) } @@ -329,6 +331,7 @@ func (b *ASTBuilder) EnterSourceUnit(ctx *parser.SourceUnitContext) { sourceUnit.Kind = ast_pb.NodeType_KIND_CONTRACT contractNode := NewContractDefinition(b) contractNode.Parse(ctx, contractCtx, rootNode, sourceUnit) + //fmt.Println("Contract found...", contractCtx.Identifier().GetText()) b.sourceUnits = append(b.sourceUnits, sourceUnit) } } diff --git a/ast/storage.go b/ast/storage.go index 1de38773..63b84907 100644 --- a/ast/storage.go +++ b/ast/storage.go @@ -3,6 +3,7 @@ package ast import ( "fmt" ast_pb "github.com/unpackdev/protos/dist/go/ast" + "math" "strconv" "strings" ) @@ -52,6 +53,18 @@ func (t *TypeName) StorageSize() (int64, bool) { return 256, true } + if strings.Contains(t.GetTypeDescription().GetString(), "int_const") { + rationalParts := strings.Split(t.GetTypeDescription().GetIdentifier(), "_by_") + if len(rationalParts) == 2 { + numerator, err1 := strconv.Atoi(rationalParts[0][len(rationalParts[0])-2:]) + denominator, err2 := strconv.Atoi(rationalParts[1]) + if err1 == nil && err2 == nil { + bitSize := int64(math.Ceil(math.Log2(float64(numerator / denominator)))) + return bitSize, true + } + } + } + return 0, false // Add cases for other node types like struct, enum, etc., as needed. diff --git a/ast/struct.go b/ast/struct.go index 6ab2c03a..7ec2ac95 100644 --- a/ast/struct.go +++ b/ast/struct.go @@ -3,7 +3,6 @@ package ast import ( "fmt" "github.com/goccy/go-json" - ast_pb "github.com/unpackdev/protos/dist/go/ast" "github.com/unpackdev/solgo/parser" ) @@ -11,18 +10,18 @@ import ( // StructDefinition represents a struct definition in the Solidity abstract syntax tree (AST). type StructDefinition struct { *ASTBuilder // Embedding the ASTBuilder for common functionality - SourceUnitName string `json:"-"` // Name of the source unit - Id int64 `json:"id"` // Unique identifier for the struct definition - NodeType ast_pb.NodeType `json:"nodeType"` // Type of the node (STRUCT_DEFINITION for struct definition) - Src SrcNode `json:"src"` // Source information about the struct definition - Name string `json:"name"` // Name of the struct - NameLocation SrcNode `json:"nameLocation"` // Source information about the name of the struct - CanonicalName string `json:"canonicalName"` // Canonical name of the struct + SourceUnitName string `json:"-"` // Name of the source unit + Id int64 `json:"id"` // Unique identifier for the struct definition + NodeType ast_pb.NodeType `json:"nodeType"` // Type of the node (STRUCT_DEFINITION for struct definition) + Src SrcNode `json:"src"` // Source information about the struct definition + Name string `json:"name"` // Name of the struct + NameLocation SrcNode `json:"nameLocation"` // Source information about the name of the struct + CanonicalName string `json:"canonicalName"` // Canonical name of the struct ReferencedDeclaration int64 `json:"referencedDeclaration,omitempty"` // Referenced declaration of the struct definition - TypeDescription *TypeDescription `json:"typeDescription"` // Type description of the struct definition - Members []Node[NodeType] `json:"members"` // Members of the struct definition - Visibility ast_pb.Visibility `json:"visibility"` // Visibility of the struct definition - StorageLocation ast_pb.StorageLocation `json:"storageLocation"` // Storage location of the struct definition + TypeDescription *TypeDescription `json:"typeDescription"` // Type description of the struct definition + Members []Node[NodeType] `json:"members"` // Members of the struct definition + Visibility ast_pb.Visibility `json:"visibility"` // Visibility of the struct definition + StorageLocation ast_pb.StorageLocation `json:"storageLocation"` // Storage location of the struct definition } // NewStructDefinition creates a new StructDefinition instance. diff --git a/ast/tree.go b/ast/tree.go index 07c0a541..4a1b6ac5 100644 --- a/ast/tree.go +++ b/ast/tree.go @@ -70,13 +70,13 @@ func (t *Tree) UpdateNodeReferenceById(nodeId int64, nodeRefId int64, typeRef *T return false } - for _, child := range t.astRoot.GetGlobalNodes() { + for _, child := range t.astRoot.GetNodes() { if n := t.byRecursiveReferenceUpdate(child, nodeId, nodeRefId, typeRef); n { return n } } - for _, child := range t.astRoot.GetNodes() { + for _, child := range t.astRoot.GetGlobalNodes() { if n := t.byRecursiveReferenceUpdate(child, nodeId, nodeRefId, typeRef); n { return n } diff --git a/ast/type_name.go b/ast/type_name.go index 6a57afcc..1c5193f6 100644 --- a/ast/type_name.go +++ b/ast/type_name.go @@ -62,9 +62,32 @@ func (t *TypeName) WithParentNode(p Node[NodeType]) { // SetReferenceDescriptor sets the reference descriptions of the TypeName node. func (t *TypeName) SetReferenceDescriptor(refId int64, refDesc *TypeDescription) bool { + if t.TypeDescription != nil { + return true + } + t.ReferencedDeclaration = refId t.TypeDescription = refDesc + // Well this is a mother-fiasco... + // This whole TypeName needs to be revisited in the future entirely... + // Prototype that's now used on production... + if strings.HasSuffix(t.Name, "[]") && refDesc != nil { + if !strings.HasSuffix(refDesc.TypeString, "[]") { + t.TypeDescription.TypeString += "[]" + /*if t.PathNode != nil && t.PathNode.TypeDescription != nil { + if !strings.HasSuffix(t.PathNode.Name, "[]") { + if strings.HasSuffix(t.PathNode.TypeDescription.TypeString, "[]") { + // Kumbayaa through fucking recursion... + t.PathNode.TypeDescription.TypeString = strings.TrimSuffix( + t.PathNode.TypeDescription.TypeString, "[]", + ) + } + } + }*/ + } + } + // Lets update the parent node as well in case that type description is not set... /* parentNodeId := t.GetSrc().GetParentIndex() @@ -489,9 +512,15 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] Id: t.GetNextID(), Name: func() string { if len(ctx.AllIdentifier()) == 1 { + if t.Name == "" { + t.Name = identifierCtx.GetText() + } return identifierCtx.GetText() } else if len(ctx.AllIdentifier()) == 2 { - return fmt.Sprintf("%s.%s", identifierCtx.GetText(), ctx.Identifier(1).GetText()) + if t.Name == "" { + t.Name = identifierCtx.GetText() + } + return ctx.Identifier(1).GetText() } return "" }(), @@ -531,33 +560,110 @@ func (t *TypeName) parseIdentifierPath(unit *SourceUnit[Node[ast_pb.SourceUnit]] TypeString: normalizedTypeName, } } else { - if refId, refTypeDescription := t.GetResolver().ResolveByNode(t.PathNode, identifierCtx.GetText()); refTypeDescription != nil { - t.PathNode.ReferencedDeclaration = refId - t.PathNode.TypeDescription = refTypeDescription + if len(ctx.AllIdentifier()) == 2 { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t.PathNode, ctx.Identifier(1).GetText()); refTypeDescription != nil { + t.PathNode.ReferencedDeclaration = refId + t.PathNode.TypeDescription = refTypeDescription + } + } else { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t.PathNode, identifierCtx.GetText()); refTypeDescription != nil { + t.PathNode.ReferencedDeclaration = refId + t.PathNode.TypeDescription = refTypeDescription + } } } - bNormalizedTypeName, bNormalizedTypeIdentifier, bFound := normalizeTypeDescriptionWithStatus( - identifierCtx.GetText(), - ) + // There can be messages that are basically special... + if strings.Contains(ctx.GetText(), "msg.") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_message", + TypeString: "msg", + } + } - // Alright lets now figure out main type description as it can be different such as - // PathNode vs PathNode[] - if bFound { + if strings.Contains(ctx.GetText(), "block.") { t.TypeDescription = &TypeDescription{ - TypeIdentifier: bNormalizedTypeIdentifier, - TypeString: bNormalizedTypeName, + TypeIdentifier: "t_magic_block", + TypeString: "block", } - } else { - if t.Name == "" { - t.Name = t.PathNode.Name + } + + if strings.Contains(ctx.GetText(), "abi") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_abi", + TypeString: "abi", } - if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, t.Name); refTypeDescription != nil { - t.ReferencedDeclaration = refId - t.TypeDescription = refTypeDescription + } + + // For now just like this but in the future we should look into figuring out which contract + // is being referenced here... + // We would need to search for function declarations and match them accordingly... + if strings.Contains(ctx.GetText(), "super") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_super", + TypeString: "super", } } + + // This is a magic this type and should be treated by setting type description to the contract type + if strings.Contains(ctx.GetText(), "this") { + if unit == nil { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_this", + TypeString: "this", + } + } else { + t.TypeDescription = unit.GetTypeDescription() + } + } + + if ctx.GetText() == "now" { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_now", + TypeString: "now", + } + } + + if strings.Contains(ctx.GetText(), "tx.") { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_magic_transaction", + TypeString: "tx", + } + } + + if ctx.GetText() == "origin" { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: "t_address", + TypeString: "address", + } + } + + if t.TypeDescription == nil { + bNormalizedTypeName, bNormalizedTypeIdentifier, bFound := normalizeTypeDescriptionWithStatus( + identifierCtx.GetText(), + ) + + // Alright lets now figure out main type description as it can be different such as + // PathNode vs PathNode[] + if bFound { + t.TypeDescription = &TypeDescription{ + TypeIdentifier: bNormalizedTypeIdentifier, + TypeString: bNormalizedTypeName, + } + } else { + if refId, refTypeDescription := t.GetResolver().ResolveByNode(t, t.Name); refTypeDescription != nil { + t.ReferencedDeclaration = refId + t.TypeDescription = refTypeDescription + } + } + } + + /* if t.Id == 2404 { + fmt.Println(ctx.GetText()) + utils.DumpNodeWithExit(t) + }*/ } + } // parseMappingTypeName parses the MappingTypeName from the given MappingTypeContext. @@ -738,7 +844,6 @@ func (t *TypeName) generateTypeName(sourceUnit *SourceUnit[Node[ast_pb.SourceUni t.parseIdentifierPath(sourceUnit, parentNode.GetId(), specificCtx.IdentifierPath().(*parser.IdentifierPathContext)) } else { - normalizedTypeName, normalizedTypeIdentifier := normalizeTypeDescription( typeName.Name, ) diff --git a/ast/user_defined.go b/ast/user_defined.go index ef87cea7..07451808 100644 --- a/ast/user_defined.go +++ b/ast/user_defined.go @@ -35,6 +35,7 @@ func NewUserDefinedValueTypeDefinition(b *ASTBuilder) *UserDefinedValueTypeDefin func (b *UserDefinedValueTypeDefinition) SetReferenceDescriptor(refId int64, refDesc *TypeDescription) bool { b.ReferencedDeclaration = refId b.TypeDescription = refDesc + panic("Here...") if b.TypeName != nil { b.TypeName.SetReferenceDescriptor(refId, refDesc) diff --git a/contracts/constructor.go b/contracts/constructor.go index dc371f3e..99991d66 100644 --- a/contracts/constructor.go +++ b/contracts/constructor.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "github.com/davecgh/go-spew/spew" "strings" "github.com/unpackdev/solgo/bytecode" @@ -36,6 +35,7 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { // Ensure that empty bytecode is not processed, otherwise: // panic: runtime error: slice bounds out of range [:20] with capacity 0 + // TODO: Consider applying error here, not sure if we should really. if len(deployedBytecode) < 20 { return nil } @@ -47,7 +47,13 @@ func (c *Contract) DiscoverConstructor(ctx context.Context) error { if constructorDataIndex > len(adjustedData) { return fmt.Errorf("constructor data index out of range") } - spew.Dump(constructorAbi) + + // TODO: Fix + // - 0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936 (239 bytes more) - Some shitty text... + + //spew.Dump(hex.EncodeToString(adjustedData[constructorDataIndex:])) + //spew.Dump(constructorAbi) // 239 + //utils.DumpNodeWithExit(irRoot.GetEntryContract().GetConstructor().GetParameters()) constructor, err := bytecode.DecodeConstructorFromAbi(adjustedData[constructorDataIndex:], constructorAbi) if err != nil { if !strings.Contains(err.Error(), "would go over slice boundary") { diff --git a/ir/root.go b/ir/root.go index 5ea8c9ec..bff9fb64 100644 --- a/ir/root.go +++ b/ir/root.go @@ -242,7 +242,7 @@ func (b *Builder) processRoot(root *ast.RootNode) *RootSourceUnit { builder: b, Unit: root, NodeType: root.GetType(), - ContractsCount: int32(root.GetSourceUnitCount()), + ContractsCount: root.GetSourceUnitCount(), Contracts: make([]*Contract, 0), ContractTypes: make([]string, 0), Standards: make([]*Standard, 0), diff --git a/ir/variables.go b/ir/variables.go index d90f1ff6..c5cd5b2b 100644 --- a/ir/variables.go +++ b/ir/variables.go @@ -1,6 +1,7 @@ package ir import ( + "github.com/unpackdev/solgo/utils" "strings" ast_pb "github.com/unpackdev/protos/dist/go/ast" @@ -128,7 +129,7 @@ func (b *Builder) processStateVariables(unit *ast.StateVariableDeclaration) *Sta // It could be that the name of the type name node is not set, but the type description string is. if variableNode.Type == "" { if variableNode.TypeDescription == nil { - //utils.DumpNodeWithExit(variableNode) + utils.DumpNodeWithExit(variableNode) } variableNode.Type = variableNode.TypeDescription.TypeString } diff --git a/storage/reader.go b/storage/reader.go index d03c5f2f..3dd02f56 100644 --- a/storage/reader.go +++ b/storage/reader.go @@ -3,6 +3,7 @@ package storage import ( "context" "fmt" + "github.com/unpackdev/solgo/utils" "strings" ) @@ -74,7 +75,7 @@ func (r *Reader) CalculateStorageLayout() error { for _, variable := range variables { storageSize, found := variable.GetAST().GetTypeName().StorageSize() if !found { - //utils.DumpNodeWithExit(variable.GetAST().GetTypeName()) + utils.DumpNodeWithExit(variable.GetAST().GetTypeName()) return fmt.Errorf("error calculating storage size for variable: %s", variable.GetName()) }