void GenerateSources()
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            string[] solFiles;
            if (!string.IsNullOrEmpty(_solSourceSingleFile))
            {
                solFiles = new[] { _solSourceSingleFile };
            }
            else
            {
                solFiles = GetSolContractFiles(_solSourceDirectory);
            }

            var outputFlags = new[]
            {
                OutputType.Abi,
                OutputType.EvmBytecodeObject,
                OutputType.EvmBytecodeOpcodes,
                OutputType.EvmBytecodeSourceMap,
                OutputType.EvmDeployedBytecodeObject,
                OutputType.EvmDeployedBytecodeOpcodes,
                OutputType.EvmDeployedBytecodeSourceMap,
                OutputType.DevDoc,
                OutputType.UserDoc,
                OutputType.Metadata,
                OutputType.Ast
            };

            var solcOptimizerSettings = new Optimizer();

            if (_solcOptimzer > 0)
            {
                solcOptimizerSettings.Enabled = true;
                solcOptimizerSettings.Runs    = _solcOptimzer;
            }

            _logger("Compiling solidity files in " + _solSourceDirectory);
            var soliditySourceContent = new Dictionary <string, string>();
            var solcOutput            = _solcLib.Compile(solFiles, outputFlags, solcOptimizerSettings, soliditySourceFileContent: soliditySourceContent);

            sw.Stop();
            _logger($"Compiling solidity completed in {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");

            #region Generated hashes for solidity sources
            sw.Restart();

            // Calculate a deterministic hash of the solidity source code base, including file paths and the Meadow assembly version.
            var codeBaseHash = KeccakHash.FromString(string.Join('|', soliditySourceContent
                                                                 .OrderBy(k => k.Key)
                                                                 .SelectMany(k => new[] { k.Key, k.Value })
                                                                 .Concat(new[] { _assemblyVersion })));

            _genResults.SolcCodeBaseHash = HexUtil.GetHexFromBytes(codeBaseHash);

            var            flattenedContracts = solcOutput.ContractsFlattened.OrderBy(c => c.SolFile).ToArray();
            ContractInfo[] contractInfos      = new ContractInfo[solcOutput.ContractsFlattened.Length];

            for (var i = 0; i < contractInfos.Length; i++)
            {
                var c = flattenedContracts[i];

                // Check if any previous contracts have the same name as this one.
                int dupNames = 0;
                for (var f = 0; f < i; f++)
                {
                    if (flattenedContracts[f].ContractName == c.ContractName)
                    {
                        dupNames++;
                    }
                }

                string generatedContractName = c.ContractName;

                // If there are duplicate contract names, prepend a unique amount of underscore suffixes.
                if (dupNames > 0)
                {
                    generatedContractName += new string(Enumerable.Repeat('_', dupNames).ToArray());
                }

                contractInfos[i] = new ContractInfo(
                    Util.GetRelativeFilePath(_solSourceDirectory, c.SolFile),
                    generatedContractName,
                    c.Contract,
                    GetSourceHashesXor(c.Contract),
                    c.Contract.Evm.Bytecode.Object);
            }



            _logger($"Generated sol source file hashes in {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");
            sw.Stop();
            #endregion


            _logger("Writing generated output to directory: " + _generatedContractsDirectory);

            #region Output directory cleanup
            if (!Directory.Exists(_generatedContractsDirectory))
            {
                _logger("Creating directory: " + _generatedContractsDirectory);
                Directory.CreateDirectory(_generatedContractsDirectory);
            }
            else
            {
                var expectedFiles = contractInfos
                                    .Select(c => c.ContractName)
                                    .Concat(new[] { EventHelperFile, SolcOutputDataHelperFile })
                                    .Select(c => NormalizePath($"{_generatedContractsDirectory}/{c}{G_CS_FILE_EXT}"))
                                    .ToArray();

                var existingFiles = Directory
                                    .GetFiles(_generatedContractsDirectory, $"*{G_CS_FILE_EXT}", SearchOption.TopDirectoryOnly)
                                    .Where(f => f.EndsWith(".sol.cs", StringComparison.Ordinal) || f.EndsWith(".sol.resx", StringComparison.Ordinal))
                                    .Select(f => NormalizePath(f))
                                    .ToArray();

                // Delete existing files with no corresponding file that can be generated
                foreach (var existingFile in existingFiles)
                {
                    bool found = false;
                    foreach (var expected in expectedFiles)
                    {
                        if (expected.Equals(existingFile, StringComparison.InvariantCultureIgnoreCase))
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        _logger("Deleting outdated file: " + existingFile);
                        File.Delete(existingFile);
                    }
                }
            }
            #endregion

            #region AST output generation
            sw.Restart();
            GenerateSolcOutputDataFiles(solcOutput, soliditySourceContent, codeBaseHash);
            sw.Stop();
            _logger($"Resx file for solc output generation took: {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");
            #endregion


            #region
            sw.Restart();
            var generatedEvents = new List <GeneratedEventMetadata>();
            GeneratedContractSourceFiles(contractInfos, generatedEvents);
            GenerateEventHelper(generatedEvents);
            sw.Stop();
            _logger($"Contract and event source code generation took: {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");
            #endregion
        }
Example #2
0
        void GenerateSources()
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            var solFiles = GetSolContractFiles(_solSourceDirectory);

            var outputFlags = new[]
            {
                OutputType.Abi,
                OutputType.EvmBytecodeObject,
                OutputType.EvmBytecodeOpcodes,
                OutputType.EvmBytecodeSourceMap,
                OutputType.EvmDeployedBytecodeObject,
                OutputType.EvmDeployedBytecodeOpcodes,
                OutputType.EvmDeployedBytecodeSourceMap,
                OutputType.DevDoc,
                OutputType.UserDoc,
                OutputType.Metadata,
                OutputType.Ast
            };

            var solcOptimizerSettings = new Optimizer();

            if (_solcOptimzer > 0)
            {
                solcOptimizerSettings.Enabled = true;
                solcOptimizerSettings.Runs    = _solcOptimzer;
            }

            Console.WriteLine("Compiling solidity files in " + _solSourceDirectory);
            var soliditySourceContent = new Dictionary <string, string>();
            var solcOutput            = _solcLib.Compile(solFiles, outputFlags, solcOptimizerSettings, soliditySourceFileContent: soliditySourceContent);

            sw.Stop();
            Console.WriteLine($"Compiling solidity completed in {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");


            Console.WriteLine("Writing generated output to directory: " + _generatedContractsDirectory);

            #region Output directory cleanup
            if (!Directory.Exists(_generatedContractsDirectory))
            {
                Console.WriteLine("Creating directory: " + _generatedContractsDirectory);
                Directory.CreateDirectory(_generatedContractsDirectory);
            }
            else
            {
                var expectedFiles = solcOutput.Contracts.Values
                                    .SelectMany(c => c.Keys)
                                    .Concat(new[] { EventHelperFile, SolcOutputDataHelperFile })
                                    .Select(c => NormalizePath($"{_generatedContractsDirectory}/{c}{G_CS_FILE_EXT}"))
                                    .ToArray();

                var existingFiles = Directory
                                    .GetFiles(_generatedContractsDirectory, $"*{G_CS_FILE_EXT}", SearchOption.TopDirectoryOnly)
                                    .Select(f => NormalizePath(f))
                                    .ToArray();

                // Delete existing files with no corresponding file that can be generated
                foreach (var existingFile in existingFiles)
                {
                    bool found = false;
                    foreach (var expected in expectedFiles)
                    {
                        if (expected.Equals(existingFile, StringComparison.InvariantCultureIgnoreCase))
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        Console.WriteLine("Deleting outdated file: " + existingFile);
                        File.Delete(existingFile);
                    }
                }
            }
            #endregion


            #region Generated hashes for solidity sources
            sw.Restart();

            ContractInfo[] contractInfos      = new ContractInfo[solcOutput.ContractsFlattened.Length];
            var            flattenedContracts = solcOutput.ContractsFlattened.OrderBy(c => c.SolFile).ToArray();

            for (var i = 0; i < contractInfos.Length; i++)
            {
                // Check if any previous contracts have the same name as this one.
                var c        = flattenedContracts[i];
                int dupNames = 0;
                for (var f = 0; f < i; f++)
                {
                    if (flattenedContracts[f].ContractName == c.ContractName)
                    {
                        dupNames++;
                    }
                }

                // If there are duplicate contract names, prepend a unique amount of underscore suffixes.
                string dupeNameSuffix;
                if (dupNames > 0)
                {
                    dupeNameSuffix = new string(Enumerable.Repeat('_', dupNames).ToArray());
                }
                else
                {
                    dupeNameSuffix = string.Empty;
                }

                contractInfos[i] = new ContractInfo(
                    Util.GetRelativeFilePath(_solSourceDirectory, c.SolFile),
                    c.ContractName + dupeNameSuffix,
                    c.Contract,
                    GetSourceHashesXor(c.Contract),
                    c.Contract.Evm.Bytecode.Object);
            }


            for (var i = 0; i < contractInfos.Length; i++)
            {
                var cur = contractInfos[i];
                for (var j = 0; j < contractInfos.Length; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }

                    var other = contractInfos[j];
                    if (cur.ContractName.Equals(other.ContractName, StringComparison.OrdinalIgnoreCase))
                    {
                        throw new Exception($"Duplicate contract names: '{cur.ContractName}' in {cur.SolFile} and '{other.ContractName}' in {other.SolFile}");
                    }
                }
            }

            var contractPathsHash = KeccakHashString(string.Join("\n", contractInfos.SelectMany(c => new[] { c.SolFile, c.ContractName })));
            var codeBaseHash      = XorAllHashes(contractInfos.Select(c => c.Hash).Concat(new[] { contractPathsHash }).ToArray());
            Console.WriteLine($"Generated sol source file hashes in {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");
            sw.Stop();
            #endregion


            #region AST output generation
            sw.Restart();
            GenerateSolcOutputDataFiles(solcOutput, soliditySourceContent, codeBaseHash);
            sw.Stop();
            Console.WriteLine($"Resx file for solc output generation took: {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");
            #endregion


            #region
            sw.Restart();
            var generatedEvents = new List <GeneratedEventMetadata>();
            GeneratedContractSourceFiles(contractInfos, generatedEvents);
            GenerateEventHelper(generatedEvents);
            sw.Stop();
            Console.WriteLine($"Contract and event source code generation took: {Math.Round(sw.Elapsed.TotalSeconds, 2)} seconds");
            #endregion
        }