mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-24 03:54:21 +01:00
Merge branch 'main' into Adjusted_Documentation
This commit is contained in:
@@ -8,6 +8,13 @@ CASHU_DIR=~/.cashu
|
||||
MINT_HOST=127.0.0.1
|
||||
MINT_PORT=3338
|
||||
|
||||
# use builtin tor, this overrides SOCKS_HOST and SOCKS_PORT
|
||||
TOR=TRUE
|
||||
|
||||
# use custom tor proxy, use with TOR=false
|
||||
SOCKS_HOST=localhost
|
||||
SOCKS_PORT=9050
|
||||
|
||||
# MINT
|
||||
|
||||
MINT_PRIVATE_KEY=supersecretprivatekey
|
||||
|
||||
5
.github/workflows/tests.yml
vendored
5
.github/workflows/tests.yml
vendored
@@ -4,9 +4,10 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
poetry:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
python-version: ["3.9"]
|
||||
poetry-version: ["1.2.1"]
|
||||
steps:
|
||||
@@ -22,6 +23,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
poetry install --with dev
|
||||
shell: bash
|
||||
- name: Run mint
|
||||
env:
|
||||
LIGHTNING: False
|
||||
@@ -35,6 +37,7 @@ jobs:
|
||||
LIGHTNING: False
|
||||
MINT_HOST: localhost
|
||||
MINT_PORT: 3338
|
||||
TOR: False
|
||||
run: |
|
||||
poetry run pytest tests --cov-report xml --cov cashu
|
||||
- name: Upload coverage to Codecov
|
||||
|
||||
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
@@ -0,0 +1,2 @@
|
||||
include requirements.txt
|
||||
recursive-include cashu/tor *
|
||||
@@ -105,7 +105,7 @@ cashu info
|
||||
|
||||
Returns:
|
||||
```bash
|
||||
Version: 0.4.2
|
||||
Version: 0.5.2
|
||||
Debug: False
|
||||
Cashu dir: /home/user/.cashu
|
||||
Wallet: wallet
|
||||
|
||||
@@ -2,23 +2,28 @@
|
||||
|
||||
"""
|
||||
Implementation of https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406
|
||||
Alice:
|
||||
|
||||
Alice (Client):
|
||||
A = a*G
|
||||
return A
|
||||
Bob:
|
||||
|
||||
Bob (Mint):
|
||||
Y = hash_to_curve(secret_message)
|
||||
r = random blinding factor
|
||||
B'= Y + r*G
|
||||
return B'
|
||||
|
||||
Alice:
|
||||
C' = a*B'
|
||||
(= a*Y + a*r*G)
|
||||
return C'
|
||||
|
||||
Bob:
|
||||
C = C' - r*A
|
||||
(= C' - a*r*G)
|
||||
(= a*Y)
|
||||
return C, secret_message
|
||||
|
||||
Alice:
|
||||
Y = hash_to_curve(secret_message)
|
||||
C == a*Y
|
||||
|
||||
@@ -14,7 +14,9 @@ class P2SHScript(BaseModel):
|
||||
|
||||
|
||||
class Proof(BaseModel):
|
||||
id: str = ""
|
||||
id: Union[
|
||||
None, str
|
||||
] = "" # NOTE: None for backwards compatibility of old clients < 0.3
|
||||
amount: int = 0
|
||||
secret: str = ""
|
||||
C: str = ""
|
||||
|
||||
@@ -4,8 +4,12 @@ from secp256k1 import PublicKey
|
||||
|
||||
|
||||
def hash_to_point_pre_0_3_3(secret_msg):
|
||||
"""Generates x coordinate from the message hash and checks if the point lies on the curve.
|
||||
If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
|
||||
"""
|
||||
NOTE: Clients pre 0.3.3 used a different hash_to_curve
|
||||
|
||||
Generates x coordinate from the message hash and checks if the point lies on the curve.
|
||||
If it does not, it tries computing again a new x coordinate from the hash of the coordinate.
|
||||
"""
|
||||
point = None
|
||||
msg = secret_msg
|
||||
while point is None:
|
||||
|
||||
@@ -24,6 +24,11 @@ CASHU_DIR = env.str("CASHU_DIR", default=os.path.join(str(Path.home()), ".cashu"
|
||||
CASHU_DIR = CASHU_DIR.replace("~", str(Path.home()))
|
||||
assert len(CASHU_DIR), "CASHU_DIR not defined"
|
||||
|
||||
TOR = env.bool("TOR", default=True)
|
||||
|
||||
SOCKS_HOST = env.str("SOCKS_HOST", default=None)
|
||||
SOCKS_PORT = env.int("SOCKS_PORT", default=9050)
|
||||
|
||||
LIGHTNING = env.bool("LIGHTNING", default=True)
|
||||
LIGHTNING_FEE_PERCENT = env.float("LIGHTNING_FEE_PERCENT", default=1.0)
|
||||
assert LIGHTNING_FEE_PERCENT >= 0, "LIGHTNING_FEE_PERCENT must be at least 0"
|
||||
@@ -48,4 +53,4 @@ LNBITS_ENDPOINT = env.str("LNBITS_ENDPOINT", default=None)
|
||||
LNBITS_KEY = env.str("LNBITS_KEY", default=None)
|
||||
|
||||
MAX_ORDER = 64
|
||||
VERSION = "0.4.2"
|
||||
VERSION = "0.5.2"
|
||||
|
||||
@@ -64,11 +64,13 @@ class Ledger:
|
||||
if not len(tmp_keyset_local):
|
||||
logger.debug(f"Storing keyset {keyset.id}.")
|
||||
await self.crud.store_keyset(keyset=keyset, db=self.db)
|
||||
|
||||
# store the new keyset in the current keysets
|
||||
self.keysets.keysets[keyset.id] = keyset
|
||||
return keyset
|
||||
|
||||
async def init_keysets(self):
|
||||
"""Loads all keysets from db."""
|
||||
self.keyset = await self.load_keyset(self.derivation_path)
|
||||
# load all past keysets from db
|
||||
tmp_keysets: List[MintKeyset] = await self.crud.get_keyset(db=self.db)
|
||||
self.keysets = MintKeysets(tmp_keysets)
|
||||
@@ -77,6 +79,8 @@ class Ledger:
|
||||
for _, v in self.keysets.keysets.items():
|
||||
logger.debug(f"Generating keys for keyset {v.id}")
|
||||
v.generate_keys(self.master_key)
|
||||
# load the current keyset
|
||||
self.keyset = await self.load_keyset(self.derivation_path)
|
||||
|
||||
async def _generate_promises(
|
||||
self, B_s: List[BlindedMessage], keyset: MintKeyset = None
|
||||
|
||||
@@ -104,8 +104,10 @@ async def split(
|
||||
"""
|
||||
proofs = payload.proofs
|
||||
amount = payload.amount
|
||||
|
||||
# NOTE: backwards compatibility with clients < v0.2.2
|
||||
outputs = payload.outputs.blinded_messages if payload.outputs else None
|
||||
# backwards compatibility with clients < v0.2.2
|
||||
|
||||
assert outputs, Exception("no outputs provided.")
|
||||
try:
|
||||
split_return = await ledger.split(proofs, amount, outputs)
|
||||
|
||||
1609
cashu/tor/LICENCE_tor
Executable file
1609
cashu/tor/LICENCE_tor
Executable file
File diff suppressed because it is too large
Load Diff
0
cashu/tor/__init__.py
Normal file
0
cashu/tor/__init__.py
Normal file
BIN
cashu/tor/bundle/.DS_Store
vendored
Executable file
BIN
cashu/tor/bundle/.DS_Store
vendored
Executable file
Binary file not shown.
389
cashu/tor/bundle/linux/LICENSE
Executable file
389
cashu/tor/bundle/linux/LICENSE
Executable file
@@ -0,0 +1,389 @@
|
||||
This file contains the license for Tor,
|
||||
a free software project to provide anonymity on the Internet.
|
||||
|
||||
It also lists the licenses for other components used by Tor.
|
||||
|
||||
For more information about Tor, see https://www.torproject.org/.
|
||||
|
||||
If you got this file as a part of a larger bundle,
|
||||
there may be other license terms that you should be aware of.
|
||||
|
||||
===============================================================================
|
||||
Tor is distributed under the "3-clause BSD" license, a commonly used
|
||||
software license that means Tor is both free software and open source:
|
||||
|
||||
Copyright (c) 2001-2004, Roger Dingledine
|
||||
Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
|
||||
Copyright (c) 2007-2019, The Tor Project, Inc.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the names of the copyright owners nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
===============================================================================
|
||||
src/ext/strlcat.c and src/ext/strlcpy.c by Todd C. Miller are licensed
|
||||
under the following license:
|
||||
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
===============================================================================
|
||||
src/ext/tor_queue.h is licensed under the following license:
|
||||
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
|
||||
===============================================================================
|
||||
src/ext/csiphash.c is licensed under the following license:
|
||||
|
||||
Copyright (c) 2013 Marek Majkowski <marek@popcount.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
===============================================================================
|
||||
Trunnel is distributed under this license:
|
||||
|
||||
Copyright 2014 The Tor Project, Inc.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the names of the copyright owners nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
===============================================================================
|
||||
getdelim.c is distributed under this license:
|
||||
|
||||
Copyright (c) 2011 The NetBSD Foundation, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This code is derived from software contributed to The NetBSD Foundation
|
||||
by Christos Zoulas.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
===============================================================================
|
||||
src/config/geoip and src/config/geoip6:
|
||||
|
||||
These files are based on the IPFire Location Database. For more
|
||||
information, see https://location.ipfire.org/.
|
||||
|
||||
The data is distributed under a creative commons "BY-SA 4.0" license.
|
||||
|
||||
Find the full license terms at:
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
===============================================================================
|
||||
m4/pc_from_ucontext.m4 is available under the following license. Note that
|
||||
it is *not* built into the Tor software.
|
||||
|
||||
Copyright (c) 2005, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
===============================================================================
|
||||
m4/pkg.m4 is available under the following license. Note that
|
||||
it is *not* built into the Tor software.
|
||||
|
||||
pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
serial 1 (pkg-config-0.24)
|
||||
|
||||
Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception to the GNU General Public License, if you
|
||||
distribute this file as part of a program that contains a
|
||||
configuration script generated by Autoconf, you may include it under
|
||||
the same distribution terms that you use for the rest of that program.
|
||||
===============================================================================
|
||||
src/ext/readpassphrase.[ch] are distributed under this license:
|
||||
|
||||
Copyright (c) 2000-2002, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
Sponsored in part by the Defense Advanced Research Projects
|
||||
Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
|
||||
===============================================================================
|
||||
src/ext/mulodi4.c is distributed under this license:
|
||||
|
||||
=========================================================================
|
||||
compiler_rt License
|
||||
=========================================================================
|
||||
|
||||
The compiler_rt library is dual licensed under both the
|
||||
University of Illinois "BSD-Like" license and the MIT license.
|
||||
As a user of this code you may choose to use it under either
|
||||
license. As a contributor, you agree to allow your code to be
|
||||
used under both.
|
||||
|
||||
Full text of the relevant licenses is included below.
|
||||
|
||||
=========================================================================
|
||||
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2009-2016 by the contributors listed in CREDITS.TXT
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal with the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimers in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois
|
||||
at Urbana-Champaign, nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this
|
||||
Software without specific prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS WITH THE SOFTWARE.
|
||||
|
||||
=========================================================================
|
||||
|
||||
Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
=========================================================================
|
||||
Copyrights and Licenses for Third Party Software Distributed with LLVM:
|
||||
=========================================================================
|
||||
|
||||
The LLVM software contains code written by third parties. Such
|
||||
software will have its own individual LICENSE.TXT file in the
|
||||
directory in which it appears. This file will describe the
|
||||
copyrights, license, and restrictions which apply to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open
|
||||
Source License applies to all code in the LLVM Distribution, and
|
||||
nothing in any of the other licenses gives permission to use the
|
||||
names of the LLVM Team or the University of Illinois to endorse
|
||||
or promote products derived from this Software.
|
||||
|
||||
===============================================================================
|
||||
If you got Tor as a static binary with OpenSSL included, then you should know:
|
||||
"This product includes software developed by the OpenSSL Project
|
||||
for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
===============================================================================
|
||||
BIN
cashu/tor/bundle/linux/libcrypto.so.1.1
Executable file
BIN
cashu/tor/bundle/linux/libcrypto.so.1.1
Executable file
Binary file not shown.
BIN
cashu/tor/bundle/linux/libevent-2.1.so.7
Executable file
BIN
cashu/tor/bundle/linux/libevent-2.1.so.7
Executable file
Binary file not shown.
BIN
cashu/tor/bundle/linux/libssl.so.1.1
Executable file
BIN
cashu/tor/bundle/linux/libssl.so.1.1
Executable file
Binary file not shown.
BIN
cashu/tor/bundle/linux/libstdc++/libstdc++.so.6
Executable file
BIN
cashu/tor/bundle/linux/libstdc++/libstdc++.so.6
Executable file
Binary file not shown.
BIN
cashu/tor/bundle/linux/tor
Executable file
BIN
cashu/tor/bundle/linux/tor
Executable file
Binary file not shown.
BIN
cashu/tor/bundle/mac/libevent-2.1.7.dylib
Executable file
BIN
cashu/tor/bundle/mac/libevent-2.1.7.dylib
Executable file
Binary file not shown.
BIN
cashu/tor/bundle/mac/tor
Executable file
BIN
cashu/tor/bundle/mac/tor
Executable file
Binary file not shown.
34
cashu/tor/timeout.py
Executable file
34
cashu/tor/timeout.py
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def main():
|
||||
assert len(sys.argv) > 2, "Usage: timeout.py [seconds] [command...]"
|
||||
# cmd = " ".join(sys.argv[2:]) # for with shell=True
|
||||
cmd = sys.argv[2:]
|
||||
timeout = int(sys.argv[1])
|
||||
assert timeout > 0, "timeout (in seconds) must be a positive integer."
|
||||
start_time = time.time()
|
||||
|
||||
pro = subprocess.Popen(cmd, shell=False)
|
||||
|
||||
while time.time() < start_time + timeout + 1:
|
||||
time.sleep(1)
|
||||
pro.terminate()
|
||||
pro.wait()
|
||||
pro.kill()
|
||||
pro.wait()
|
||||
|
||||
# we kill the child processes as well (tor.py and tor) just to be sure
|
||||
os.kill(pro.pid + 1, 15)
|
||||
os.kill(pro.pid + 1, 9)
|
||||
|
||||
os.kill(pro.pid + 2, 15)
|
||||
os.kill(pro.pid + 2, 9)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
177
cashu/tor/tor.py
Executable file
177
cashu/tor/tor.py
Executable file
@@ -0,0 +1,177 @@
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class TorProxy:
|
||||
def __init__(self, timeout=False):
|
||||
self.base_path = pathlib.Path(__file__).parent.resolve()
|
||||
self.platform = platform.system()
|
||||
self.timeout = 60 * 60 if timeout else 0 # seconds
|
||||
self.tor_proc = None
|
||||
self.pid_file = os.path.join(self.base_path, "tor.pid")
|
||||
self.tor_pid = None
|
||||
self.startup_finished = True
|
||||
self.tor_running = self.is_running()
|
||||
|
||||
@classmethod
|
||||
def check_platform(cls):
|
||||
if platform.system() == "Linux":
|
||||
if platform.machine() != "x86_64":
|
||||
logger.debug("Builtin Tor not supported on this platform.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def log_status(self):
|
||||
logger.debug(f"Tor binary path: {self.tor_path()}")
|
||||
logger.debug(f"Tor config path: {self.tor_config_path()}")
|
||||
logger.debug(f"Tor running: {self.tor_running}")
|
||||
logger.debug(
|
||||
f"Tor port open: {self.is_port_open()}",
|
||||
)
|
||||
logger.debug(f"Tor PID in tor.pid: {self.read_pid()}")
|
||||
logger.debug(f"Tor PID running: {self.signal_pid(self.read_pid())}")
|
||||
|
||||
def run_daemon(self, verbose=False):
|
||||
if not self.check_platform() or self.tor_running:
|
||||
return
|
||||
self.log_status()
|
||||
logger.debug("Starting Tor")
|
||||
cmd = [f"{self.tor_path()}", "--defaults-torrc", f"{self.tor_config_path()}"]
|
||||
if self.timeout:
|
||||
logger.debug(f"Starting tor with timeout {self.timeout}s")
|
||||
cmd = [
|
||||
sys.executable,
|
||||
os.path.join(self.base_path, "timeout.py"),
|
||||
f"{self.timeout}",
|
||||
] + cmd
|
||||
env = dict(os.environ)
|
||||
if platform.system() == "Linux":
|
||||
env["LD_LIBRARY_PATH"] = os.path.dirname(self.tor_path())
|
||||
elif platform.system() == "Darwin":
|
||||
env["DYLD_LIBRARY_PATH"] = os.path.dirname(self.tor_path())
|
||||
self.tor_proc = subprocess.Popen(
|
||||
cmd,
|
||||
env=env,
|
||||
shell=False,
|
||||
close_fds=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
start_new_session=True,
|
||||
)
|
||||
logger.debug("Running tor daemon with pid {}".format(self.tor_proc.pid))
|
||||
with open(self.pid_file, "w", encoding="utf-8") as f:
|
||||
f.write(str(self.tor_proc.pid))
|
||||
|
||||
self.wait_until_startup(verbose=verbose)
|
||||
|
||||
def stop_daemon(self, pid=None):
|
||||
pid = pid or self.tor_proc.pid if self.tor_proc else None
|
||||
if self.tor_proc and pid:
|
||||
self.signal_pid(pid, 15) # sigterm
|
||||
time.sleep(5)
|
||||
self.signal_pid(pid, 9) # sigkill
|
||||
|
||||
if os.path.exists(self.pid_file):
|
||||
os.remove(self.pid_file)
|
||||
|
||||
def tor_path(self):
|
||||
PATHS = {
|
||||
"Windows": os.path.join(self.base_path, "bundle", "win", "Tor", "tor.exe"),
|
||||
"Linux": os.path.join(self.base_path, "bundle", "linux", "tor"),
|
||||
"Darwin": os.path.join(self.base_path, "bundle", "mac", "tor"),
|
||||
}
|
||||
# make sure that file has correct permissions
|
||||
try:
|
||||
logger.debug(f"Setting permissions of {PATHS[platform.system()]} to 755")
|
||||
os.chmod(PATHS[platform.system()], 0o755)
|
||||
except:
|
||||
logger.debug("Exception: could not set permissions of Tor binary")
|
||||
return PATHS[platform.system()]
|
||||
|
||||
def tor_config_path(self):
|
||||
return os.path.join(self.base_path, "torrc")
|
||||
|
||||
def is_running(self):
|
||||
# another tor proxy is running
|
||||
if not self.is_port_open():
|
||||
return False
|
||||
# our tor proxy running from a previous session
|
||||
if self.signal_pid(self.read_pid()):
|
||||
return True
|
||||
# current attached process running
|
||||
return self.tor_proc and self.tor_proc.poll() is None
|
||||
|
||||
def wait_until_startup(self, verbose=False):
|
||||
if not self.check_platform():
|
||||
return
|
||||
if self.is_port_open():
|
||||
return
|
||||
if self.tor_proc is None:
|
||||
raise Exception("Tor proxy not attached.")
|
||||
if not self.tor_proc.stdout:
|
||||
raise Exception("could not get tor stdout.")
|
||||
if verbose:
|
||||
print("Starting Tor...", end="", flush=True)
|
||||
for line in self.tor_proc.stdout:
|
||||
# print(line)
|
||||
if verbose:
|
||||
print(".", end="", flush=True)
|
||||
if "Bootstrapped 100%" in str(line):
|
||||
if verbose:
|
||||
print("done", flush=True)
|
||||
break
|
||||
# tor is ready
|
||||
self.startup_finished = True
|
||||
return
|
||||
|
||||
def is_port_open(self):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
location = ("127.0.0.1", 9050)
|
||||
try:
|
||||
s.connect(location)
|
||||
s.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def read_pid(self):
|
||||
if not os.path.isfile(self.pid_file):
|
||||
return None
|
||||
with open(self.pid_file, "r") as f:
|
||||
pid = f.readlines()
|
||||
# check if pid is valid
|
||||
if len(pid) == 0 or not int(pid[0]) > 0:
|
||||
return None
|
||||
return pid[0]
|
||||
|
||||
def signal_pid(self, pid, signal=0):
|
||||
"""
|
||||
Checks whether a process with pid is running (signal 0 is not a kill signal!)
|
||||
or stops (signal 15) or kills it (signal 9).
|
||||
"""
|
||||
if not pid:
|
||||
return False
|
||||
if not int(pid) > 0:
|
||||
return False
|
||||
pid = int(pid)
|
||||
try:
|
||||
os.kill(pid, signal)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tor = TorProxy(timeout=True)
|
||||
tor.run_daemon(verbose=True)
|
||||
# time.sleep(5)
|
||||
# logger.debug("Killing Tor")
|
||||
# tor.stop_daemon()
|
||||
254
cashu/tor/torrc
Executable file
254
cashu/tor/torrc
Executable file
@@ -0,0 +1,254 @@
|
||||
## Configuration file for a typical Tor user
|
||||
## Last updated 28 February 2019 for Tor 0.3.5.1-alpha.
|
||||
## (may or may not work for much older or much newer versions of Tor.)
|
||||
##
|
||||
## Lines that begin with "## " try to explain what's going on. Lines
|
||||
## that begin with just "#" are disabled commands: you can enable them
|
||||
## by removing the "#" symbol.
|
||||
##
|
||||
## See 'man tor', or https://www.torproject.org/docs/tor-manual.html,
|
||||
## for more options you can use in this file.
|
||||
##
|
||||
## Tor will look for this file in various places based on your platform:
|
||||
## https://www.torproject.org/docs/faq#torrc
|
||||
|
||||
## Tor opens a SOCKS proxy on port 9050 by default -- even if you don't
|
||||
## configure one below. Set "SOCKSPort 0" if you plan to run Tor only
|
||||
## as a relay, and not make any local application connections yourself.
|
||||
SOCKSPort 9050 # Default: Bind to localhost:9050 for local connections.
|
||||
#SOCKSPort 192.168.0.1:9100 # Bind to this address:port too.
|
||||
|
||||
## Entry policies to allow/deny SOCKS requests based on IP address.
|
||||
## First entry that matches wins. If no SOCKSPolicy is set, we accept
|
||||
## all (and only) requests that reach a SOCKSPort. Untrusted users who
|
||||
## can access your SOCKSPort may be able to learn about the connections
|
||||
## you make.
|
||||
#SOCKSPolicy accept 192.168.0.0/16
|
||||
#SOCKSPolicy accept6 FC00::/7
|
||||
#SOCKSPolicy reject *
|
||||
SOCKSPolicy accept 127.0.0.1
|
||||
|
||||
## Logs go to stdout at level "notice" unless redirected by something
|
||||
## else, like one of the below lines. You can have as many Log lines as
|
||||
## you want.
|
||||
##
|
||||
## We advise using "notice" in most cases, since anything more verbose
|
||||
## may provide sensitive information to an attacker who obtains the logs.
|
||||
##
|
||||
## Send all messages of level 'notice' or higher to /usr/local/var/log/tor/notices.log
|
||||
#Log notice file /usr/local/var/log/tor/notices.log
|
||||
## Send every possible message to /usr/local/var/log/tor/debug.log
|
||||
#Log debug file /usr/local/var/log/tor/debug.log
|
||||
## Use the system log instead of Tor's logfiles
|
||||
#Log notice syslog
|
||||
## To send all messages to stderr:
|
||||
#Log debug stderr
|
||||
|
||||
## Uncomment this to start the process in the background... or use
|
||||
## --runasdaemon 1 on the command line. This is ignored on Windows;
|
||||
## see the FAQ entry if you want Tor to run as an NT service.
|
||||
#RunAsDaemon 1
|
||||
|
||||
## The directory for keeping all the keys/etc. By default, we store
|
||||
## things in $HOME/.tor on Unix, and in Application Data\tor on Windows.
|
||||
#DataDirectory /usr/local/var/lib/tor
|
||||
|
||||
## The port on which Tor will listen for local connections from Tor
|
||||
## controller applications, as documented in control-spec.txt.
|
||||
ControlPort 9051
|
||||
## If you enable the controlport, be sure to enable one of these
|
||||
## authentication methods, to prevent attackers from accessing it.
|
||||
HashedControlPassword 16:3F85DAF2A2A34032603235343E19ABBE3CB6BF03F1443984F21EEE749F
|
||||
#CookieAuthentication 1
|
||||
|
||||
############### This section is just for location-hidden services ###
|
||||
|
||||
## Once you have configured a hidden service, you can look at the
|
||||
## contents of the file ".../hidden_service/hostname" for the address
|
||||
## to tell people.
|
||||
##
|
||||
## HiddenServicePort x y:z says to redirect requests on port x to the
|
||||
## address y:z.
|
||||
|
||||
#HiddenServiceDir /usr/local/var/lib/tor/hidden_service/
|
||||
#HiddenServicePort 80 127.0.0.1:80
|
||||
|
||||
#HiddenServiceDir /usr/local/var/lib/tor/other_hidden_service/
|
||||
#HiddenServicePort 80 127.0.0.1:80
|
||||
#HiddenServicePort 22 127.0.0.1:22
|
||||
|
||||
################ This section is just for relays #####################
|
||||
#
|
||||
## See https://www.torproject.org/docs/tor-doc-relay for details.
|
||||
|
||||
## Required: what port to advertise for incoming Tor connections.
|
||||
#ORPort 9001
|
||||
## If you want to listen on a port other than the one advertised in
|
||||
## ORPort (e.g. to advertise 443 but bind to 9090), you can do it as
|
||||
## follows. You'll need to do ipchains or other port forwarding
|
||||
## yourself to make this work.
|
||||
#ORPort 443 NoListen
|
||||
#ORPort 127.0.0.1:9090 NoAdvertise
|
||||
## If you want to listen on IPv6 your numeric address must be explictly
|
||||
## between square brackets as follows. You must also listen on IPv4.
|
||||
#ORPort [2001:DB8::1]:9050
|
||||
|
||||
## The IP address or full DNS name for incoming connections to your
|
||||
## relay. Leave commented out and Tor will guess.
|
||||
Address 1.1.1.1
|
||||
|
||||
## If you have multiple network interfaces, you can specify one for
|
||||
## outgoing traffic to use.
|
||||
## OutboundBindAddressExit will be used for all exit traffic, while
|
||||
## OutboundBindAddressOR will be used for all OR and Dir connections
|
||||
## (DNS connections ignore OutboundBindAddress).
|
||||
## If you do not wish to differentiate, use OutboundBindAddress to
|
||||
## specify the same address for both in a single line.
|
||||
#OutboundBindAddressExit 10.0.0.4
|
||||
#OutboundBindAddressOR 10.0.0.5
|
||||
|
||||
## A handle for your relay, so people don't have to refer to it by key.
|
||||
## Nicknames must be between 1 and 19 characters inclusive, and must
|
||||
## contain only the characters [a-zA-Z0-9].
|
||||
## If not set, "Unnamed" will be used.
|
||||
#Nickname ididnteditheconfig
|
||||
|
||||
## Define these to limit how much relayed traffic you will allow. Your
|
||||
## own traffic is still unthrottled. Note that RelayBandwidthRate must
|
||||
## be at least 75 kilobytes per second.
|
||||
## Note that units for these config options are bytes (per second), not
|
||||
## bits (per second), and that prefixes are binary prefixes, i.e. 2^10,
|
||||
## 2^20, etc.
|
||||
#RelayBandwidthRate 100 KBytes # Throttle traffic to 100KB/s (800Kbps)
|
||||
#RelayBandwidthBurst 200 KBytes # But allow bursts up to 200KB (1600Kb)
|
||||
|
||||
## Use these to restrict the maximum traffic per day, week, or month.
|
||||
## Note that this threshold applies separately to sent and received bytes,
|
||||
## not to their sum: setting "40 GB" may allow up to 80 GB total before
|
||||
## hibernating.
|
||||
##
|
||||
## Set a maximum of 40 gigabytes each way per period.
|
||||
#AccountingMax 40 GBytes
|
||||
## Each period starts daily at midnight (AccountingMax is per day)
|
||||
#AccountingStart day 00:00
|
||||
## Each period starts on the 3rd of the month at 15:00 (AccountingMax
|
||||
## is per month)
|
||||
#AccountingStart month 3 15:00
|
||||
|
||||
## Administrative contact information for this relay or bridge. This line
|
||||
## can be used to contact you if your relay or bridge is misconfigured or
|
||||
## something else goes wrong. Note that we archive and publish all
|
||||
## descriptors containing these lines and that Google indexes them, so
|
||||
## spammers might also collect them. You may want to obscure the fact that
|
||||
## it's an email address and/or generate a new address for this purpose.
|
||||
##
|
||||
## If you are running multiple relays, you MUST set this option.
|
||||
##
|
||||
#ContactInfo Random Person <nobody AT example dot com>
|
||||
## You might also include your PGP or GPG fingerprint if you have one:
|
||||
#ContactInfo 0xFFFFFFFF Random Person <nobody AT example dot com>
|
||||
|
||||
## Uncomment this to mirror directory information for others. Please do
|
||||
## if you have enough bandwidth.
|
||||
#DirPort 9030 # what port to advertise for directory connections
|
||||
## If you want to listen on a port other than the one advertised in
|
||||
## DirPort (e.g. to advertise 80 but bind to 9091), you can do it as
|
||||
## follows. below too. You'll need to do ipchains or other port
|
||||
## forwarding yourself to make this work.
|
||||
#DirPort 80 NoListen
|
||||
#DirPort 127.0.0.1:9091 NoAdvertise
|
||||
## Uncomment to return an arbitrary blob of html on your DirPort. Now you
|
||||
## can explain what Tor is if anybody wonders why your IP address is
|
||||
## contacting them. See contrib/tor-exit-notice.html in Tor's source
|
||||
## distribution for a sample.
|
||||
#DirPortFrontPage /usr/local/etc/tor/tor-exit-notice.html
|
||||
|
||||
## Uncomment this if you run more than one Tor relay, and add the identity
|
||||
## key fingerprint of each Tor relay you control, even if they're on
|
||||
## different networks. You declare it here so Tor clients can avoid
|
||||
## using more than one of your relays in a single circuit. See
|
||||
## https://www.torproject.org/docs/faq#MultipleRelays
|
||||
## However, you should never include a bridge's fingerprint here, as it would
|
||||
## break its concealability and potentially reveal its IP/TCP address.
|
||||
##
|
||||
## If you are running multiple relays, you MUST set this option.
|
||||
##
|
||||
## Note: do not use MyFamily on bridge relays.
|
||||
#MyFamily $keyid,$keyid,...
|
||||
|
||||
## Uncomment this if you want your relay to be an exit, with the default
|
||||
## exit policy (or whatever exit policy you set below).
|
||||
## (If ReducedExitPolicy, ExitPolicy, or IPv6Exit are set, relays are exits.
|
||||
## If none of these options are set, relays are non-exits.)
|
||||
#ExitRelay 1
|
||||
|
||||
## Uncomment this if you want your relay to allow IPv6 exit traffic.
|
||||
## (Relays do not allow any exit traffic by default.)
|
||||
#IPv6Exit 1
|
||||
|
||||
## Uncomment this if you want your relay to be an exit, with a reduced set
|
||||
## of exit ports.
|
||||
#ReducedExitPolicy 1
|
||||
|
||||
## Uncomment these lines if you want your relay to be an exit, with the
|
||||
## specified set of exit IPs and ports.
|
||||
##
|
||||
## A comma-separated list of exit policies. They're considered first
|
||||
## to last, and the first match wins.
|
||||
##
|
||||
## If you want to allow the same ports on IPv4 and IPv6, write your rules
|
||||
## using accept/reject *. If you want to allow different ports on IPv4 and
|
||||
## IPv6, write your IPv6 rules using accept6/reject6 *6, and your IPv4 rules
|
||||
## using accept/reject *4.
|
||||
##
|
||||
## If you want to _replace_ the default exit policy, end this with either a
|
||||
## reject *:* or an accept *:*. Otherwise, you're _augmenting_ (prepending to)
|
||||
## the default exit policy. Leave commented to just use the default, which is
|
||||
## described in the man page or at
|
||||
## https://www.torproject.org/documentation.html
|
||||
##
|
||||
## Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses
|
||||
## for issues you might encounter if you use the default exit policy.
|
||||
##
|
||||
## If certain IPs and ports are blocked externally, e.g. by your firewall,
|
||||
## you should update your exit policy to reflect this -- otherwise Tor
|
||||
## users will be told that those destinations are down.
|
||||
##
|
||||
## For security, by default Tor rejects connections to private (local)
|
||||
## networks, including to the configured primary public IPv4 and IPv6 addresses,
|
||||
## and any public IPv4 and IPv6 addresses on any interface on the relay.
|
||||
## See the man page entry for ExitPolicyRejectPrivate if you want to allow
|
||||
## "exit enclaving".
|
||||
##
|
||||
#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports on IPv4 and IPv6 but no more
|
||||
#ExitPolicy accept *:119 # accept nntp ports on IPv4 and IPv6 as well as default exit policy
|
||||
#ExitPolicy accept *4:119 # accept nntp ports on IPv4 only as well as default exit policy
|
||||
#ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy
|
||||
#ExitPolicy reject *:* # no exits allowed
|
||||
|
||||
## Bridge relays (or "bridges") are Tor relays that aren't listed in the
|
||||
## main directory. Since there is no complete public list of them, even an
|
||||
## ISP that filters connections to all the known Tor relays probably
|
||||
## won't be able to block all the bridges. Also, websites won't treat you
|
||||
## differently because they won't know you're running Tor. If you can
|
||||
## be a real relay, please do; but if not, be a bridge!
|
||||
##
|
||||
## Warning: when running your Tor as a bridge, make sure than MyFamily is
|
||||
## NOT configured.
|
||||
#BridgeRelay 1
|
||||
## By default, Tor will advertise your bridge to users through various
|
||||
## mechanisms like https://bridges.torproject.org/. If you want to run
|
||||
## a private bridge, for example because you'll give out your bridge
|
||||
## address manually to your friends, uncomment this line:
|
||||
#PublishServerDescriptor 0
|
||||
|
||||
## Configuration options can be imported from files or folders using the %include
|
||||
## option with the value being a path. If the path is a file, the options from the
|
||||
## file will be parsed as if they were written where the %include option is. If
|
||||
## the path is a folder, all files on that folder will be parsed following lexical
|
||||
## order. Files starting with a dot are ignored. Files on subfolders are ignored.
|
||||
## The %include option can be used recursively.
|
||||
#%include /etc/torrc.d/
|
||||
#%include /etc/torrc.custom
|
||||
|
||||
#HTTPTunnelPort 8118
|
||||
161
cashu/wallet/cli.py
Executable file → Normal file
161
cashu/wallet/cli.py
Executable file → Normal file
@@ -19,7 +19,18 @@ from loguru import logger
|
||||
from cashu.core.base import Proof
|
||||
from cashu.core.helpers import sum_proofs
|
||||
from cashu.core.migrations import migrate_databases
|
||||
from cashu.core.settings import CASHU_DIR, DEBUG, ENV_FILE, LIGHTNING, MINT_URL, VERSION
|
||||
from cashu.core.settings import (
|
||||
CASHU_DIR,
|
||||
DEBUG,
|
||||
ENV_FILE,
|
||||
LIGHTNING,
|
||||
MINT_URL,
|
||||
SOCKS_HOST,
|
||||
SOCKS_PORT,
|
||||
TOR,
|
||||
VERSION,
|
||||
)
|
||||
from cashu.tor.tor import TorProxy
|
||||
from cashu.wallet import migrations
|
||||
from cashu.wallet.crud import (
|
||||
get_lightning_invoices,
|
||||
@@ -60,6 +71,18 @@ def cli(ctx, host: str, walletname: str):
|
||||
ctx.obj["HOST"] = host
|
||||
ctx.obj["WALLET_NAME"] = walletname
|
||||
wallet = Wallet(ctx.obj["HOST"], os.path.join(CASHU_DIR, walletname))
|
||||
|
||||
if TOR and not TorProxy().check_platform():
|
||||
error_str = "Your settings say TOR=true but the built-in Tor bundle is not supported on your system. Please install Tor manually and set TOR=false and SOCKS_HOST=localhost and SOCKS_PORT=9050 in your Cashu config (recommended) or turn off Tor by setting TOR=false (not recommended). Cashu will not work until you edit your config file accordingly."
|
||||
error_str += "\n\n"
|
||||
if ENV_FILE:
|
||||
error_str += f"Edit your Cashu config file here: {ENV_FILE}"
|
||||
else:
|
||||
error_str += (
|
||||
f"Ceate a new Cashu config file here: {os.path.join(CASHU_DIR, '.env')}"
|
||||
)
|
||||
raise Exception(error_str)
|
||||
|
||||
ctx.obj["WALLET"] = wallet
|
||||
asyncio.run(init_wallet(wallet))
|
||||
pass
|
||||
@@ -74,6 +97,35 @@ def coro(f):
|
||||
return wrapper
|
||||
|
||||
|
||||
@cli.command("pay", help="Pay Lightning invoice.")
|
||||
@click.argument("invoice", type=str)
|
||||
@click.option(
|
||||
"--yes", "-y", default=False, is_flag=True, help="Skip confirmation.", type=bool
|
||||
)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def pay(ctx, invoice: str, yes: bool):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
await wallet.load_mint()
|
||||
wallet.status()
|
||||
amount, fees = await wallet.get_pay_amount_with_fees(invoice)
|
||||
if not yes:
|
||||
click.confirm(
|
||||
f"Pay {amount - fees} sat ({amount} sat incl. fees)?",
|
||||
abort=True,
|
||||
default=True,
|
||||
)
|
||||
|
||||
print(f"Paying Lightning invoice ...")
|
||||
assert amount > 0, "amount is not positive"
|
||||
if wallet.available_balance < amount:
|
||||
print("Error: Balance too low.")
|
||||
return
|
||||
_, send_proofs = await wallet.split_to_send(wallet.proofs, amount)
|
||||
await wallet.pay_lightning(send_proofs, invoice)
|
||||
wallet.status()
|
||||
|
||||
|
||||
@cli.command("invoice", help="Create Lighting invoice.")
|
||||
@click.argument("amount", type=int)
|
||||
@click.option("--hash", default="", help="Hash of the paid invoice.", type=str)
|
||||
@@ -120,39 +172,18 @@ async def invoice(ctx, amount: int, hash: str):
|
||||
return
|
||||
|
||||
|
||||
@cli.command("pay", help="Pay Lightning invoice.")
|
||||
@click.argument("invoice", type=str)
|
||||
@cli.command("balance", help="Balance.")
|
||||
@click.option(
|
||||
"--yes", "-y", default=False, is_flag=True, help="Skip confirmation.", type=bool
|
||||
"--verbose",
|
||||
"-v",
|
||||
default=False,
|
||||
is_flag=True,
|
||||
help="Show pending tokens as well.",
|
||||
type=bool,
|
||||
)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def pay(ctx, invoice: str, yes: bool):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
await wallet.load_mint()
|
||||
wallet.status()
|
||||
amount, fees = await wallet.get_pay_amount_with_fees(invoice)
|
||||
if not yes:
|
||||
click.confirm(
|
||||
f"Pay {amount - fees} sat ({amount} sat incl. fees)?",
|
||||
abort=True,
|
||||
default=True,
|
||||
)
|
||||
|
||||
print(f"Paying Lightning invoice ...")
|
||||
assert amount > 0, "amount is not positive"
|
||||
if wallet.available_balance < amount:
|
||||
print("Error: Balance too low.")
|
||||
return
|
||||
_, send_proofs = await wallet.split_to_send(wallet.proofs, amount)
|
||||
await wallet.pay_lightning(send_proofs, invoice)
|
||||
wallet.status()
|
||||
|
||||
|
||||
@cli.command("balance", help="Balance.")
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def balance(ctx):
|
||||
async def balance(ctx, verbose):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
keyset_balances = wallet.balance_per_keyset()
|
||||
if len(keyset_balances) > 1:
|
||||
@@ -163,14 +194,17 @@ async def balance(ctx):
|
||||
f"Keyset: {k or 'undefined'} Balance: {v['balance']} sat (available: {v['available']} sat)"
|
||||
)
|
||||
print("")
|
||||
print(
|
||||
f"Balance: {wallet.balance} sat (available: {wallet.available_balance} sat in {len([p for p in wallet.proofs if not p.reserved])} tokens)"
|
||||
)
|
||||
if verbose:
|
||||
print(
|
||||
f"Balance: {wallet.balance} sat (available: {wallet.available_balance} sat in {len([p for p in wallet.proofs if not p.reserved])} tokens)"
|
||||
)
|
||||
else:
|
||||
print(f"Balance: {wallet.available_balance} sat")
|
||||
|
||||
|
||||
@cli.command("send", help="Send coins.")
|
||||
@cli.command("send", help="Send tokens.")
|
||||
@click.argument("amount", type=int)
|
||||
@click.option("--lock", "-l", default=None, help="Lock coins (P2SH).", type=str)
|
||||
@click.option("--lock", "-l", default=None, help="Lock tokens (P2SH).", type=str)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def send(ctx, amount: int, lock: str):
|
||||
@@ -186,19 +220,19 @@ async def send(ctx, amount: int, lock: str):
|
||||
_, send_proofs = await wallet.split_to_send(
|
||||
wallet.proofs, amount, lock, set_reserved=True
|
||||
)
|
||||
coin = await wallet.serialize_proofs(
|
||||
token = await wallet.serialize_proofs(
|
||||
send_proofs, hide_secrets=True if lock and not p2sh else False
|
||||
)
|
||||
print(coin)
|
||||
print(token)
|
||||
wallet.status()
|
||||
|
||||
|
||||
@cli.command("receive", help="Receive coins.")
|
||||
@click.argument("coin", type=str)
|
||||
@click.option("--lock", "-l", default=None, help="Unlock coins.", type=str)
|
||||
@cli.command("receive", help="Receive tokens.")
|
||||
@click.argument("token", type=str)
|
||||
@click.option("--lock", "-l", default=None, help="Unlock tokens.", type=str)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def receive(ctx, coin: str, lock: str):
|
||||
async def receive(ctx, token: str, lock: str):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
await wallet.load_mint()
|
||||
wallet.status()
|
||||
@@ -215,25 +249,25 @@ async def receive(ctx, coin: str, lock: str):
|
||||
signature = p2shscripts[0].signature
|
||||
else:
|
||||
script, signature = None, None
|
||||
proofs = [Proof(**p) for p in json.loads(base64.urlsafe_b64decode(coin))]
|
||||
proofs = [Proof(**p) for p in json.loads(base64.urlsafe_b64decode(token))]
|
||||
_, _ = await wallet.redeem(proofs, scnd_script=script, scnd_siganture=signature)
|
||||
wallet.status()
|
||||
|
||||
|
||||
@cli.command("burn", help="Burn spent coins.")
|
||||
@click.argument("coin", required=False, type=str)
|
||||
@click.option("--all", "-a", default=False, is_flag=True, help="Burn all spent coins.")
|
||||
@cli.command("burn", help="Burn spent tokens.")
|
||||
@click.argument("token", required=False, type=str)
|
||||
@click.option("--all", "-a", default=False, is_flag=True, help="Burn all spent tokens.")
|
||||
@click.option(
|
||||
"--force", "-f", default=False, is_flag=True, help="Force check on all coins."
|
||||
"--force", "-f", default=False, is_flag=True, help="Force check on all tokens."
|
||||
)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def burn(ctx, coin: str, all: bool, force: bool):
|
||||
async def burn(ctx, token: str, all: bool, force: bool):
|
||||
wallet: Wallet = ctx.obj["WALLET"]
|
||||
await wallet.load_mint()
|
||||
if not (all or coin or force) or (coin and all):
|
||||
if not (all or token or force) or (token and all):
|
||||
print(
|
||||
"Error: enter a coin or use --all to burn all pending coins or --force to check all coins."
|
||||
"Error: enter a token or use --all to burn all pending tokens or --force to check all tokens."
|
||||
)
|
||||
return
|
||||
if all:
|
||||
@@ -244,12 +278,14 @@ async def burn(ctx, coin: str, all: bool, force: bool):
|
||||
proofs = wallet.proofs
|
||||
else:
|
||||
# check only the specified ones
|
||||
proofs = [Proof(**p) for p in json.loads(base64.urlsafe_b64decode(coin))]
|
||||
proofs = [Proof(**p) for p in json.loads(base64.urlsafe_b64decode(token))]
|
||||
wallet.status()
|
||||
await wallet.invalidate(proofs)
|
||||
wallet.status()
|
||||
|
||||
@cli.command("pending", help="Show pending coins.")
|
||||
|
||||
|
||||
@cli.command("pending", help="Show pending tokens.")
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def pending(ctx):
|
||||
@@ -263,8 +299,8 @@ async def pending(ctx):
|
||||
groupby(sorted_proofs, key=itemgetter("send_id"))
|
||||
):
|
||||
grouped_proofs = list(value)
|
||||
coin = await wallet.serialize_proofs(grouped_proofs)
|
||||
coin_hidden_secret = await wallet.serialize_proofs(
|
||||
token = await wallet.serialize_proofs(grouped_proofs)
|
||||
token_hidden_secret = await wallet.serialize_proofs(
|
||||
grouped_proofs, hide_secrets=True
|
||||
)
|
||||
reserved_date = datetime.utcfromtimestamp(
|
||||
@@ -273,7 +309,7 @@ async def pending(ctx):
|
||||
print(
|
||||
f"#{i} Amount: {sum_proofs(grouped_proofs)} sat Time: {reserved_date} ID: {key}\n"
|
||||
)
|
||||
print(f"With secret: {coin}\n\nSecretless: {coin_hidden_secret}\n")
|
||||
print(f"With secret: {token}\n\nSecretless: {token_hidden_secret}\n")
|
||||
print(f"--------------------------\n")
|
||||
wallet.status()
|
||||
|
||||
@@ -286,16 +322,16 @@ async def lock(ctx):
|
||||
p2shscript = await wallet.create_p2sh_lock()
|
||||
txin_p2sh_address = p2shscript.address
|
||||
print("---- Pay to script hash (P2SH) ----\n")
|
||||
print("Use a lock to receive coins that only you can unlock.")
|
||||
print("Use a lock to receive tokens that only you can unlock.")
|
||||
print("")
|
||||
print(f"Public receiving lock: P2SH:{txin_p2sh_address}")
|
||||
print("")
|
||||
print(
|
||||
f"Anyone can send coins to this lock:\n\ncashu send <amount> --lock P2SH:{txin_p2sh_address}"
|
||||
f"Anyone can send tokens to this lock:\n\ncashu send <amount> --lock P2SH:{txin_p2sh_address}"
|
||||
)
|
||||
print("")
|
||||
print(
|
||||
f"Only you can receive coins from this lock:\n\ncashu receive <coin> --lock P2SH:{txin_p2sh_address}\n"
|
||||
f"Only you can receive tokens from this lock:\n\ncashu receive <token> --lock P2SH:{txin_p2sh_address}\n"
|
||||
)
|
||||
|
||||
|
||||
@@ -313,7 +349,7 @@ async def locks(ctx):
|
||||
print(f"Script: {l.script}")
|
||||
print(f"Signature: {l.signature}")
|
||||
print("")
|
||||
print(f"Receive: cashu receive <coin> --lock P2SH:{l.address}")
|
||||
print(f"Receive: cashu receive <token> --lock P2SH:{l.address}")
|
||||
print("")
|
||||
print(f"--------------------------\n")
|
||||
else:
|
||||
@@ -386,10 +422,15 @@ async def wallets(ctx):
|
||||
@coro
|
||||
async def info(ctx):
|
||||
print(f"Version: {VERSION}")
|
||||
print(f"Debug: {DEBUG}")
|
||||
print(f"Wallet: {ctx.obj['WALLET_NAME']}")
|
||||
if DEBUG:
|
||||
print(f"Debug: {DEBUG}")
|
||||
print(f"Cashu dir: {CASHU_DIR}")
|
||||
if ENV_FILE:
|
||||
print(f"Settings: {ENV_FILE}")
|
||||
print(f"Wallet: {ctx.obj['WALLET_NAME']}")
|
||||
if TOR:
|
||||
print(f"Tor enabled: {TOR}")
|
||||
if SOCKS_HOST:
|
||||
print(f"Socks proxy: {SOCKS_HOST}:{SOCKS_PORT}")
|
||||
print(f"Mint URL: {MINT_URL}")
|
||||
return
|
||||
|
||||
@@ -107,8 +107,8 @@ async def m005_wallet_keysets(db: Database):
|
||||
await db.execute(
|
||||
f"""
|
||||
CREATE TABLE IF NOT EXISTS keysets (
|
||||
id TEXT NOT NULL,
|
||||
mint_url TEXT NOT NULL,
|
||||
id TEXT,
|
||||
mint_url TEXT,
|
||||
valid_from TIMESTAMP DEFAULT {db.timestamp_now},
|
||||
valid_to TIMESTAMP DEFAULT {db.timestamp_now},
|
||||
first_seen TIMESTAMP DEFAULT {db.timestamp_now},
|
||||
|
||||
@@ -35,8 +35,9 @@ from cashu.core.script import (
|
||||
step2_carol_sign_tx,
|
||||
)
|
||||
from cashu.core.secp import PublicKey
|
||||
from cashu.core.settings import DEBUG, VERSION
|
||||
from cashu.core.settings import DEBUG, SOCKS_HOST, SOCKS_PORT, TOR, VERSION
|
||||
from cashu.core.split import amount_split
|
||||
from cashu.tor.tor import TorProxy
|
||||
from cashu.wallet.crud import (
|
||||
get_keyset,
|
||||
get_proofs,
|
||||
@@ -54,11 +55,30 @@ from cashu.wallet.crud import (
|
||||
class LedgerAPI:
|
||||
keys: Dict[int, str]
|
||||
keyset: str
|
||||
tor: TorProxy
|
||||
|
||||
def __init__(self, url):
|
||||
self.url = url
|
||||
self.s = requests.Session()
|
||||
self.s.headers.update({"Client-version": VERSION})
|
||||
|
||||
def _set_requests(self):
|
||||
s = requests.Session()
|
||||
s.headers.update({"Client-version": VERSION})
|
||||
socks_host, socks_port = None, None
|
||||
if TOR and TorProxy().check_platform():
|
||||
self.tor = TorProxy(timeout=True)
|
||||
self.tor.run_daemon(verbose=True)
|
||||
socks_host, socks_port = "localhost", 9050
|
||||
else:
|
||||
socks_host, socks_port = SOCKS_HOST, SOCKS_PORT
|
||||
|
||||
if socks_host and socks_port:
|
||||
proxies = {
|
||||
"http": f"socks5://{socks_host}:{socks_port}",
|
||||
"https": f"socks5://{socks_host}:{socks_port}",
|
||||
}
|
||||
s.proxies.update(proxies)
|
||||
s.headers.update({"User-Agent": scrts.token_urlsafe(8)})
|
||||
return s
|
||||
|
||||
def _construct_proofs(
|
||||
self, promises: List[BlindedSignature], secrets: List[str], rs: List[str]
|
||||
@@ -154,6 +174,7 @@ class LedgerAPI:
|
||||
"""
|
||||
|
||||
async def _get_keys(self, url):
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.get(
|
||||
url + "/keys",
|
||||
)
|
||||
@@ -168,6 +189,7 @@ class LedgerAPI:
|
||||
return keyset
|
||||
|
||||
async def _get_keysets(self, url):
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.get(
|
||||
url + "/keysets",
|
||||
)
|
||||
@@ -178,6 +200,7 @@ class LedgerAPI:
|
||||
|
||||
def request_mint(self, amount):
|
||||
"""Requests a mint from the server and returns Lightning invoice."""
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.get(self.url + "/mint", params={"amount": amount})
|
||||
resp.raise_for_status()
|
||||
return_dict = resp.json()
|
||||
@@ -189,7 +212,7 @@ class LedgerAPI:
|
||||
secrets = [self._generate_secret() for s in range(len(amounts))]
|
||||
await self._check_used_secrets(secrets)
|
||||
payloads, rs = self._construct_outputs(amounts, secrets)
|
||||
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.post(
|
||||
self.url + "/mint",
|
||||
json=payloads.dict(),
|
||||
@@ -245,6 +268,7 @@ class LedgerAPI:
|
||||
"proofs": {i: proofs_include for i in range(len(proofs))},
|
||||
}
|
||||
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.post(
|
||||
self.url + "/split",
|
||||
json=split_payload.dict(include=_splitrequest_include_fields(proofs)),
|
||||
@@ -277,6 +301,7 @@ class LedgerAPI:
|
||||
"proofs": {i: {"secret"} for i in range(len(proofs))},
|
||||
}
|
||||
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.post(
|
||||
self.url + "/check",
|
||||
json=payload.dict(include=_check_spendable_include_fields(proofs)),
|
||||
@@ -289,6 +314,7 @@ class LedgerAPI:
|
||||
async def check_fees(self, payment_request: str):
|
||||
"""Checks whether the Lightning payment is internal."""
|
||||
payload = CheckFeesRequest(pr=payment_request)
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.post(
|
||||
self.url + "/checkfees",
|
||||
json=payload.dict(),
|
||||
@@ -313,6 +339,7 @@ class LedgerAPI:
|
||||
"proofs": {i: proofs_include for i in range(len(proofs))},
|
||||
}
|
||||
|
||||
self.s = self._set_requests()
|
||||
resp = self.s.post(
|
||||
self.url + "/melt",
|
||||
json=payload.dict(include=_meltequest_include_fields(proofs)),
|
||||
@@ -527,9 +554,10 @@ class Wallet(LedgerAPI):
|
||||
return sum_proofs([p for p in self.proofs if not p.reserved])
|
||||
|
||||
def status(self):
|
||||
print(
|
||||
f"Balance: {self.balance} sat (available: {self.available_balance} sat in {len([p for p in self.proofs if not p.reserved])} tokens)"
|
||||
)
|
||||
# print(
|
||||
# f"Balance: {self.balance} sat (available: {self.available_balance} sat in {len([p for p in self.proofs if not p.reserved])} tokens)"
|
||||
# )
|
||||
print(f"Balance: {self.available_balance} sat")
|
||||
|
||||
def balance_per_keyset(self):
|
||||
return {
|
||||
|
||||
91
docs/specs/wip/0 - Notation.md
Normal file
91
docs/specs/wip/0 - Notation.md
Normal file
@@ -0,0 +1,91 @@
|
||||
Sending user: `Alice`
|
||||
Receiving user: `Carol`
|
||||
Mint: `Bob`
|
||||
|
||||
## Bob (mint)
|
||||
- `k` private key of mint (one for each amount)
|
||||
- `K` public key of mint
|
||||
- `Q` promise (blinded signature)
|
||||
|
||||
## Alice (user)
|
||||
- `x` random string (secret message), corresponds to point `Y` on curve
|
||||
- `r` private key (blinding factor)
|
||||
- `T` blinded message
|
||||
- `Z` proof (unblinded signature)
|
||||
|
||||
# Blind Diffie-Hellmann key exchange (BDHKE)
|
||||
- Mint `Bob` publishes `K = kG`
|
||||
- `Alice` picks secret `x` and computes `Y = hash_to_curve(x)`
|
||||
- `Alice` sends to `Bob`: `T = Y + rG` with `r` being a random nonce
|
||||
- `Bob` sends back to `Alice` blinded key: `Q = kT` (these two steps are the DH key exchange)
|
||||
- `Alice` can calculate the unblinded key as `Q - rK = kY + krG - krG = kY = Z`
|
||||
- Alice can take the pair `(x, Z)` as a token and can send it to `Carol`.
|
||||
- `Carol` can send `(x, Z)` to `Bob` who then checks that `k*hash_to_curve(x) == Z`, and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets.
|
||||
|
||||
## 0.1 - Models
|
||||
|
||||
### `BlindedMessage`
|
||||
A encrypted ("blinded") secret and an amount sent from `Alice` to `Bob`.
|
||||
|
||||
```json
|
||||
{
|
||||
"amount": int,
|
||||
"B_": str
|
||||
}
|
||||
```
|
||||
|
||||
### `BlindedSignature`
|
||||
A signature on the `BlindedMessage` sent from `Bob` to `Alice`.
|
||||
|
||||
```json
|
||||
{
|
||||
"amount": int,
|
||||
"C_": str,
|
||||
"id": str | None
|
||||
}
|
||||
```
|
||||
|
||||
### `Proof`
|
||||
A `Proof` is also called a `Token` and has the following form:
|
||||
|
||||
```json
|
||||
{
|
||||
"amount": int,
|
||||
"secret": str,
|
||||
"C": str,
|
||||
"id": None | str,
|
||||
"script": P2SHScript | None,
|
||||
}
|
||||
```
|
||||
|
||||
### `Proofs`
|
||||
A list of `Proof`'s. In general, this will be used for most operations instead of a single `Proof`. `Proofs` can be serialized (see Methods/Serialization [TODO: Link Serialization])
|
||||
|
||||
## 0.2 - Methods
|
||||
|
||||
### Serialization of `Proofs`
|
||||
To send and receive `Proofs`, wallets serialize them in a `base64_urlsafe` format.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "DSAl9nvvyfva",
|
||||
"amount": 8,
|
||||
"secret": "DbRKIya0etdwI5sFAN0AXQ",
|
||||
"C": "02df7f2fc29631b71a1db11c163b0b1cb40444aa2b3d253d43b68d77a72ed2d625"
|
||||
},
|
||||
{
|
||||
"id": "DSAl9nvvyfva",
|
||||
"amount": 16,
|
||||
"secret": "d_PPc5KpuAB2M60WYAW5-Q",
|
||||
"C": "0270e0a37f7a0b21eab43af751dd3c03f61f04c626c0448f603f1d1f5ae5a7d7e6"
|
||||
}
|
||||
```
|
||||
|
||||
becomes
|
||||
|
||||
```
|
||||
W3siaWQiOiAiRFNBbDludnZ5ZnZhIiwgImFtb3VudCI6IDgsICJzZWNyZXQiOiAiRGJSS0l5YTBldGR3STVzRkFOMEFYUSIsICJDIjogIjAyZGY3ZjJmYzI5NjMxYjcxYTFkYjExYzE2M2IwYjFjYjQwNDQ0YWEyYjNkMjUzZDQzYjY4ZDc3YTcyZWQyZDYyNSJ9LCB7ImlkIjogIkRTQWw5bnZ2eWZ2YSIsICJhbW91bnQiOiAxNiwgInNlY3JldCI6ICJkX1BQYzVLcHVBQjJNNjBXWUFXNS1RIiwgIkMiOiAiMDI3MGUwYTM3ZjdhMGIyMWVhYjQzYWY3NTFkZDNjMDNmNjFmMDRjNjI2YzA0NDhmNjAzZjFkMWY1YWU1YTdkN2U2In1d
|
||||
```
|
||||
32
docs/specs/wip/1 - Request public keys from mint.md
Normal file
32
docs/specs/wip/1 - Request public keys from mint.md
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
`Alice` receives public keys from mint `Bob` via `GET /keys` and stores them in a key-value store like a dictionary.
|
||||
|
||||
`Bob` responds with his **active** keyset [TODO: Link #2]. Note that a mint can support multiple keysets at the same time but will only respond with the active keyset. See [TODO: Link #2] for how a wallet deals with multiple keysets.
|
||||
|
||||
Keysets are received as a JSON of the form `{<amount_1> : <mint_pubkey_1>, <amount_2> : ...}` for each `<amount_i>` of the amounts the mint `Bob` supports and the corresponding public key `<mint_pubkey_1>`, that is `K_i` (see #0).
|
||||
|
||||
## Example
|
||||
|
||||
Request of `Alice`:
|
||||
|
||||
```http
|
||||
GET https://mint.host:3338/keys
|
||||
```
|
||||
|
||||
With curl:
|
||||
|
||||
```bash
|
||||
curl -X GET https://mint.host:3338/keys
|
||||
```
|
||||
|
||||
Response of `Bob`:
|
||||
|
||||
```json
|
||||
{
|
||||
"1": "03a40f20667ed53513075dc51e715ff2046cad64eb68960632269ba7f0210e38bc",
|
||||
"2": "03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de",
|
||||
"4": "02648eccfa4c026960966276fa5a4cae46ce0fd432211a4f449bf84f13aa5f8303",
|
||||
"8": "02fdfd6796bfeac490cbee12f778f867f0a2c68f6508d17c649759ea0dc3547528",
|
||||
...
|
||||
}
|
||||
```
|
||||
76
docs/specs/wip/2 - Keysets and keyset IDs.md
Normal file
76
docs/specs/wip/2 - Keysets and keyset IDs.md
Normal file
@@ -0,0 +1,76 @@
|
||||
A keyset is a set of public keys that the mint `Bob` generates and shares with its users. It refers to the set of public keys that each correspond to the amount values that the mint supports (e.g. 1, 2, 4, 8, ...) respectively.
|
||||
|
||||
## Requesting mint keyset IDs
|
||||
|
||||
A mint can have multiple active keysets at the same time but **MUST** have only one active keyset. A wallet can ask the mint for all active keyset IDs via the `GET /keysets` endpoint. A wallet **CAN** request the list of active keysets from the mint upon startup and, if it does so, **MUST** choose only tokens from its database that have a keyset ID supported by the mint to interact with it.
|
||||
|
||||
This is useful in the case a wallet interacts with multiple mints. That way, a wallet always knows which tokens it can use with the mint it is currently interacting with.
|
||||
|
||||
## Example
|
||||
|
||||
Request of `Alice`:
|
||||
|
||||
```http
|
||||
GET https://mint.host:3338/keysets
|
||||
```
|
||||
|
||||
With curl:
|
||||
|
||||
```bash
|
||||
curl -X GET https://mint.host:3338/keysets
|
||||
```
|
||||
|
||||
Response of `Bob`:
|
||||
|
||||
```json
|
||||
{
|
||||
"keysets": [
|
||||
"DSAl9nvvyfva",
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 2.1 - Generating a keyset
|
||||
|
||||
A keyset is generated by the mint by a single seed `s` from which the private keys `k_i` are derived, with `i` being the index of the amount value. The derivation for the elements `k_i` goes like this:
|
||||
|
||||
```python
|
||||
for i in range(MAX_ORDER):
|
||||
k_i = HASH_SHA256(s + D + i)[:32]
|
||||
```
|
||||
|
||||
Here, `MAX_ORDER` refers to the order of the maximum token value that the mint supports, i.e., `2^MAX_ORDER`. Typically, `MAX_ORDER = 64`. `D` refers to a derivation path that is chosen by the mint to rotate keys. `i` is the string representation of the index of the amount value.
|
||||
|
||||
## 2.2 - Keyset ID
|
||||
|
||||
A keyset ID is an identifier for a specific keyset. It can be derived by anyone who knows the set of public keys of a mint. The keyset ID **CAN** be stored in a Cashu token [TODO: Link to definition of token] if it was generated by a mint corresponding to the keyset.
|
||||
|
||||
A wallet can use the keyset ID in a token to recognize a mint it was issued by. For example, a wallet might store the `MINT_URL` together with the `keyset_id` in its database the first time it receives a keyset from that mint. That way, a wallet can know which mint to contact when it receives a token with a `keyset_id` of a mint that it has interacted with before.
|
||||
|
||||
### 2.2.1 - Deriving the keyset ID
|
||||
|
||||
The mint and its users can derive a keyset ID from the keyset of the mint. To derive the keyset ID of a mint, execute the following steps:
|
||||
|
||||
```
|
||||
1 - sort keyset by amount
|
||||
2 - concatenate all (sorted) public keys to one string
|
||||
3 - HASH_SHA256 the concatenated public keys
|
||||
4 - take the first 12 characters of the hash
|
||||
```
|
||||
|
||||
An example implementation in Python:
|
||||
|
||||
```python
|
||||
def derive_keyset_id(keys: Dict[int, PublicKey]):
|
||||
"""Deterministic derivation keyset_id from set of public keys."""
|
||||
sorted_keys = dict(sorted(keys.items()))
|
||||
pubkeys_concat = "".join([p.serialize().hex() for _, p in sorted_keys.items()])
|
||||
return base64.b64encode(
|
||||
hashlib.sha256((pubkeys_concat).encode("utf-8")).digest()
|
||||
).decode()[:12]
|
||||
```
|
||||
|
||||
### 2.2.2 - Storing the keyset ID in a token
|
||||
|
||||
A mint **CAN** add the keyset ID to a `BlindedSignature` during the minting process [TODO: link to blinded signature. TODO: link to /mint]. If a wallet receives a `BlindedSignature` with a keyset ID,
|
||||
28
docs/specs/wip/3 - Request minting.md
Normal file
28
docs/specs/wip/3 - Request minting.md
Normal file
@@ -0,0 +1,28 @@
|
||||
Minting tokens is a two-step process: requesting a mint and minting the tokens. Here, we describe the first step. A wallet requests the minting of tokens in exchange for paying a bolt11 Lightning invoice (typically generated by the mint to add funds to its reserves, and typically paid with another Lightning wallet).
|
||||
|
||||
To request the minting of tokens, a wallet `Alice` sends a `GET /mint&amount=<amount_sat>` request with the requested amount `<amount_sat>` in satoshis. The mint `Bob` then responds with a Lightning invoice.
|
||||
|
||||
## Example
|
||||
|
||||
Request of `Alice`:
|
||||
|
||||
```http
|
||||
GET https://mint.host:3338/mint&amount=1000
|
||||
```
|
||||
|
||||
With curl:
|
||||
|
||||
```bash
|
||||
curl -X GET https://mint.host:3338/mint&amount=1000
|
||||
```
|
||||
|
||||
Response of `Bob`:
|
||||
|
||||
```json
|
||||
{
|
||||
"pr": "lnbc100n1p3kdrv5sp5lpdxzghe5j67q...",
|
||||
"hash": "67d1d9ea6ada225c115418671b64a..."
|
||||
}
|
||||
```
|
||||
|
||||
with `pr` being the bolt11 payment request and `hash` the hash of the invoice. A wallet **MUST** store the `hash` and `amount_sat` in its database to later request the tokens upon paying the invoice. A wallet **SHOULD** then present the payment request (for example via QR code) to the user such that they can pay the invoice with another Lightning wallet. After the user has paid the invoice, a wallet **MUST** continue with #4 - Minting tokens [TODO: Link to #4].
|
||||
92
docs/specs/wip/4 - Minting tokens.md
Normal file
92
docs/specs/wip/4 - Minting tokens.md
Normal file
@@ -0,0 +1,92 @@
|
||||
After requesting a mint (see #3 [TODO: Link]) and paying the invoice that was returned by the mint, a wallet proceeds with requesting tokens from the mint in return for paying the invoice.
|
||||
|
||||
For that, a wallet sends a `POST /mint&payment_hash=<hash>` request with a JSON body to the mint. The body **MUST** include `BlindedMessages` that are worth a maximum of `<amount_sat>` [TODO: Refer to BlindedMessages]. If successful (i.e. the invoice has been previously paid and the `BlindedMessages` are valid), the mint responds with `Promises` [TODO: Link Promises].
|
||||
|
||||
## Example
|
||||
|
||||
Request of `Alice`:
|
||||
|
||||
```http
|
||||
POST https://mint.host:3338/mint&payment_hash=67d1d9ea6ada225c115418671b64a
|
||||
```
|
||||
|
||||
With the data being of the form `MintRequest`:
|
||||
|
||||
```json
|
||||
{
|
||||
"blinded_messages":
|
||||
[
|
||||
BlindedMessage,
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
With curl:
|
||||
|
||||
```bash
|
||||
curl -X POST https://mint.host:3338/mint&payment_hash=67d1d9ea6ada225c115418671b64a -d \
|
||||
{
|
||||
"blinded_messages":
|
||||
[
|
||||
{
|
||||
"amount": 2,
|
||||
"B_": "02634a2c2b34bec9e8a4aba4361f6bf202d7fa2365379b0840afe249a7a9d71239"
|
||||
},
|
||||
{
|
||||
"amount": 8,
|
||||
"B_": "03b54ab451b15005f2c64d38fc512fca695914c8fd5094ee044e5724ad41fda247"
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Response of `Bob`:
|
||||
|
||||
If the invoice was successfully paid, `Bob` responds with a `PostMintResponse` which is essentially a list of `BlindedSignature`'s [TODO: Link PostMintResponse]
|
||||
|
||||
```json
|
||||
{
|
||||
"promises":
|
||||
[
|
||||
{
|
||||
"id": "DSAl9nvvyfva",
|
||||
"amount": 2,
|
||||
"C_": "03e61daa438fc7bcc53f6920ec6c8c357c24094fb04c1fc60e2606df4910b21ffb"
|
||||
},
|
||||
{
|
||||
"id": "DSAl9nvvyfva",
|
||||
"amount": 8,
|
||||
"C_": "03fd4ce5a16b65576145949e6f99f445f8249fee17c606b688b504a849cdc452de"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
If the invoice was not paid yet, `Bob` responds with an error. In that case, `Alice` **CAN** repeat the same response until the Lightning invoice is settled.
|
||||
|
||||
## Unblinding signatures
|
||||
|
||||
Upon receiving the `PostMintResponse` with the list of `BlindedSignature`'s from the mint `Bob`, a wallet `Alice` **MUST** then unblind the `BlindedSignature`'s from `Bob` (see #0 Notation [TODO: Link to unblinding]) to generate a list of `Proof`'s. A `Proof` is effectively an ecash `Token` and can later be used to redeem the token. The wallet **MUST** store the `Proof` in its database.
|
||||
|
||||
A list multiple `Proof`'s is called `Proofs` and has the form:
|
||||
|
||||
```json
|
||||
{
|
||||
"proofs" :
|
||||
[
|
||||
{
|
||||
"id": "DSAl9nvvyfva",
|
||||
"amount": 2,
|
||||
"secret": "S+tDfc1Lfsrb06zaRdVTed6Izg",
|
||||
"C": "0242b0fb43804d8ba9a64ceef249ad7a60f42c15fe6d4907238b05e857527832a3"
|
||||
},
|
||||
{
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
57
docs/specs/wip/5 - Melting tokens.md
Normal file
57
docs/specs/wip/5 - Melting tokens.md
Normal file
@@ -0,0 +1,57 @@
|
||||
Melting tokens is the opposite of minting them (see #4): the wallet `Alice` sends `Proofs` to the mint `Bob` together with a bolt11 Lightning invoice that `Alice` wants to be paid. To melt tokens, `Alice` sends a `POST /melt` request with a JSON body to the mint. The `Proofs` included in the request will be burned by the mint and the mint will pay the invoice in exchange.
|
||||
|
||||
`Alice`'s request **MUST** include a `MeltRequest` ([TODO: Link MeltRequest]) JSON body with `Proofs` that have at least the amount of the invoice to be paid.
|
||||
|
||||
## Example
|
||||
|
||||
**Request** of `Alice`:
|
||||
|
||||
```http
|
||||
POST https://mint.host:3338/melt
|
||||
```
|
||||
|
||||
With the data being of the form `MeltRequest`:
|
||||
|
||||
```json
|
||||
{
|
||||
"proofs":
|
||||
[
|
||||
Proof,
|
||||
...
|
||||
],
|
||||
"invoice": str
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
With curl:
|
||||
|
||||
```bash
|
||||
curl -X POST https://mint.host:3338/mint&payment_hash=67d1d9ea6ada225c115418671b64a -d \
|
||||
{
|
||||
"proofs" :
|
||||
[
|
||||
{
|
||||
"id": "DSAl9nvvyfva",
|
||||
"amount": 2,
|
||||
"secret": "S+tDfc1Lfsrb06zaRdVTed6Izg",
|
||||
"C": "0242b0fb43804d8ba9a64ceef249ad7a60f42c15fe6d4907238b05e857527832a3"
|
||||
},
|
||||
{
|
||||
...
|
||||
}
|
||||
],
|
||||
"invoice": "lnbc100n1p3kdrv5sp5lpdxzghe5j67q..."
|
||||
}
|
||||
```
|
||||
|
||||
**Response** `PostMeltResponse` from `Bob`:
|
||||
|
||||
```json
|
||||
{
|
||||
"paid": true,
|
||||
"preimage": "da225c115418671b64a67d1d9ea6a..."
|
||||
}
|
||||
```
|
||||
|
||||
Only if the `paid==true`, the wallet `Alice` **MUST** delete the `Proofs` from her database (or move them to a history). If `paid==false`, `Alice` **CAN** repeat the same multiple times until the payment is successful.
|
||||
66
docs/specs/wip/6 - Split.md
Normal file
66
docs/specs/wip/6 - Split.md
Normal file
@@ -0,0 +1,66 @@
|
||||
The split operation is the most important component of the Cashu system. The wallet `Alice` can use it to redeem tokens (i.e. receive new ones in return) that she received from `Carol`, or she can split her own tokens to a target amount she needs to send to `Carol`, if she does not have the necessary amounts to compose the target amount in her wallet already.
|
||||
|
||||
The basic idea is that `Alice` sends `Bob` a set of `Proof`'s and a set of `BlindedMessage`'s with an equal amount. Additionally, she specifies the `amount` at which she would like to have the split.
|
||||
|
||||
## 6.1 - Split to send
|
||||
|
||||
To make this more clear, we make an example of a typical case of sending tokens from `Alice` to `Carol`:
|
||||
|
||||
`Alice` has 64 satoshis in her wallet, composed of two tokens, one worth 32 sats and another two worth 16 sats. She wants to send `Carol` 40 sats but does not have the necessary tokens to combine them to reach the exact target amount of 40 sats. `Alice` requests a split from the mint. For that, she sends the mint `Bob` her tokens (`Proofs`) worth `[32, 16, 16]` and asks for a split at amount 40. The mint will then return her new tokens with the amounts `[32, 8, 16, 8]`. Notice that the first two tokens can now be combined to 40 sats. The original tokens that `Alice` sent to `Bob` are now invalidated.
|
||||
|
||||
## 6.2 - Split to receive
|
||||
|
||||
Another case of how split can be useful becomes apparent if we follow up the example above where `Alice` split her tokens ready to be sent to `Carol`. `Carol` can receive these tokens, which means to invalidate the tokens she receives and redeem them for new ones, using the same mechanism. Only if `Carol` redeems them for new tokens that only she can spend, `Alice` can't double-spend them anymore and this simple transaction can be considered settled. `Carol` requests a split of the tokens (`Proofs`) worth `[32, 8]` at the amount `40` (the total amount) to receive back new tokens with the same total amount.
|
||||
|
||||
## Example
|
||||
|
||||
**Request** of `Alice`:
|
||||
|
||||
```http
|
||||
POST https://mint.host:3338/split
|
||||
```
|
||||
|
||||
With the data being of the form `SplitRequest`:
|
||||
|
||||
```json
|
||||
{
|
||||
"proofs": Proofs,
|
||||
"outputs": MintRequest,
|
||||
"amount": int
|
||||
}
|
||||
```
|
||||
|
||||
With curl:
|
||||
|
||||
```bash
|
||||
curl -X POST https://mint.host:3338/split -d \
|
||||
{
|
||||
"proofs":
|
||||
[
|
||||
{
|
||||
"id": "DSAl9nvvyfva",
|
||||
"amount": 2,
|
||||
"secret": "S+tDfc1Lfsrb06zaRdVTed6Izg",
|
||||
"C": "0242b0fb43804d8ba9a64ceef249ad7a60f42c15fe6d4907238b05e857527832a3"
|
||||
},
|
||||
{
|
||||
...
|
||||
}
|
||||
],
|
||||
"outputs":{
|
||||
"blinded_messages":
|
||||
[
|
||||
{
|
||||
"amount": 2,
|
||||
"B_": "02634a2c2b34bec9e8a4aba4361f6bf202d7fa2365379b0840afe249a7a9d71239"
|
||||
},
|
||||
{
|
||||
...
|
||||
}
|
||||
]
|
||||
},
|
||||
"amount": 40
|
||||
}
|
||||
```
|
||||
|
||||
If successful, `Bob` will respond
|
||||
15
poetry.lock
generated
15
poetry.lock
generated
@@ -397,6 +397,14 @@ python-versions = ">=3.6.8"
|
||||
[package.extras]
|
||||
diagrams = ["jinja2", "railroad-diagrams"]
|
||||
|
||||
[[package]]
|
||||
name = "PySocks"
|
||||
version = "1.7.1"
|
||||
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.1.3"
|
||||
@@ -658,7 +666,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "6ba135e91c4ec10d7f55569ff65531717da6d7070d41355173f06f2cdfc1797f"
|
||||
content-hash = "aa0c3cf3a023b4143939128be203cf0c519341abc7cd7ef0b200694f8b925b78"
|
||||
|
||||
[metadata.files]
|
||||
anyio = [
|
||||
@@ -974,6 +982,11 @@ pyparsing = [
|
||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||
]
|
||||
PySocks = [
|
||||
{file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
|
||||
{file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
|
||||
{file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
|
||||
{file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "cashu"
|
||||
version = "0.4.2"
|
||||
version = "0.5.2"
|
||||
description = "Ecash wallet and mint."
|
||||
authors = ["calle <callebtc@protonmail.com>"]
|
||||
license = "MIT"
|
||||
@@ -23,6 +23,7 @@ secp256k1 = "^0.14.0"
|
||||
sqlalchemy-aio = "^0.17.0"
|
||||
python-bitcoinlib = "^0.11.2"
|
||||
h11 = "0.12.0"
|
||||
PySocks = "^1.7.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = {version = "^22.8.0", allow-prereleases = true}
|
||||
|
||||
@@ -23,6 +23,7 @@ py==1.11.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
pycparser==2.21 ; python_version >= "3.7" and python_version < "4.0"
|
||||
pydantic==1.10.2 ; python_version >= "3.7" and python_version < "4.0"
|
||||
pyparsing==3.0.9 ; python_version >= "3.7" and python_version < "4.0"
|
||||
pysocks==1.7.1 ; python_version >= "3.7" and python_version < "4.0"
|
||||
pytest-asyncio==0.19.0 ; python_version >= "3.7" and python_version < "4.0"
|
||||
pytest==7.1.3 ; python_version >= "3.7" and python_version < "4.0"
|
||||
python-bitcoinlib==0.11.2 ; python_version >= "3.7" and python_version < "4.0"
|
||||
|
||||
2
setup.py
2
setup.py
@@ -13,7 +13,7 @@ entry_points = {"console_scripts": ["cashu = cashu.wallet.cli:cli"]}
|
||||
|
||||
setuptools.setup(
|
||||
name="cashu",
|
||||
version="0.4.2",
|
||||
version="0.5.2",
|
||||
description="Ecash wallet and mint with Bitcoin Lightning support",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
@@ -42,14 +42,19 @@ def test_hash_to_curve_iteration():
|
||||
def test_step1():
|
||||
""""""
|
||||
B_, blinding_factor = step1_alice(
|
||||
"test_message", blinding_factor=b"00000000000000000000000000000001" # 32 bytes
|
||||
"test_message",
|
||||
blinding_factor=bytes.fromhex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
), # 32 bytes
|
||||
)
|
||||
|
||||
assert (
|
||||
B_.serialize().hex()
|
||||
== "0243379106c73dfc635cd1422f406e83fbfa25be83bb3620aefc08f2b89d02d777"
|
||||
== "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2"
|
||||
)
|
||||
assert blinding_factor.private_key == bytes.fromhex(
|
||||
"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
)
|
||||
assert blinding_factor.private_key == b"00000000000000000000000000000001"
|
||||
|
||||
|
||||
def test_step2():
|
||||
@@ -76,7 +81,7 @@ def test_step3():
|
||||
# C = C_ - A.mult(r)
|
||||
C_ = PublicKey(
|
||||
bytes.fromhex(
|
||||
"02b15f14ae9259c101cdbc437e8877b1ca5d4af3a0c0684866b38d8c8d0b6f6374"
|
||||
"02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2"
|
||||
),
|
||||
raw=True,
|
||||
)
|
||||
@@ -97,5 +102,5 @@ def test_step3():
|
||||
|
||||
assert (
|
||||
C.serialize().hex()
|
||||
== "03398f7153b381ce54d57962a5e03ce0a4f3b79755e882c972b788e8488e59b0c9"
|
||||
== "03c724d7e6a5443b39ac8acf11f40420adc4f99a02e7cc1b57703d9391f6d129cd"
|
||||
)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import time
|
||||
from typing import List
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
|
||||
from cashu.core.base import BlindedMessage, Proof
|
||||
from cashu.core.helpers import async_unwrap, sum_proofs
|
||||
from cashu.core.migrations import migrate_databases
|
||||
|
||||
SERVER_ENDPOINT = "http://localhost:3338"
|
||||
@@ -13,8 +11,7 @@ SERVER_ENDPOINT = "http://localhost:3338"
|
||||
import os
|
||||
|
||||
from cashu.core.db import Database
|
||||
from cashu.core.settings import MAX_ORDER, MINT_PRIVATE_KEY
|
||||
from cashu.lightning.lnbits import LNbitsWallet
|
||||
from cashu.core.settings import MAX_ORDER
|
||||
from cashu.mint import migrations
|
||||
from cashu.mint.ledger import Ledger
|
||||
|
||||
|
||||
22
tests/test_tor.py
Normal file
22
tests/test_tor.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from cashu.tor.tor import TorProxy
|
||||
|
||||
|
||||
# @pytest.mark.skip
|
||||
def test_tor_setup():
|
||||
s = requests.Session()
|
||||
|
||||
tor = TorProxy(timeout=False)
|
||||
tor.run_daemon()
|
||||
socks_host, socks_port = "localhost", 9050
|
||||
|
||||
proxies = {
|
||||
"http": f"socks5://{socks_host}:{socks_port}",
|
||||
"https": f"socks5://{socks_host}:{socks_port}",
|
||||
}
|
||||
s.proxies.update(proxies)
|
||||
|
||||
resp = s.get("https://google.com")
|
||||
resp.raise_for_status()
|
||||
Reference in New Issue
Block a user