Beispiel #1
0
 def info(self):
     '''Displays verbose information about the transaction, including
     decoded event logs.'''
     if self.contract_address:
         line = "New Contract Address: " + self.contract_address
     else:
         line = "To: {0.receiver}\nValue: {0.value}".format(self)
         if self.input != "0x00":
             line += "\nFunction: {}".format(self.fn_name)
     formatted = (TX_INFO.format(
         self,
         self.sender if type(self.sender) is str else self.sender.address,
         line, "" if self.status else " ({0[error]}{1}{0})".format(
             color, self.revert_msg or "reverted"),
         self.gas_used / self.gas_limit))
     color.print_colors(formatted)
     if self.events:
         print(
             "   Events In This Transaction\n   --------------------------")
         for event in self.events:
             print("   " + color('bright yellow') + event['name'] + color())
             for i in event['data']:
                 color.print_colors("      {0[name]}: {0[value]}".format(i),
                                    value=None if i['decoded'] else "dull")
         print()
Beispiel #2
0
 def _dir(self, obj=None):
     if obj is None:
         obj = self
     results = [(i, getattr(obj, i)) for i in builtins.dir(obj)
                if i[0] != "_"]
     print("[" + "{}, ".format(color()).join(
         _dir_color(i[1]) + i[0] for i in results) + color() + "]")
Beispiel #3
0
def _print_path(trace, idx, sep):
    col = "error" if trace['op'] in ("REVERT", "INVALID") else "pending"
    name = "{0[contractName]}.{1}{0[fn]}".format(trace, color(col))
    print(
        ("  "*sep*trace['depth']) + ("  "*(trace['jumpDepth']-1)) +
        "{}{} {}{} ({})".format(color(col), name, color('dull'), idx, trace['address']) +
        color()
    )
Beispiel #4
0
def _dir_color(obj):
    if type(obj).__name__ == "module":
        return color('module')
    try:
        if issubclass(type(obj), _ContractBase):
            return color('contract')
        if issubclass(type(obj), _ContractMethod):
            return color('contract_method')
    except TypeError:
        pass
    if not callable(obj):
        return color('value')
    return color('callable')
Beispiel #5
0
 def __repr__(self):
     c = {-1: 'pending', 0: 'error', 1: None}
     return "<Transaction object '{}{}{}'>".format(color(c[self.status]),
                                                   self.txid, color)
Beispiel #6
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))