ethereum / py-solc

Python wrapper around the solc Solidity compiler.
MIT License
185 stars 232 forks source link

Unable to load Solidity contract using `compile_standard` function due to error `Second argument needs to be a buffer` #42

Open ltfschoen opened 6 years ago

ltfschoen commented 6 years ago

What was wrong?

I used PyEnv to install and switch to the latest Python version 3.6.4rc1 https://github.com/pyenv/pyenv. I then installed the following Python packages:

pyenv install 3.6.4rc1
pyenv versions
pyenv global 3.6.4rc1
python -m pip install py-solc==2.1.0
python -m solc.install v0.4.18
python -m pip install web3==4.0.0b5 
python -m pip install ethereum==2.1.5
python scripts/main.py

I then created and executed a Python file i.e. main.py with python main.py: main.py

import solc
from solc import compile_files
compiled_sol = compile_files(["FixedSupplyToken.sol"])

Note that the Solidity file I am using is in my repo here https://github.com/ltfschoen/geth-node/blob/master/contracts/FixedSupplyToken.sol, and it compiles successfully with solc v0.4.18, as I've done so in MIST and deployed it to a Private Network previously already.

I'm trying to replicate using Web3.py and py-solc the same functionality that worked when I used Web3.js and solc-js here: https://github.com/ltfschoen/geth-node/blob/master/scripts/main.js

In the file I use py-solc to compile Solidity source code from a file using the function compile_files, but I get a full output error in Bash terminal as follows:

$ python main.py 
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    compiled_sol = compile_files(["FixedSupplyToken.sol"])
  File "/Users/Ls/.pyenv/versions/3.6.4rc1/lib/python3.6/site-packages/solc/main.py", line 133, in compile_files
    stdoutdata, stderrdata, command, proc = solc_wrapper(**compiler_kwargs)
  File "/Users/Ls/.pyenv/versions/3.6.4rc1/lib/python3.6/site-packages/solc/utils/string.py", line 85, in inner
    return force_obj_to_text(fn(*args, **kwargs))
  File "/Users/Ls/.pyenv/versions/3.6.4rc1/lib/python3.6/site-packages/solc/wrapper.py", line 165, in solc_wrapper
    stderr_data=stderrdata,
solc.exceptions.SolcError: An error occurred during execution
> command: `solc --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,devdoc,interface,opcodes,userdoc FixedSupplyToken.sol`
> return code: `1`
> stderr:

> stdout:
Invalid option selected, must specify either --bin or --abi

I then looked closer at the Py-Solc documentation here: https://github.com/ethereum/py-solc#standard-json-compilation, and changed my code to use the same syntax as the second example under the heading "Standard JSON Compilation": main.py

import solc
from solc import compile_standard
compiled_sol = compile_standard({
  'language': 'Solidity',
  'sources': {'FixedSupplyToken.sol': 'urls': ["./contracts/FixedSupplyToken.sol"]},
}, allow_paths="./contracts/")

But when I run this it gives another error in the Bash terminal, which is because the example is missing a { just before 'urls':

$ python main.py 
  File "main.py", line 5
    'sources': {'FixedSupplyToken.sol': 'urls': ["FixedSupplyToken.sol"]},
                                              ^
SyntaxError: invalid syntax

So I added the missing {, and I used a JSON Validator to make sure the argument was valid json format:

main.py

import solc
from solc import compile_standard
compiled_sol = compile_standard({
  "language": "Solidity",
  "sources": {
    "FixedSupplyToken.sol": {
      "urls": ["file:///Users/Ls/code/blockchain/geth-node/contracts/FixedSupplyToken.sol"]
    }
  }
}, allow_paths="file:///Users/Ls/code/blockchain/geth-node/contracts/")

But then it gave me error:

$ python main.py 
Traceback (most recent call last):
  File "main.py", line 10, in <module>
    }, allow_paths="./contracts/")
  File "/Users/Ls/.pyenv/versions/3.6.4rc1/lib/python3.6/site-packages/solc/main.py", line 161, in compile_standard
    **kwargs
  File "/Users/Ls/.pyenv/versions/3.6.4rc1/lib/python3.6/site-packages/solc/utils/string.py", line 85, in inner
    return force_obj_to_text(fn(*args, **kwargs))
  File "/Users/Ls/.pyenv/versions/3.6.4rc1/lib/python3.6/site-packages/solc/wrapper.py", line 165, in solc_wrapper
    stderr_data=stderrdata,
solc.exceptions.SolcError: An error occurred during execution
        > command: `solc --allow-paths file:///Users/Ls/code/blockchain/geth-node/contracts/ --standard-json`
        > return code: `1`
        > stderr:

        > stdout:
        /Users/Ls/.nvm/versions/node/v8.7.0/lib/node_modules/solc/node_modules/graceful-fs/polyfills.js:144
        throw er
        ^

TypeError: Second argument needs to be a buffer
    at Object.fs.readSync (fs.js:682:18)
    at Object.readSync (/Users/Ls/.nvm/versions/node/v8.7.0/lib/node_modules/solc/node_modules/graceful-fs/polyfills.js:138:28)
    at Object.<anonymous> (/Users/Ls/.nvm/versions/node/v8.7.0/lib/node_modules/solc/solcjs:65:18)
    at Module._compile (module.js:624:30)
    at Object.Module._extensions..js (module.js:635:10)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)
    at Function.Module.runMain (module.js:665:10)
    at startup (bootstrap_node.js:187:16)

So then I clicked the link that says "Solidity Documentation for Standard JSON input and ouptup format" (FYI, note the typo here in the word "output") http://solidity.readthedocs.io/en/develop/using-the-compiler.html#compiler-input-and-output-json-description, specifically the section http://solidity.readthedocs.io/en/develop/using-the-compiler.html#input-description.

It highlighted that that object we are passing to compile_standard is an Input JSON Description, which the compiler API expects to be in JSON format, and it outputs the compilation result in a JSON formatted output.

When looked more closely at the error, which says solc --allow-paths file:///Users/Ls/code/blockchain/geth-node/contracts/ --standard-json, it's clearly missing a JSON file as an input argument at the end. Is this something that the compiler is supposed to automatically generate? A similar error was raised in the solc-js repo issues here https://github.com/ethereum/solc-js/issues/126

I tried experimenting a bit more trying to find an alternative and found that I'm able to manually created ABI files in Bash terminal with the command: solc --abi FixedSupplyToken.sol --output-dir ./build, which generates the following in the ./build/ folder:

But I don't know how to load these generated ABI files into the variable contract_interface in main.py. Currently my code is contract_interface = compiled_sol['<stdin>:FixedSupplyToken'],

I then tried copying the JSON argument into a file called test.json. test.json

{
  "language": "Solidity",
  "sources": {
    "FixedSupplyToken.sol": {
      "urls": ["file:///Users/Ls/code/blockchain/geth-node/contracts/FixedSupplyToken.sol"]
    }
  }
}

I then ran the following:

solc --standard-json test.json --output-dir ./build

But it just returned the following;

Empty input was read

So I don't even understand how to use the CLI. Any help greatly appreciated I've pushed my latest code here: https://github.com/ltfschoen/geth-node/blob/master/scripts/main.py

Cute Animal Picture

Python

ltfschoen commented 6 years ago

I tried changing to the following in https://github.com/ltfschoen/geth-node/blob/master/scripts/main.py and passed additional arguments defined in https://github.com/ethereum/py-solc/blob/master/solc/wrapper.py to the compile_files function. I also modified https://github.com/ethereum/py-solc/blob/master/solc/wrapper.py, moving the conditional check for source_files after the conditional check for abi to try and get it to run solc --output-dir ./ --allow-paths ./ --bin ./contracts/FixedSupplyToken.sol

Changes to https://github.com/ethereum/py-solc/blob/master/solc/wrapper.py

...
if abi:
    command.append('--abi')

if source_files is not None:
    # command.extend(source_files)
    command.append(source_files['sources']['FixedSupplyToken.sol']['urls'][0])  
...

Changes to https://github.com/ltfschoen/geth-node/blob/master/scripts/main.py

# Standard JSON Compilation - https://github.com/ethereum/py-solc#standard-json-compilation
compiled_sol = compile_files({
  "language": "Solidity",
  "sources": {
    "FixedSupplyToken.sol": {
      "urls": ["../contracts/FixedSupplyToken.sol"]
    }
  }
}, 
output_dir="./",
bin=True,
allow_paths="./")

But it just outputs binary files in the current directory instead of assigning to the variable