mirror of https://github.com/crytic/slither
parent
e2e6873dab
commit
43fa9c73df
@ -0,0 +1,107 @@ |
||||
#!/usr/bin/env python3 |
||||
|
||||
import argparse |
||||
import logging |
||||
import sys |
||||
import traceback |
||||
import operator |
||||
import numpy as np |
||||
|
||||
from .info import info |
||||
from .test import test |
||||
from .train import train |
||||
|
||||
|
||||
logging.basicConfig() |
||||
logger = logging.getLogger("Slither") |
||||
|
||||
slither_simil_usage = "USAGE" # TODO |
||||
modes = ["info", "test", "train"] |
||||
|
||||
def parse_args(): |
||||
parser = argparse.ArgumentParser(description='', |
||||
usage=slither_simil_usage) |
||||
|
||||
parser.add_argument('mode', |
||||
help="|".join(modes)) |
||||
|
||||
parser.add_argument('model', |
||||
help='model.bin') |
||||
|
||||
parser.add_argument('--solc', |
||||
help='solc path', |
||||
action='store', |
||||
default='solc') |
||||
|
||||
parser.add_argument('--filename', |
||||
action='store', |
||||
dest='filename', |
||||
help='contract.sol') |
||||
|
||||
parser.add_argument('--contract', |
||||
action='store', |
||||
dest='contract', |
||||
help='Contract') |
||||
|
||||
parser.add_argument('--filter', |
||||
action='store', |
||||
dest='filter', |
||||
help='Extension to filter contracts') |
||||
|
||||
parser.add_argument('--fname', |
||||
action='store', |
||||
dest='fname', |
||||
help='Function name') |
||||
|
||||
parser.add_argument('--input', |
||||
action='store', |
||||
dest='input', |
||||
help='File or directory used as input') |
||||
|
||||
parser.add_argument('--version', |
||||
help='displays the current version', |
||||
version="0.0", |
||||
action='version') |
||||
|
||||
if len(sys.argv) == 1: |
||||
parser.print_help(sys.stderr) |
||||
sys.exit(1) |
||||
|
||||
args = parser.parse_args() |
||||
return args |
||||
|
||||
# endregion |
||||
################################################################################### |
||||
################################################################################### |
||||
# region Main |
||||
################################################################################### |
||||
################################################################################### |
||||
|
||||
def main(): |
||||
args = parse_args() |
||||
|
||||
default_log = logging.INFO |
||||
logger.setLevel(default_log) |
||||
|
||||
try: |
||||
mode = args.mode |
||||
|
||||
if mode == "info": |
||||
info(args) |
||||
elif mode == "train": |
||||
train(args) |
||||
elif mode == "test": |
||||
test(args) |
||||
else: |
||||
logger.error('Invalid mode!. It should be one of these: %s' % ", ".join(modes)) |
||||
sys.exit(-1) |
||||
|
||||
except Exception: |
||||
logger.error('Error in %s' % args.filename) |
||||
logger.error(traceback.format_exc()) |
||||
sys.exit(-1) |
||||
|
||||
if __name__ == '__main__': |
||||
main() |
||||
|
||||
# endregion |
@ -0,0 +1,22 @@ |
||||
import numpy as np |
||||
|
||||
from .encode import encode_contract, load_contracts |
||||
|
||||
def load_cache(infile, model, ext=None, solc='solc'): |
||||
cache = dict() |
||||
if infile.endswith(".npz"): |
||||
with np.load(infile) as data: |
||||
array = data['arr_0'][0] |
||||
for x,y in array: |
||||
cache[x] = y |
||||
else: |
||||
contracts = load_contracts(infile, ext=ext) |
||||
for contract in contracts: |
||||
for x,ir in encode_contract(contract, solc=solc).items(): |
||||
if ir != []: |
||||
y = " ".join(ir) |
||||
cache[x] = model.get_sentence_vector(y) |
||||
return cache |
||||
|
||||
def save_cache(cache, outfile): |
||||
np.savez(outfile,[np.array(list(cache.items()))]) |
@ -0,0 +1,168 @@ |
||||
import os |
||||
import sys |
||||
|
||||
from slither import Slither |
||||
from slither.slithir.operations import * |
||||
from slither.slithir.variables import * |
||||
from slither.core.declarations import * |
||||
from slither.solc_parsing.declarations.function import * |
||||
|
||||
from slither.solc_parsing.variables.state_variable import * |
||||
from slither.solc_parsing.variables.local_variable import * |
||||
from slither.solc_parsing.variables.local_variable_init_from_tuple import * |
||||
|
||||
def load_contracts(dirname, ext=None): |
||||
r = [] |
||||
walk = list(os.walk(dirname)) |
||||
for x, y, files in walk: |
||||
for f in files: |
||||
if ext is None or f.endswith(ext): |
||||
r.append(x + "/".join(y) + "/" + f) |
||||
return r |
||||
|
||||
def ntype(_type): |
||||
if type(_type) is not str: |
||||
_type = str(_type) |
||||
|
||||
if "struct" in _type: |
||||
return "struct" |
||||
elif "enum" in _type: |
||||
return "enum" |
||||
elif "tuple" in _type: |
||||
return "tuple" |
||||
elif "contract" in _type: |
||||
return "contract" |
||||
elif "mapping" in _type: |
||||
return "mapping" |
||||
elif "." in _type or _type[0].isupper(): |
||||
return "<name>" |
||||
else: |
||||
return _type.replace(" ","_") |
||||
|
||||
def encode_ir(ir): |
||||
# operations |
||||
if isinstance(ir, Assignment): |
||||
return '({}):=({})'.format(encode_ir(ir.lvalue), encode_ir(ir.rvalue)) |
||||
if isinstance(ir, Index): |
||||
return 'index({})'.format(ntype(ir._type)) |
||||
if isinstance(ir, Member): |
||||
return 'member' #.format(ntype(ir._type)) |
||||
if isinstance(ir, Length): |
||||
return 'length' |
||||
if isinstance(ir, Balance): |
||||
return 'balance' |
||||
if isinstance(ir, Binary): |
||||
return 'binary({})'.format(ir.type_str) |
||||
if isinstance(ir, Unary): |
||||
return 'unary({})'.format(ir.type_str) |
||||
if isinstance(ir, Condition): |
||||
return 'condition({})'.format(encode_ir(ir.value)) |
||||
if isinstance(ir, NewStructure): |
||||
return 'new_structure' |
||||
if isinstance(ir, NewContract): |
||||
return 'new_contract' |
||||
if isinstance(ir, NewArray): |
||||
return 'new_array({})'.format(ntype(ir._array_type)) |
||||
if isinstance(ir, NewElementaryType): |
||||
return 'new_elementary({})'.format(ntype(ir._type)) |
||||
if isinstance(ir, Push): |
||||
return 'push({},{})'.format(encode_ir(ir.value), encode_ir(ir.lvalue)) |
||||
if isinstance(ir, Delete): |
||||
return 'delete({},{})'.format(encode_ir(ir.lvalue), encode_ir(ir.variable)) |
||||
if isinstance(ir, SolidityCall): |
||||
return 'solidity_call({})'.format(ir.function.full_name) |
||||
if isinstance(ir, InternalCall): |
||||
return 'internal_call({})'.format(ntype(ir._type_call)) |
||||
if isinstance(ir, EventCall): # is this useful? |
||||
return 'event' |
||||
if isinstance(ir, LibraryCall): |
||||
return 'library_call' |
||||
if isinstance(ir, InternalDynamicCall): |
||||
return 'internal_dynamic_call' |
||||
if isinstance(ir, HighLevelCall): # TODO: improve |
||||
return 'high_level_call' |
||||
if isinstance(ir, LowLevelCall): # TODO: improve |
||||
return 'low_level_call' |
||||
if isinstance(ir, TypeConversion): |
||||
return 'type_conversion({})'.format(ntype(ir.type)) |
||||
if isinstance(ir, Return): # this can be improved using values |
||||
return 'return' #.format(ntype(ir.type)) |
||||
if isinstance(ir, Transfer): |
||||
return 'transfer({})'.format(encode_ir(ir.call_value)) |
||||
if isinstance(ir, Send): |
||||
return 'send({})'.format(encode_ir(ir.call_value)) |
||||
if isinstance(ir, Unpack): # TODO: improve |
||||
return 'unpack' |
||||
if isinstance(ir, InitArray): # TODO: improve |
||||
return 'init_array' |
||||
if isinstance(ir, FunctionSolc): # TODO: investigate this |
||||
return 'function_solc' |
||||
|
||||
# variables |
||||
if isinstance(ir, Constant): |
||||
return 'constant({})'.format(ntype(ir._type)) |
||||
if isinstance(ir, SolidityVariableComposed): |
||||
return 'solidity_variable_composed({})'.format(ir.name) |
||||
if isinstance(ir, SolidityVariable): |
||||
return 'solidity_variable{}'.format(ir.name) |
||||
if isinstance(ir, TemporaryVariable): |
||||
return 'temporary_variable' |
||||
if isinstance(ir, ReferenceVariable): |
||||
return 'reference({})'.format(ntype(ir._type)) |
||||
if isinstance(ir, LocalVariableSolc): |
||||
return 'local_solc_variable({})'.format(ir._location) |
||||
if isinstance(ir, StateVariableSolc): |
||||
return 'state_solc_variable({})'.format(ntype(ir._type)) |
||||
if isinstance(ir, LocalVariableInitFromTupleSolc): |
||||
return 'local_variable_init_tuple' |
||||
if isinstance(ir, TupleVariable): |
||||
return 'tuple_variable' |
||||
|
||||
# default |
||||
else: |
||||
print(type(ir),"is missing encoding!") |
||||
#sys.exit(1) |
||||
return '' |
||||
|
||||
def encode_contract(filename, solc): |
||||
r = dict() |
||||
|
||||
# Init slither |
||||
try: |
||||
slither = Slither(filename, solc=solc) |
||||
except: |
||||
print("Compilation failed") |
||||
return r |
||||
|
||||
# Iterate over all the contracts |
||||
for contract in slither.contracts: |
||||
|
||||
# Iterate over all the functions |
||||
for function in contract.functions: |
||||
|
||||
# Dont explore inherited functions |
||||
if function.contract == contract: |
||||
|
||||
if function.nodes == []: |
||||
continue |
||||
|
||||
x = "-".join([filename,contract.name,function.name]) |
||||
|
||||
r[x] = [] |
||||
|
||||
# Iterate over the nodes of the function |
||||
for node in function.nodes: |
||||
|
||||
# Print the Solidity expression of the nodes |
||||
# And the SlithIR operations |
||||
if node.expression: |
||||
|
||||
#print('\tSolidity expression: {}'.format(node.expression)) |
||||
#print('\tSlithIR:') |
||||
for ir in node.irs: |
||||
#print(ir) |
||||
r[x].append(encode_ir(ir)) |
||||
#print('\t\t\t{}'.format(ir)) |
||||
return r |
||||
|
||||
|
@ -0,0 +1,47 @@ |
||||
import logging |
||||
import sys |
||||
import traceback |
||||
|
||||
from fastText import load_model |
||||
from .encode import encode_contract |
||||
|
||||
logging.basicConfig() |
||||
logger = logging.getLogger("Slither") |
||||
|
||||
def info(args): |
||||
|
||||
try: |
||||
model = args.model |
||||
model = load_model(model) |
||||
filename = args.filename |
||||
contract = args.contract |
||||
solc = args.solc |
||||
fname = args.fname |
||||
if filename is None and contract is None and fname is None: |
||||
print(args.model,"uses the following words:") |
||||
for word in model.get_words(): |
||||
print(word) |
||||
sys.exit(0) |
||||
|
||||
if filename is None or contract is None or fname is None: |
||||
logger.error('The encode mode requires filename, contract and fname parameters.') |
||||
sys.exit(-1) |
||||
|
||||
irs = encode_contract(filename, solc=solc) |
||||
if len(irs) == 0: |
||||
sys.exit(-1) |
||||
|
||||
x = "-".join([filename,contract,fname]) |
||||
y = " ".join(irs[x]) |
||||
|
||||
fvector = model.get_sentence_vector(y) |
||||
print("Function {} in contract {} is encoded as:".format(fname, contract)) |
||||
print(y) |
||||
print(fvector) |
||||
|
||||
except Exception: |
||||
logger.error('Error in %s' % args.filename) |
||||
logger.error(traceback.format_exc()) |
||||
sys.exit(-1) |
||||
|
||||
|
@ -0,0 +1,6 @@ |
||||
import numpy as np |
||||
|
||||
def similarity(v1, v2): |
||||
n1 = np.linalg.norm(v1) |
||||
n2 = np.linalg.norm(v2) |
||||
return np.dot(v1, v2) / n1 / n2 |
@ -0,0 +1,49 @@ |
||||
import argparse |
||||
import logging |
||||
import sys |
||||
import traceback |
||||
import operator |
||||
import numpy as np |
||||
|
||||
from fastText import load_model |
||||
from .encode import encode_contract, load_contracts |
||||
from .cache import load_cache, save_cache |
||||
from .similarity import similarity |
||||
|
||||
logger = logging.getLogger("crytic-pred") |
||||
|
||||
def test(args): |
||||
|
||||
try: |
||||
model = args.model |
||||
model = load_model(model) |
||||
filename = args.filename |
||||
contract = args.contract |
||||
fname = args.fname |
||||
solc = args.solc |
||||
infile = args.input |
||||
ext = args.filter |
||||
if filename is None or contract is None or fname is None or infile is None: |
||||
logger.error('The test mode requires filename, contract, fname and input parameters.') |
||||
sys.exit(-1) |
||||
|
||||
irs = encode_contract(filename,solc=solc) |
||||
x = "-".join([filename,contract,fname]) |
||||
y = " ".join(irs[x]) |
||||
|
||||
fvector = model.get_sentence_vector(y) |
||||
cache = load_cache(infile, model, ext=ext, solc=solc) |
||||
#save_cache("cache.npz", cache) |
||||
|
||||
r = dict() |
||||
for x,y in cache.items(): |
||||
r[x] = similarity(fvector, y) |
||||
|
||||
r = sorted(r.items(), key=operator.itemgetter(1), reverse=True) |
||||
for x,score in r[:10]: |
||||
print(x,score) |
||||
|
||||
except Exception: |
||||
logger.error('Error in %s' % args.filename) |
||||
logger.error(traceback.format_exc()) |
||||
sys.exit(-1) |
@ -0,0 +1,37 @@ |
||||
import argparse |
||||
import logging |
||||
import sys |
||||
import traceback |
||||
import operator |
||||
|
||||
from fastText import train_unsupervised |
||||
from .encode import encode_contract, load_contracts |
||||
|
||||
logger = logging.getLogger("crytic-pred") |
||||
|
||||
def train(args): |
||||
|
||||
try: |
||||
model_filename = args.model |
||||
solc = args.solc |
||||
dirname = args.input |
||||
|
||||
if dirname is None: |
||||
logger.error('The train mode requires the directory parameter.') |
||||
sys.exit(-1) |
||||
|
||||
contracts = load_contracts(dirname) |
||||
with open("data.txt", 'w') as f: |
||||
for contract in contracts: |
||||
for function,ir in encode_contract(contract,solc).items(): |
||||
if ir != []: |
||||
f.write(" ".join(ir)+"\n") |
||||
|
||||
model = train_unsupervised(input='data.txt', model='skipgram') |
||||
model.save_model(model_filename) |
||||
print(model.get_words()) |
||||
|
||||
except Exception: |
||||
logger.error('Error in %s' % args.filename) |
||||
logger.error(traceback.format_exc()) |
||||
sys.exit(-1) |
Loading…
Reference in new issue