Ejemplo n.º 1
0
def topics():
    '''Generates event topics and saves them in brownie/topics.json'''
    if _topics:
        return _topics
    try:
        topics = json.load(open(
            CONFIG['folders']['brownie']+"/topics.json",
            encoding="utf-8"
        ))
    except (FileNotFoundError, json.decoder.JSONDecodeError):
        topics = {}
    contracts = compile_contracts()
    events = [x for i in contracts.values() for x in i['abi'] if x['type']=="event"]
    _topics.update(eth_event.get_event_abi(events))
    json.dump(
        _topics,
        open(CONFIG['folders']['brownie']+"/topics.json", 'w', encoding="utf-8"),
        sort_keys=True,
        indent=4
    )
    return _topics
Ejemplo n.º 2
0
 def setup(self):
     if self._init or config.ARGV['mode'] == "console":
         verbose = True
         self._init = False
     else:
         verbose = False
     if verbose:
         print("Using network '{0[string]}{1}{0}'".format(
             color, CONFIG['active_network']['name']
         ))
     if 'test-rpc' in CONFIG['active_network']:
         if verbose:
             print("Running '{0[string]}{1}{0}'...".format(
                 color, CONFIG['active_network']['test-rpc']
             ))
         rpc = Rpc(self)
     else:
         rpc = None
     web3._connect()
     accounts = Accounts(web3.eth.accounts)
     tx.tx_history.clear()
     self._network_dict = {
         'a': accounts,
         'accounts': accounts,
         'alert': alert,
         'check': check,
         'config': CONFIG,
         'gas': gas,
         'history': tx.tx_history,
         'logging': logging,
         'reset': self.reset,
         'run': self.run,
         'rpc': rpc,
         'web3': web3,
         'wei': wei
     }
     for name, build in compiler.compile_contracts().items():
         if build['type'] == "interface":
             continue
         if name in self._network_dict:
             raise AttributeError("Namespace collision between Contract '{0}' and 'Network.{0}'".format(name))
         self._network_dict[name] = contract.ContractContainer(build, self._network_dict)
     if self._module:
         self._module.__dict__.update(self._network_dict)
     
     # update _ImportableBrownie dict and reload all scripts
     sys.modules['brownie'].__dict__ = self._network_dict
     for module in [v for k,v in sys.modules.items() if k[:7]=='scripts']:
         importlib.reload(module)
     
     if not CONFIG['active_network']['persist']:
         return
     while True:
         persist_file = "build/networks/{}.json".format(CONFIG['active_network']['name'])
         exists = os.path.exists(persist_file)
         if not exists:
             print("Persistent environment for '{}' has not yet been declared.".format(
                 CONFIG['active_network']['name']))
             self._key = FernetKey(getpass(
                 "Please set a password for the persistent environment: "
             ))
             json.dump(
                 {
                     'height': web3.eth.blockNumber,
                     'password': self._key.encrypt('password', False)
                 },
                 open(persist_file, 'w', encoding="utf-8"),
                 sort_keys=True,
                 indent=4
             )
             return
         try:
             data = json.load(open(persist_file, encoding="utf-8"))
             if data['height'] > web3.eth.blockNumber:
                 print(
                     "WARNING: This appears to be a local RPC network. Persistence is not possible."
                     "\n         Remove 'persist': true from config.json to silence this warning."
                 )
                 CONFIG['active_network']['persist'] = False
                 return
             if not self._key:
                 self._key = FernetKey(getpass(
                     "Enter the persistence password for '{}': ".format(
                         CONFIG['active_network']['name'])))
             self._key.decrypt(data['password'])
             print("Loading persistent environment...")
             for priv_key in data['accounts']:
                 self._network_dict['accounts'].add(self._key.decrypt(priv_key))
             break
         except InvalidToken:
             self._key = None
             print("Password is incorrect, please try again or CTRL-C to disable persistence.")
         except KeyboardInterrupt:
             self._key = None
             print("\nPersistence has been disabled.")
             return
Ejemplo n.º 3
0
def main():
    args = docopt(__doc__)

    if args['<filename>']:
        name = args['<filename>'].replace(".py", "")
        if not os.path.exists("tests/{}.py".format(name)):
            sys.exit("{0[error]}ERROR{0}: Cannot find".format(color) +
                     " {0[module]}tests/{1}.py{0}".format(color, name))
        test_files = [name]
    else:
        test_files = [i[:-3] for i in os.listdir("tests") if i[-3:] == ".py"]
        test_files.remove('__init__')

    compiled = deepcopy(compile_contracts())
    fn_map, line_map = get_coverage_map(compiled)
    network = Network()

    if args['--always-transact']:
        CONFIG['test']['always_transact'] = True
    print("Contract calls will be handled as: {0[value]}{1}{0}".format(
        color,
        "transactions" if CONFIG['test']['always_transact'] else "calls"))

    for filename in test_files:
        history, tb = run_test(filename, network)
        if tb:
            sys.exit("\n{0[error]}ERROR{0}: Cannot ".format(color) +
                     "calculate coverage while tests are failing\n")
        for tx in history:
            if not tx.receiver:
                continue
            for i in range(len(tx.trace)):
                t = tx.trace[i]
                pc = t['pc']
                name = t['contractName']
                if not name:
                    continue
                try:
                    # find the function map item and record the tx
                    fn = next(i for i in fn_map[name] if pc in i['pc'])
                    fn['tx'].add(tx)
                    if t['op'] != "JUMPI":
                        # if not a JUMPI, find the line map item and record
                        ln = next(i for i in line_map[name] if pc in i['pc'])
                        ln['tx'].add(tx)
                        continue
                    # if a JUMPI, we need to have hit the jump pc AND a related opcode
                    ln = next(i for i in line_map[name] if pc == i['jump'])
                    if tx not in ln['tx']:
                        continue
                    # if the next opcode is not pc+1, the JUMPI was executed truthy
                    key = 'false' if tx.trace[i +
                                              1]['pc'] == pc + 1 else 'true'
                    ln[key].add(tx)
                # pc didn't exist in map
                except StopIteration:
                    continue

    for ln in [x for v in line_map.values() for x in v]:
        if ln['jump']:
            ln['jump'] = [len(ln.pop('true')), len(ln.pop('false'))]
        ln['count'] = len(ln.pop('tx'))
        del ln['pc']

    for contract in fn_map:
        for fn in fn_map[contract].copy():
            fn['count'] = len(fn.pop('tx'))
            del fn['pc']
            line_fn = [
                i for i in line_map[contract] if i['method'] == fn['method']
            ]
            if not fn['count'] or not [i for i in line_fn if i['count']]:
                for ln in line_fn:
                    line_map[contract].remove(ln)
            elif line_fn:
                fn_map[contract].remove(fn)
        fn_map[contract].extend(line_map[contract])

    json.dump(fn_map,
              open("build/coverage.json", "w", encoding="utf-8"),
              sort_keys=True,
              indent=4)
    print("\nCoverage analysis complete!\n")
    for contract in fn_map:
        fn_list = sorted(
            set(i['method'] for i in fn_map[contract] if i['method']))
        if not fn_list:
            continue
        if not [i for i in fn_map[contract] if i['count']]:
            print("  contract: {0[contract]}{1}{0} - {0[bright red]}0.0%{0}".
                  format(color, contract))
            continue
        print("  contract: {0[contract]}{1}{0}".format(color, contract))
        for fn in fn_list:
            map_ = [i for i in fn_map[contract] if i['method'] == fn]
            count = 0
            for i in map_:
                if not i['count']:
                    continue
                if not i['jump']:
                    count += 1
                    continue
                if i['jump'][0]:
                    count += 1
                if i['jump'][1]:
                    count += 1
            total = sum([1 if not i['jump'] else 2 for i in map_])
            pct = count / total
            c = next(i[1] for i in COVERAGE_COLORS if pct <= i[0])
            print("    {0[contract_method]}{1}{0} - {2}{3:.1%}{0}".format(
                color, fn, color(c), pct))
        print()
    print(
        "\nDetailed results saved to {0[string]}build/coverage.json{0}".format(
            color))