예제 #1
0
        /// <summary>
        /// Try to update the calling graph when updating the function.
        /// If some functions have unknow reference of foreign edge(call other contract's function), the update will not be approved and nothing will take effect
        /// </summary>
        /// <param name=""></param>
        /// <param name="contractAddress"></param>
        /// <param name="callingGraph"></param>
        /// <param name="contractMetadataTemplate"></param>
        /// <returns>The new calling graph</returns>
        /// <exception cref="FunctionMetadataException"></exception>
        public CallGraph TryUpdateAndGetCallingGraph(Address contractAddress, CallGraph callingGraph,
                                                     ContractMetadataTemplate contractMetadataTemplate)
        {
            List <Edge <string> > outEdgesToAdd = new List <Edge <string> >();

            //check for unknown reference
            foreach (var kvPair in contractMetadataTemplate.MethodMetadataTemplates)
            {
                var sourceFunc =
                    Replacement.ReplaceValueIntoReplacement(kvPair.Key, Replacement.This,
                                                            contractAddress.GetFormatted());

                foreach (var calledFunc in kvPair.Value.CallingSet)
                {
                    if (!calledFunc.Contains(Replacement.This))
                    {
                        Replacement.TryGetReplacementWithIndex(calledFunc, 0, out var memberReplacement);
                        var referenceAddress =
                            contractMetadataTemplate.ContractReferences[
                                Replacement.Value(
                                    memberReplacement)]; //FunctionMetadataTemplate itself ensure this value exist
                        var globalCalledFunc = Replacement.ReplaceValueIntoReplacement(calledFunc, memberReplacement,
                                                                                       referenceAddress.GetFormatted());
                        if (!callingGraph.ContainsVertex(globalCalledFunc))
                        {
                            throw new FunctionMetadataException(
                                      "Unknow reference of the foreign target in edge <" + sourceFunc + "," + calledFunc +
                                      "> when trying to add contract " + contractMetadataTemplate.FullName +
                                      " into calling graph, consider the target function does not exist in the metadata");
                        }

                        outEdgesToAdd.Add(new Edge <string>(sourceFunc, globalCalledFunc));
                    }
                }
            }

            //Merge local calling graph, mind that there are functions that call nothing, they also need to appear in the call graph (to be called in future)
            foreach (var localVertex in contractMetadataTemplate.LocalCallingGraph.Vertices)
            {
                var globalVertex =
                    Replacement.ReplaceValueIntoReplacement(localVertex, Replacement.This,
                                                            contractAddress.GetFormatted());
                callingGraph.AddVertex(globalVertex);
                foreach (var outEdge in contractMetadataTemplate.LocalCallingGraph.OutEdges(localVertex))
                {
                    var toVertex = Replacement.ReplaceValueIntoReplacement(outEdge.Target, Replacement.This,
                                                                           contractAddress.GetFormatted());
                    callingGraph.AddVerticesAndEdge(new Edge <string>(globalVertex, toVertex));
                }
            }

            //add foreign edges
            callingGraph.AddEdgeRange(outEdgesToAdd);

            return(callingGraph);
        }
예제 #2
0
        public CallGraph TryRemoveAndGetCallingGraph(Address contractAddress, CallGraph callingGraph,
                                                     ContractMetadataTemplate contractMetadataTemplate)
        {
            foreach (var kvPair in contractMetadataTemplate.MethodMetadataTemplates)
            {
                var sourceFunc =
                    Replacement.ReplaceValueIntoReplacement(kvPair.Key, Replacement.This,
                                                            contractAddress.GetFormatted());

                foreach (var calledFunc in kvPair.Value.CallingSet)
                {
                    if (!calledFunc.Contains(Replacement.This))
                    {
                        Replacement.TryGetReplacementWithIndex(calledFunc, 0, out var memberReplacement);
                        var referenceAddress =
                            contractMetadataTemplate.ContractReferences[
                                Replacement.Value(
                                    memberReplacement)]; //FunctionMetadataTemplate itself ensure this value exist
                        var globalCalledFunc = Replacement.ReplaceValueIntoReplacement(calledFunc, memberReplacement,
                                                                                       referenceAddress.GetFormatted());
                        if (!callingGraph.ContainsVertex(globalCalledFunc))
                        {
                            throw new FunctionMetadataException(
                                      "Unknow reference of the foreign target in edge <" + sourceFunc + "," + calledFunc +
                                      "> when trying to add contract " + contractMetadataTemplate.FullName +
                                      " into calling graph, consider the target function does not exist in the metadata");
                        }

                        callingGraph.RemoveEdge(new Edge <string>(sourceFunc, globalCalledFunc));
                    }
                }
            }

            foreach (var localVertex in contractMetadataTemplate.LocalCallingGraph.Vertices)
            {
                var globalVertex =
                    Replacement.ReplaceValueIntoReplacement(localVertex, Replacement.This,
                                                            contractAddress.GetFormatted());
                callingGraph.AddVertex(globalVertex);
                foreach (var outEdge in contractMetadataTemplate.LocalCallingGraph.OutEdges(localVertex))
                {
                    var toVertex = Replacement.ReplaceValueIntoReplacement(outEdge.Target, Replacement.This,
                                                                           contractAddress.GetFormatted());

                    callingGraph.RemoveEdge(new Edge <string>(globalVertex, toVertex));
                    callingGraph.RemoveVertex(globalVertex);
                    callingGraph.RemoveVertex(toVertex);
                }

                callingGraph.RemoveVertex(globalVertex);
            }

            return(callingGraph);
        }
예제 #3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="functionFullName">should be "[Addr].FunctionSig"</param>
        /// <param name="functionTemplate"></param>
        /// <param name="contractAddr"></param>
        /// <param name="contractReferences"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="FunctionMetadataException"></exception>
        private async Task <FunctionMetadata> GetMetadataForNewFunction(Hash chainId, string functionFullName, FunctionMetadataTemplate functionTemplate, Address contractAddr, Dictionary <string, Address> contractReferences, Dictionary <string, FunctionMetadata> localMetadataMap)
        {
            var resourceSet = new HashSet <Resource>(functionTemplate.LocalResourceSet.Select(resource =>
            {
                var resName = Replacement.ReplaceValueIntoReplacement(resource.Name, Replacement.This, contractAddr.DumpHex());
                return(new Resource(resName, resource.DataAccessMode));
            }));

            var callingSet = new HashSet <string>();

            foreach (var calledFunc in functionTemplate.CallingSet ?? Enumerable.Empty <string>())
            {
                if (!Replacement.TryGetReplacementWithIndex(calledFunc, 0, out var locationReplacement))
                {
                    throw new FunctionMetadataException("not valid template in calling set of function " +
                                                        functionFullName + " because the calling function" +
                                                        calledFunc +
                                                        "have no location replacement (${this} or ${[calling contract name]})");
                }

                //just add foreign resource into set because local resources are already recursively analyzed
                if (locationReplacement.Equals(Replacement.This))
                {
                    var replacedCalledFunc = Replacement.ReplaceValueIntoReplacement(calledFunc, Replacement.This,
                                                                                     contractAddr.DumpHex());
                    if (!localMetadataMap.TryGetValue(replacedCalledFunc, out var localCalledFuncMetadata))
                    {
                        throw new FunctionMetadataException("There are no local function " + replacedCalledFunc + " in the given local function map, consider wrong reference cause wrong topological order");
                    }
                    resourceSet.UnionWith(localCalledFuncMetadata.FullResourceSet);
                    callingSet.Add(replacedCalledFunc);
                }
                else
                {
                    if (!contractReferences.TryGetValue(Replacement.Value(locationReplacement), out var referenceAddr))
                    {
                        throw new FunctionMetadataException("There are no member reference " + Replacement.Value(locationReplacement) + " in the given contractReferences map");
                    }
                    var replacedCalledFunc = Replacement.ReplaceValueIntoReplacement(calledFunc, locationReplacement,
                                                                                     referenceAddr.DumpHex());

                    var metadataOfCalledFunc = await GetFunctionMetadata(chainId, replacedCalledFunc); //could throw exception

                    resourceSet.UnionWith(metadataOfCalledFunc.FullResourceSet);
                    callingSet.Add(replacedCalledFunc);
                }
            }

            var metadata = new FunctionMetadata(callingSet, resourceSet);

            return(metadata);
        }
        /// <summary>
        /// FunctionMetadataException will be thrown in following cases:
        /// (1) Duplicate member function name.
        /// (2) Local resource are not declared in the code.
        /// (3) Duplicate smart contract reference name
        /// (4) Duplicate declared field name.
        /// (5) Unknown reference in calling set
        /// </summary>
        /// <param name="contractType"></param>
        /// <param name="contractReferences"></param>
        /// <exception cref="FunctionMetadataException"></exception>
        private Dictionary <string, FunctionMetadataTemplate> ExtractRawMetadataFromType(Type contractType,
                                                                                         out Dictionary <string, Address> contractReferences)
        {
            var localFunctionMetadataTemplateMap = new Dictionary <string, FunctionMetadataTemplate>();
            var templocalFieldMap = new Dictionary <string, DataAccessMode>();

            contractReferences = new Dictionary <string, Address>();

            //load localFieldMap: <"${this}.[ResourceName]", DataAccessMode>
            foreach (var fieldInfo in contractType.GetFields(
                         BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                var fieldAttr = fieldInfo.GetCustomAttribute <SmartContractFieldDataAttribute>();
                if (fieldAttr == null)
                {
                    continue;
                }
                if (!templocalFieldMap.TryAdd(fieldAttr.FieldName, fieldAttr.DataAccessMode))
                {
                    throw new FunctionMetadataException("Duplicate name of field attributes in contract " +
                                                        contractType.FullName);
                }
            }

            //load smartContractReferenceMap: <"[contract_member_name]", Address of the referenced contract>
            foreach (var fieldInfo in contractType.GetFields(
                         BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                var smartContractRefAttr = fieldInfo.GetCustomAttribute <SmartContractReferenceAttribute>();
                if (smartContractRefAttr == null)
                {
                    continue;
                }
                try
                {
                    if (!contractReferences.TryAdd(smartContractRefAttr.FieldName,
                                                   Address.FromString(smartContractRefAttr.ContractAddress)))
                    {
                        throw new FunctionMetadataException(
                                  "Duplicate name of smart contract reference attributes in contract " +
                                  contractType.FullName);
                    }
                }
                catch (Exception e) when(!(e is FunctionMetadataException))
                {
                    throw new FunctionMetadataException(
                              $"When deploy contract {contractType.FullName}, error occurs where the address {smartContractRefAttr.ContractAddress} of contract reference {smartContractRefAttr.FieldName} is not a valid hex format address ");
                }
            }

            //load localFunctionMetadataTemplateMap: <"${[this]}.FunctionSignature", FunctionMetadataTemplate>
            //FunctionMetadataTemplate: <calling_set, local_resource_set>
            //calling_set: { "${[contract_member_name]}.[FunctionSignature]", ${this}.[FunctionSignature]... }
            //local_resource_set: {"${this}.[ResourceName]"}
            foreach (var methodInfo in contractType.GetMethods(
                         BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
            {
                var functionAttribute = methodInfo.GetCustomAttribute <SmartContractFunctionAttribute>();
                if (functionAttribute == null)
                {
                    continue;
                }

                var resourceSet = functionAttribute.LocalResources.Select(resource =>
                {
                    if (!templocalFieldMap.TryGetValue(resource, out var dataAccessMode))
                    {
                        throw new FunctionMetadataException("Unknown reference local field " + resource +
                                                            " in function " + functionAttribute.FunctionSignature);
                    }

                    return(new Resource(resource, dataAccessMode));
                });

                if (!localFunctionMetadataTemplateMap.TryAdd(functionAttribute.FunctionSignature,
                                                             new FunctionMetadataTemplate(new HashSet <string>(functionAttribute.CallingSet),
                                                                                          new HashSet <Resource>(resourceSet))))
                {
                    throw new FunctionMetadataException("Duplicate name of function attribute" +
                                                        functionAttribute.FunctionSignature + " in contract" +
                                                        contractType.FullName);
                }
            }

            if (localFunctionMetadataTemplateMap.Count == 0)
            {
                var blackLists = new[] { "ToString", "Equals", "GetHashCode", "GetType" };
                foreach (var methodInfo in contractType.GetMethods())
                {
                    if (!blackLists.Contains(methodInfo.Name))
                    {
                        localFunctionMetadataTemplateMap.Add("${this}." + methodInfo.Name,
                                                             new FunctionMetadataTemplate(false));
                    }
                }

                return(localFunctionMetadataTemplateMap);

                throw new FunctionMetadataException(
                          "no function marked in the target contract " + contractType.FullName);
            }

            //check for validaty of the calling set (whether have unknow reference)
            foreach (var kvPair in localFunctionMetadataTemplateMap)
            {
                foreach (var calledFunc in kvPair.Value.CallingSet)
                {
                    if (calledFunc.Contains(Replacement.This))
                    {
                        if (!localFunctionMetadataTemplateMap.ContainsKey(calledFunc))
                        {
                            throw new FunctionMetadataException(
                                      "calling set of function " + kvPair.Key + " when adding contract " +
                                      contractType.FullName + " contains unknown reference to it's own function: " +
                                      calledFunc);
                        }
                    }
                    else
                    {
                        if (!Replacement.TryGetReplacementWithIndex(calledFunc, 0, out var memberReplacement) ||
                            !contractReferences.ContainsKey(Replacement.Value(memberReplacement)))
                        {
                            throw new FunctionMetadataException(
                                      "calling set of function " + kvPair.Key + " when adding contract " +
                                      contractType.FullName + " contains unknown local member reference to other contract: " +
                                      calledFunc);
                        }
                    }
                }
            }

            return(localFunctionMetadataTemplateMap);
        }