Пример #1
0
def token_approve(assets: str, mixer_addr: str, asset_addr: str, username: str,
                  password: str):
    """
    Approve the mixer to spend some amount of assets
    """
    approve_value = EtherValue(assets)
    #eth_addr = load_eth_address(eth_addr)
    #client_ctx = ctx.obj
    #web3 = open_web3_from_ctx(client_ctx)
    #mixer_desc = load_mixer_description_from_ctx(client_ctx)
    #if not mixer_desc.asset:
    #    raise ClickException("no asset for mixer {mixer_desc.mixer.address}")

    asset_instance = BAC001(asset_addr)
    keystore_file = "{}/{}/{}".format(USER_DIR, username, FISCO_ADDRESS_FILE)
    with open(keystore_file, "r") as dump_f:
        keytext = json.load(dump_f)
        privkey = Account.decrypt(keytext, password)
        asset_instance.client.ecdsa_account = Account.from_key(privkey)
        keypair = BcosKeyPair()
        keypair.private_key = asset_instance.client.ecdsa_account.privateKey
        keypair.public_key = asset_instance.client.ecdsa_account.publickey
        keypair.address = asset_instance.client.ecdsa_account.address
        asset_instance.client.keypair = keypair
    print(f"- {username} approves the transfer of BAC001asset to the Mixer")
    out, transactionReceipt = asset_instance.approve(mixer_addr,
                                                     approve_value.wei)
    print("approve tranaction output", out)
    outputresult = asset_instance.allowance(
        asset_instance.client.ecdsa_account.address, mixer_addr)
    print(f"- The allowance for the Mixer from {username} is: {outputresult}")
    return outputresult
Пример #2
0
def getNotes(request) -> None:
    result = {}
    req = json.loads(request.body)
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    addr_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                  ADDRESS_FILE_DEFAULT)
    if exists(keystore_file) and exists(addr_file):
        with open(keystore_file, "r") as dump_f:
            keytext = json.load(dump_f)
            if Account.decrypt(keytext, req['password']):
                (total, commits, values,
                 spend_commits) = ls_notes(req['username'])
                result['status'] = 0
                result['commits'] = commits
                result['values'] = values
                result['total_value'] = total.ether()
                result['spend_commits'] = spend_commits
                return JsonResponse(result)
            else:
                result['status'] = 1
                result['text'] = 'the password is not match the account'
                return JsonResponse(result)
    result['status'] = 1
    result[
        'text'] = 'your account is not recorded in server, please import it firstly or create a new one'
    return JsonResponse(result)
Пример #3
0
    def load_default_account(self):
        if client_config.crypto_type == CRYPTO_TYPE_GM:
            # 加载国密账号
            if self.gm_account is not None:
                return  # 不需要重复加载
            try:
                self.gm_account = GM_Account()
                self.gm_account_file = "{}/{}".format(client_config.account_keyfile_path,
                                                      client_config.gm_account_keyfile)
                if os.path.exists(self.gm_account_file) is False:
                    raise BcosException(("gm account keyfile file {} doesn't exist, "
                                         "please check client_config.py again "
                                         "and make sure this account exist")
                                        .format(self.gm_account_file))
                self.gm_account.load_from_file(
                    self.gm_account_file, client_config.gm_account_password)
                self.keypair = self.gm_account.keypair
                return
            except Exception as e:
                raise BcosException("load gm account from {} failed, reason: {}"
                                    .format(self.gm_account_file, e))

        # 默认的 ecdsa 账号
        try:
            if self.ecdsa_account is not None:
                return  # 不需要重复加载
            # check account keyfile
            self.keystore_file = "{}/{}".format(client_config.account_keyfile_path,
                                                client_config.account_keyfile)
            if os.path.exists(self.keystore_file) is False:
                raise BcosException(("keystore file {} doesn't exist, "
                                     "please check client_config.py again "
                                     "and make sure this account exist")
                                    .format(self.keystore_file))
            with open(self.keystore_file, "r") as dump_f:
                keytext = json.load(dump_f)
                privkey = Account.decrypt(keytext, client_config.account_password)
                self.ecdsa_account = Account.from_key(privkey)
                keypair = BcosKeyPair()
                keypair.private_key = self.ecdsa_account.privateKey
                keypair.public_key = self.ecdsa_account.publickey
                keypair.address = self.ecdsa_account.address
                self.keypair = keypair
        except Exception as e:
            raise BcosException("load account from {} failed, reason: {}"
                                .format(self.keystore_file, e))
Пример #4
0
def getBalance(request) -> None:
    result = {}
    req = json.loads(request.body)
    bac_instance = BAC001(req['token_address'])
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    with open(keystore_file, "r") as dump_f:
        keytext = json.load(dump_f)
        privkey = Account.decrypt(keytext, req['password'])
        bac_instance.client.ecdsa_account = Account.from_key(privkey)
        keypair = BcosKeyPair()
        keypair.private_key = bac_instance.client.ecdsa_account.privateKey
        keypair.public_key = bac_instance.client.ecdsa_account.publickey
        keypair.address = bac_instance.client.ecdsa_account.address
        bac_instance.client.keypair = keypair
    balance = bac_instance.balance(bac_instance.client.ecdsa_account.address)
    result['status'] = 0
    result['balance'] = balance[0] / 1000000000000000000.0
    return JsonResponse(result)
Пример #5
0
def faucet(request) -> None:
    print("apply for bac tokens")
    result = {}
    req = json.loads(request.body)
    asset_instance = BAC001(req['token_address'])
    asset_instance.client = BcosClient()
    value = EtherValue(req['value'])
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    with open(keystore_file, "r") as dump_f:
        keytext = json.load(dump_f)
        privatekey = Account.decrypt(keytext, req['password'])
        account = Account.privateKeyToAccount(privatekey)
        out, transactionReceipt = asset_instance.send(account.address,
                                                      value.wei, '')
        balance = asset_instance.balance(account.address)
        print("get tokens: ", balance)
        result['status'] = 0
        result['balance'] = balance[0] / 1000000000000000000.0
        return JsonResponse(result)
Пример #6
0
def genAccount(request) -> None:
    result = {}
    req = json.loads(request.body)
    if req['username'] is None or req['username'] == '':
        result['status'] = 0
        result['text'] = 'username cannot be null'
        return JsonResponse(result)
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    addr_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                  ADDRESS_FILE_DEFAULT)
    if exists(keystore_file) and exists(addr_file):
        with open(keystore_file, "r") as dump_f:
            keytext = json.load(dump_f)
            try:
                privatekey = Account.decrypt(keytext, req['password'])
            except ValueError:
                result['status'] = 1
                result['text'] = 'invalid password'
                return JsonResponse(result)
            account = Account.privateKeyToAccount(privatekey)
            result['fisco_address'] = account.address

        zbac_addr = load_zeth_address_public(req['username'])
        result['zbac_address'] = str(zbac_addr)
        result['status'] = 0
        result['text'] = 'account existed'
        return JsonResponse(result)
    (address, publickey,
     privatekey) = gen_fisco_address(req['username'], req['password'])
    zbac_addr = gen_address(req['username'])
    pubkey = ''.join(['%02X' % b for b in publickey])
    prikey = ''.join(['%02X' % b for b in privatekey])
    result['status'] = 0
    result['fisco_address'] = address
    result['publickey'] = "0x" + pubkey.lower()
    result['privatekey'] = "0x" + prikey.lower()
    result['zbac_address'] = str(zbac_addr)
    return JsonResponse(result)
Пример #7
0
def importFiscoAddr(request) -> None:
    result = {}
    req = json.loads(request.body)
    account = Account.privateKeyToAccount(req['privatekey'])
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    addr_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                  ADDRESS_FILE_DEFAULT)
    if exists(keystore_file):
        with open(keystore_file, "r") as dump_f:
            keytext = json.load(dump_f)
            privatekey = Account.decrypt(keytext, req['password'])
            account = Account.privateKeyToAccount(privatekey)
            result['fisco_address'] = account.address
        zbac_addr = load_zeth_address_public(req['username'])
        result['zbac_address'] = str(zbac_addr)
        result['status'] = 0
        result['text'] = 'keystore existed'
        return JsonResponse(result)
    user_dir = "{}/{}/{}".format(USER_DIR, req['username'], WALLET_DIR_DEFAULT)
    _ensure_dir(user_dir)
    keytext = Account.encrypt(account.privateKey, req['password'])
    with open(keystore_file, "w") as dump_f:
        json.dump(keytext, dump_f)
    zbac_addr = ''
    if not exists(addr_file):
        zbac_addr = zbac_addr + str(gen_address(req['username']))
    else:
        zbac_addr = zbac_addr + str(load_zeth_address_public(req['username']))
    result['status'] = 0
    print(f"{req['username']}'s address: {account.address}")
    print(f"{req['username']}'s publickey: {account.publickey}")
    print(f"fisco account keypair written to {keystore_file}")
    result['status'] = 0
    result['fisco_address'] = account.address
    pubkey = ''.join(['%02X' % b for b in account.publickey])
    result['publickey'] = "0x" + pubkey.lower()
    result['zbac_address'] = zbac_addr
    return JsonResponse(result)
Пример #8
0
def create_zeth_client_and_mixer_desc(
        prover_server_endpoint: str, mixer_addr: str, username: str,
        password: str) -> Tuple[MixerClient, MixerDescription]:
    """
    Create a MixerClient and MixerDescription object, for an existing deployment.
    """
    #web3 = open_web3_from_ctx(ctx)
    #mixer_desc = load_mixer_description_from_ctx(ctx)
    mixer_instance = Groth16Mixer(mixer_addr)
    keystore_file = "{}/{}/{}".format(USER_DIR, username, FISCO_ADDRESS_FILE)
    if exists(keystore_file) is False:
        raise ClickException(f"invalid output spec: {keystore_file}")
    with open(keystore_file, "r") as dump_f:
        keytext = json.load(dump_f)
        privkey = Account.decrypt(keytext, password)
        mixer_instance.client.ecdsa_account = Account.from_key(privkey)
        keypair = BcosKeyPair()
        keypair.private_key = mixer_instance.client.ecdsa_account.privateKey
        keypair.public_key = mixer_instance.client.ecdsa_account.publickey
        keypair.address = mixer_instance.client.ecdsa_account.address
        mixer_instance.client.keypair = keypair
    zeth_client = MixerClient.open(prover_server_endpoint, mixer_instance)
    return (zeth_client)
Пример #9
0
def checkUser(request) -> None:
    result = {}
    req = json.loads(request.body)
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    if exists(keystore_file):
        with open(keystore_file, "r") as dump_f:
            keytext = json.load(dump_f)
            privatekey = Account.decrypt(keytext, req['password'])
            if privatekey:
                result['status'] = 0
                prikey = ''.join(['%02X' % b for b in privatekey])
                result['privatekey'] = "0x" + prikey.lower()
                return JsonResponse(result)
            else:
                result['status'] = 1
                result['text'] = 'password not true'
                return JsonResponse(result)
    else:
        result['status'] = 1
        result[
            'text'] = 'your account is not recorded in server, please import it firstly or create a new one'
        return JsonResponse(result)
Пример #10
0
def getTransactions(request) -> None:
    result = {}
    req = json.loads(request.body)
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    if exists(keystore_file):
        with open(keystore_file, "r") as dump_f:
            keytext = json.load(dump_f)
            privatekey = Account.decrypt(keytext, req['password'])
            if privatekey:
                sqlSearch = "select * from transactions where username = %s"
                db.ping(reconnect=True)
                cursor.execute(sqlSearch, [req['username']])
                results = cursor.fetchall()
                db.commit()
                result['transactions'] = []
                for resultTra in results:
                    transacInfo = {
                        "trasactionType": resultTra[0],
                        "senderName": resultTra[1],
                        "publicInput": resultTra[2],
                        "publicOutput": resultTra[3],
                        "input_notes": resultTra[4],
                        "output_specs": resultTra[5],
                    }
                    result['transactions'].append(transacInfo)
                result['status'] = 0
                return JsonResponse(result)
            else:
                result['status'] = 1
                result['text'] = 'password not true'
                return JsonResponse(result)
    else:
        result['status'] = 1
        result[
            'text'] = 'your account is not recorded in server, please import it firstly or create a new one'
        return JsonResponse(result)
Пример #11
0
def sendAsset(request) -> None:
    result = {}
    req = json.loads(request.body)
    asset_instance = BAC001(req['token_address'])
    keystore_file = "{}/{}/{}".format(USER_DIR, req['username'],
                                      FISCO_ADDRESS_FILE)
    with open(keystore_file, "r") as dump_f:
        keytext = json.load(dump_f)
        privkey = Account.decrypt(keytext, req['password'])
        asset_instance.client.ecdsa_account = Account.from_key(privkey)
        keypair = BcosKeyPair()
        keypair.private_key = asset_instance.client.ecdsa_account.privateKey
        keypair.public_key = asset_instance.client.ecdsa_account.publickey
        keypair.address = asset_instance.client.ecdsa_account.address
        asset_instance.client.keypair = keypair
    print(f"- {req['username']}  the transfer of BAC001asset to the Mixer")
    value = EtherValue(req['value'])
    out, transactionReceipt = asset_instance.send(req['fiscoAddress'],
                                                  value.wei, '')
    print("send tranaction output {}", out)
    balance = asset_instance.balance(keypair.address)
    result['status'] = 0
    result['balance'] = balance[0] / 1000000000000000000.0
    return JsonResponse(result)
Пример #12
0
def main() -> None:
    print("***********************")
    zksnark = zeth.zksnark.get_zksnark_provider("GROTH16")
    #web3, eth = mock.open_test_web3()
    '''
    # Ethereum addresses
    deployer_eth_address = eth.accounts[0]
    bob_eth_address = eth.accounts[1]
    alice_eth_address = eth.accounts[2]
    charlie_eth_address = eth.accounts[3]
    '''

    account_keyfile_path = "python_web3/bin/accounts"  # 保存keystore文件的路径,在此路径下,keystore文件以 [name].keystore命名
    account_keyfile = "pyaccount.keystore"
    keystore_file = "{}/{}".format(account_keyfile_path, account_keyfile)
    with open(keystore_file, "r") as dump_f:
        keytext = json.load(dump_f)
        privkey = Account.decrypt(keytext, "123456")
        deployer_ac = Account.from_key(privkey)

    # Fisco-bcos addresses
    bob_password = "******"
    alice_password = "******"
    charlie_password = "******"
    bob_ac = Account.create(bob_password)
    alice_ac = Account.create(alice_password)
    charlie_ac = Account.create(charlie_password)
    #keypair
    deployer_keypair = BcosKeyPair()
    deployer_keypair.private_key = deployer_ac.privateKey
    deployer_keypair.public_key = deployer_ac.publickey
    deployer_keypair.address = deployer_ac.address
    bob_keypair = BcosKeyPair()
    bob_keypair.private_key = bob_ac.privateKey
    bob_keypair.public_key = bob_ac.publickey
    bob_keypair.address = bob_ac.address
    # alice_keypair = BcosKeyPair()
    # alice_keypair.private_key = alice_ac.privateKey
    # alice_keypair.public_key = alice_ac.publickey
    # alice_keypair.address = alice_ac.address
    charlie_keypair = BcosKeyPair()
    charlie_keypair.private_key = charlie_ac.privateKey
    charlie_keypair.public_key = charlie_ac.publickey
    charlie_keypair.address = charlie_ac.address

    # Zeth addresses
    keystore = mock.init_test_keystore()

    # Deploy Zeth contracts
    tree_depth = constants.ZETH_MERKLE_TREE_DEPTH

    asset_address = deploy_asset("AAAA", "AAA", 18, 100000000)
    token_instance = BAC001(asset_address)
    mixer_address = deploy(asset_address)
    mixer_instance = Groth16Mixer(mixer_address)
    mixer_instance.client.ecdsa_account = deployer_ac
    mixer_instance.client.keypair = deployer_keypair
    print("token address: ", mixer_instance.token())
    zeth_client = MixerClient.open(PROVER_SERVER_ENDPOINT_DEFAULT,
                                   mixer_instance)
    mk_tree = zeth.merkle_tree.MerkleTree.empty_with_depth(tree_depth)

    #mixer_instance = zeth_client.mixer_instance

    # Keys and wallets
    def _mk_wallet(name: str, sk: ZethAddressPriv) -> Wallet:
        wallet_dir = join(mock.TEST_NOTE_DIR, name + "-erc")
        if exists(wallet_dir):
            # Note: symlink-attack resistance
            #   https://docs.python.org/3/library/shutil.html#shutil.rmtree.avoids_symlink_attacks
            shutil.rmtree(wallet_dir)
        return Wallet(mixer_instance, name, wallet_dir, sk)

    sk_alice = keystore["Alice"].addr_sk
    sk_bob = keystore["Bob"].addr_sk
    sk_charlie = keystore["Charlie"].addr_sk
    # alice_wallet = _mk_wallet('alice', sk_alice)
    # bob_wallet = _mk_wallet('bob', sk_bob)
    # charlie_wallet = _mk_wallet('charlie', sk_charlie)
    #block_num = 1

    # Universal update function
    # def _receive_notes(
    #         out_ev: List[MixOutputEvents]) \
    #         -> Dict[str, List[ZethNoteDescription]]:
    #     #nonlocal block_num
    #     notes = {
    #         'alice': alice_wallet.receive_notes(out_ev),
    #         'bob': bob_wallet.receive_notes(out_ev),
    #         'charlie': charlie_wallet.receive_notes(out_ev),
    #     }
    #     alice_wallet.update_and_save_state()
    #     bob_wallet.update_and_save_state()
    #     charlie_wallet.update_and_save_state()
    #     #block_num = block_num + 1
    #     return notes

    print("[INFO] 4. Running tests (asset mixed: ERC20 token)...")
    # We assign ETHToken to Bob

    print("- Initial balances: ")
    print_token_balances(token_instance, bob_ac.address, alice_ac.address,
                         charlie_ac.address, mixer_address)

    # Bob tries to deposit ETHToken, split in 2 notes on the mixer (without
    # approving)
    token_instance.client.ecdsa_account = bob_ac
    token_instance.client.keypair = bob_keypair
    zeth_client.mixer_instance.client.ecdsa_account = bob_ac
    zeth_client.mixer_instance.client.keypair = bob_keypair

    # Bob approves the transfer
    print("- Bob approves the transfer of ETHToken to the Mixer")
    token_instance.client.ecdsa_account = bob_ac
    token_instance.client.keypair = bob_keypair
    (outputresult, receipt) = token_instance.send(bob_ac.address,
                                                  Web3.toWei(10000, 'ether'),
                                                  '')
    print("send *****", receipt['status'])
    (outputresult, receipt) = token_instance.approve(mixer_address,
                                                     scenario.BOB_DEPOSIT_ETH)
    # eth.waitForTransactionReceipt(tx_hash)
    outputresult = token_instance.allowance(deployer_ac.address, mixer_address)
    print("- The allowance for the Mixer from Bob is:", outputresult)
    # # Bob deposits ETHToken, split in 2 notes on the mixer
    # result_deposit_bob_to_bob = scenario.bob_deposit(
    #     zeth_client, mk_tree, bob_ac.address, keystore, zeth.utils.EtherValue(0))
    #
    # print("- Balances after Bob's deposit: ")
    # print_token_balances(
    #     token_instance,
    #     bob_ac.address,
    #     alice_ac.address,
    #     charlie_ac.address,
    #     mixer_address
    # )
    #
    # # Alice sees a deposit and tries to decrypt the ciphertexts to see if she
    # # was the recipient, but Bob was the recipient so Alice fails to decrypt
    # received_notes = _receive_notes(
    #     result_deposit_bob_to_bob.output_events)
    # recovered_notes_alice = received_notes['alice']
    # assert(len(recovered_notes_alice) == 0), \
    #     "Alice decrypted a ciphertext that was not encrypted with her key!"
    #
    # # Bob does a transfer of ETHToken to Charlie on the mixer
    #
    # # Bob decrypts one of the note he previously received (useless here but
    # # useful if the payment came from someone else)
    # recovered_notes_bob = received_notes['bob']
    # assert(len(recovered_notes_bob) == 2), \
    #     f"Bob recovered {len(recovered_notes_bob)} notes from deposit, expected 2"
    # input_bob_to_charlie = recovered_notes_bob[0].as_input()
    '''
    # Execution of the transfer
    result_transfer_bob_to_charlie = scenario.bob_to_charlie(
        zeth_client,
        mk_tree,
        input_bob_to_charlie,
        bob_eth_address,
        keystore)

    # Bob tries to spend `input_note_bob_to_charlie` twice
    result_double_spending = None
    try:
        result_double_spending = scenario.bob_to_charlie(
            zeth_client,
            mk_tree,
            input_bob_to_charlie,
            bob_eth_address,
            keystore)
    except Exception as e:
        print(f"Bob's double spending successfully rejected! (msg: {e})")
    assert(result_double_spending is None), "Bob spent the same note twice!"

    print("- Balances after Bob's transfer to Charlie: ")
    print_token_balances(
        token_instance,
        bob_eth_address,
        alice_eth_address,
        charlie_eth_address,
        zeth_client.mixer_instance.address
    )

    # Charlie tries to decrypt the notes from Bob's previous transaction.
    received_notes = _receive_notes(
        result_transfer_bob_to_charlie.output_events)
    note_descs_charlie = received_notes['charlie']
    assert(len(note_descs_charlie) == 1), \
        f"Charlie decrypted {len(note_descs_charlie)}.  Expected 1!"

    _ = scenario.charlie_withdraw(
        zeth_client,
        mk_tree,
        note_descs_charlie[0].as_input(),
        charlie_eth_address,
        keystore)

    print("- Balances after Charlie's withdrawal: ")
    print_token_balances(
        token_instance,
        bob_eth_address,
        alice_eth_address,
        charlie_eth_address,
        zeth_client.mixer_instance.address
    )

    # Charlie tries to carry out a double spend by withdrawing twice the same
    # note
    result_double_spending = None
    try:
        # New commitments are added in the tree at each withdraw so we
        # recompute the path to have the updated nodes
        result_double_spending = scenario.charlie_double_withdraw(
            zeth_client,
            mk_tree,
            note_descs_charlie[0].as_input(),
            charlie_eth_address,
            keystore)
    except Exception as e:
        print(f"Charlie's double spending successfully rejected! (msg: {e})")
    print("Balances after Charlie's double withdrawal attempt: ")
    assert(result_double_spending is None), \
        "Charlie managed to withdraw the same note twice!"
    print_token_balances(
        token_instance,
        bob_eth_address,
        alice_eth_address,
        charlie_eth_address,
        zeth_client.mixer_instance.address)

    # Bob deposits once again ETH, split in 2 notes on the mixer
    # But Charlie attempts to corrupt the transaction (malleability attack)

    # Bob approves the transfer
    print("- Bob approves the transfer of ETHToken to the Mixer")
    tx_hash = approve(
        token_instance,
        bob_eth_address,
        zeth_client.mixer_instance.address,
        scenario.BOB_DEPOSIT_ETH)
    eth.waitForTransactionReceipt(tx_hash)
    allowance_mixer = allowance(
        token_instance,
        bob_eth_address,
        zeth_client.mixer_instance.address)
    print("- The allowance for the Mixer from Bob is:", allowance_mixer)

    result_deposit_bob_to_bob = scenario.charlie_corrupt_bob_deposit(
        zeth_client,
        mk_tree,
        bob_eth_address,
        charlie_eth_address,
        keystore)

    # Bob decrypts one of the note he previously received (should fail if
    # Charlie's attack succeeded)
    received_notes = _receive_notes(
        result_deposit_bob_to_bob.output_events)
    recovered_notes_bob = received_notes['bob']
    assert(len(recovered_notes_bob) == 2), \
        f"Bob recovered {len(recovered_notes_bob)} notes from deposit, expected 2"

    print("- Balances after Bob's last deposit: ")
    print_token_balances(
        token_instance,
        bob_eth_address,
        alice_eth_address,
        charlie_eth_address,
        zeth_client.mixer_instance.address)
    '''
    print("========================================\n" +
          "              TESTS PASSED\n" +
          "========================================\n")