forked from bitcoinafterlife/bal-electrum-plugin
Compare commits
1 Commits
a5f6b9f925
...
fix/balbui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7124996e1e |
445
README.md
445
README.md
@@ -1,443 +1,2 @@
|
|||||||
# Bal Electrum Plugin
|
# BalPlugin
|
||||||
|
Bitcoin After Life Electrum Plugin
|
||||||
⚡ **Bitcoin Electrum plugin for managing heir inheritance and locktime-based transactions**
|
|
||||||
|
|
||||||
This plugin extends Electrum wallet to support:
|
|
||||||
- **Heir inheritance management** - Define beneficiaries and inheritance shares
|
|
||||||
- **Locktime transactions** - Create time-locked transactions for future spending
|
|
||||||
- **Multi-signature setups** - Configure complex multisig scenarios with heirs
|
|
||||||
- **User-friendly wizards** - Easy setup interface
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📥 Installation
|
|
||||||
|
|
||||||
### Method 1: Install from Release (Recommended)
|
|
||||||
|
|
||||||
1. **Download the plugin**
|
|
||||||
- Go to: [https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/releases](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/releases)
|
|
||||||
- Download the latest `bal-electrum-plugin-vX.X.X.zip` file
|
|
||||||
|
|
||||||
2. **Install in Electrum**
|
|
||||||
- Open Electrum Bitcoin wallet
|
|
||||||
- Go to **Tools → Plugins**
|
|
||||||
- Click **Add**
|
|
||||||
- Select the downloaded `.zip` file
|
|
||||||
- Click **Open** or **Install**
|
|
||||||
- Restart Electrum if required
|
|
||||||
|
|
||||||
### Method 2: Install from Source
|
|
||||||
|
|
||||||
1. **Download the source code**
|
|
||||||
```bash
|
|
||||||
git clone https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin.git
|
|
||||||
cd bal-electrum-plugin
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Create a zip file**
|
|
||||||
```bash
|
|
||||||
zip -r bal-electrum-plugin.zip bal_electrum_plugin/
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Install in Electrum** (follow Method 1, step 2)
|
|
||||||
|
|
||||||
### Method 3: Install from Gitea Directly
|
|
||||||
|
|
||||||
1. **Download the plugin archive**
|
|
||||||
```bash
|
|
||||||
wget https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/archive/main.zip -O bal-electrum-plugin.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Install in Electrum** (follow Method 1, step 2)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
### 1. Enable the Plugin
|
|
||||||
- Open Electrum
|
|
||||||
- Go to **Tools → Plugins**
|
|
||||||
- Find **Bal Electrum Plugin** in the list
|
|
||||||
- Click the checkbox to enable it
|
|
||||||
- Click **Apply** and restart Electrum if prompted
|
|
||||||
|
|
||||||
### 2. Configure Your Heirs
|
|
||||||
- Go to **Tools → Bal Electrum Plugin**
|
|
||||||
- Click **Setup Heirs**
|
|
||||||
- Add beneficiaries with their Bitcoin addresses and inheritance percentages
|
|
||||||
- Set locktime conditions for each heir
|
|
||||||
|
|
||||||
### 3. Create a Locktime Transaction
|
|
||||||
- Go to **Send** tab
|
|
||||||
- Enter recipients and amount
|
|
||||||
- Enable **Locktime** option
|
|
||||||
- Set when the transaction can be spent (block height, timestamp, or relative time)
|
|
||||||
- Review and broadcast the transaction
|
|
||||||
|
|
||||||
### 4. Verify Heir Distribution
|
|
||||||
- Go to **Tools → Bal Electrum Plugin → View Heirs**
|
|
||||||
- Check the distribution summary
|
|
||||||
- Verify all percentages sum to 100%
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📁 Plugin Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
bal-electrum-plugin/
|
|
||||||
├── bal_electrum_plugin/ # Main plugin code (what you zip)
|
|
||||||
│ ├── __init__.py # Plugin entry point
|
|
||||||
│ ├── qt.py # Main Qt interface
|
|
||||||
│ ├── heirs.py # Heir management logic
|
|
||||||
│ ├── locktime.py # Locktime transaction handling
|
|
||||||
│ ├── dialogs/ # UI dialogs
|
|
||||||
│ │ ├── heirs_dialog.py # Heir configuration dialog
|
|
||||||
│ │ ├── locktime_dialog.py # Locktime setup dialog
|
|
||||||
│ │ └── wizard.py # Setup wizard dialog
|
|
||||||
│ └── utils.py # Utility functions
|
|
||||||
├── README.md # This file
|
|
||||||
└── LICENSE # MIT License
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: Only the `bal_electrum_plugin/` directory needs to be zipped for installation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 Configuration
|
|
||||||
|
|
||||||
### Plugin Settings
|
|
||||||
After installation, configure the plugin in Electrum:
|
|
||||||
|
|
||||||
1. Go to **Tools → Bal Electrum Plugin → Settings**
|
|
||||||
2. Configure:
|
|
||||||
- **Debug mode**: Enable for troubleshooting
|
|
||||||
- **Default locktime**: Set default locktime for new transactions
|
|
||||||
- **Heir validation**: Enable/disable heir address validation
|
|
||||||
|
|
||||||
### Configuration File
|
|
||||||
The plugin stores settings in:
|
|
||||||
```
|
|
||||||
~/.electrum/plugins/bal_electrum_plugin/config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Environment Variables (Advanced)
|
|
||||||
```bash
|
|
||||||
# Enable debug logging
|
|
||||||
BAL_DEBUG=1
|
|
||||||
|
|
||||||
# Custom config path
|
|
||||||
BAL_CONFIG_PATH=/path/to/config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Key Features
|
|
||||||
|
|
||||||
### Heir Management
|
|
||||||
✅ **Add/Remove Heirs** - Manage multiple beneficiaries
|
|
||||||
✅ **Inheritance Percentages** - Set distribution shares (sum must be 100%)
|
|
||||||
✅ **Locktime Conditions** - Define when each heir can access their funds
|
|
||||||
✅ **Address Validation** - Verify Bitcoin addresses before saving
|
|
||||||
✅ **Distribution Summary** - View total inheritance breakdown
|
|
||||||
|
|
||||||
### Locktime Transactions
|
|
||||||
✅ **Absolute Locktime** - Transaction can only be spent after a specific:
|
|
||||||
- Block height (e.g., `700000`)
|
|
||||||
- Timestamp (Unix timestamp, e.g., `1609459200`)
|
|
||||||
|
|
||||||
✅ **Relative Locktime** - Transaction can only be spent after:
|
|
||||||
- **Days**: Use suffix `d` (e.g., `3d` = 3 days from now)
|
|
||||||
- **Years**: Use suffix `y` (e.g., `2y` = 2 years from now)
|
|
||||||
|
|
||||||
✅ **Locktime Types**:
|
|
||||||
- **Block-based**: Locktime in blocks
|
|
||||||
- **Time-based**: Locktime in seconds since epoch
|
|
||||||
- **Relative**: Time intervals from current time
|
|
||||||
|
|
||||||
### Multi-Signature Support
|
|
||||||
✅ **Combine signatures** with existing wallet signatures
|
|
||||||
✅ **Configure required signatures** for spending
|
|
||||||
✅ **Support for complex multisig** setups with heirs
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📖 Usage Examples
|
|
||||||
|
|
||||||
### Example 1: Simple Heir Setup
|
|
||||||
|
|
||||||
1. **Add an heir**:
|
|
||||||
- Address: `bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq`
|
|
||||||
- Percentage: `50%`
|
|
||||||
- Locktime: `1y` (1 year from now)
|
|
||||||
|
|
||||||
2. **Add second heir**:
|
|
||||||
- Address: `bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4`
|
|
||||||
- Percentage: `50%`
|
|
||||||
- Locktime: `2y` (2 years from now)
|
|
||||||
|
|
||||||
3. **Create transaction**:
|
|
||||||
- Send 0.5 BTC to heir1 after 1 year
|
|
||||||
- Send 0.5 BTC to heir2 after 2 years
|
|
||||||
|
|
||||||
### Example 2: Locktime Transaction
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Create a transaction that can only be spent after 6 months
|
|
||||||
from bal_electrum_plugin.locktime import Locktime
|
|
||||||
|
|
||||||
locktime = Locktime(
|
|
||||||
type="relative",
|
|
||||||
value="6m", # 6 months from now
|
|
||||||
unit="blocks" # or "seconds"
|
|
||||||
)
|
|
||||||
|
|
||||||
# This transaction will be locked for 6 months
|
|
||||||
tx = create_transaction(
|
|
||||||
recipients=[recipient_address],
|
|
||||||
amount=0.1,
|
|
||||||
locktime=locktime
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 3: Complex Inheritance
|
|
||||||
|
|
||||||
- **Heir 1**: 30%, locktime `1y`
|
|
||||||
- **Heir 2**: 40%, locktime `2y`
|
|
||||||
- **Heir 3**: 30%, locktime `3y`
|
|
||||||
|
|
||||||
Total: 100% distributed over 3 years
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ Development
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
- Electrum Bitcoin wallet (version 4.0.0 or later)
|
|
||||||
- Python 3.7+
|
|
||||||
- PyQt5
|
|
||||||
- pytest (for testing)
|
|
||||||
|
|
||||||
### Setup Development Environment
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone the repository
|
|
||||||
git clone https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin.git
|
|
||||||
cd bal-electrum-plugin
|
|
||||||
|
|
||||||
# Create development zip
|
|
||||||
zip -r bal-electrum-plugin-dev.zip bal_electrum_plugin/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install for Development
|
|
||||||
|
|
||||||
1. Copy the zip file to your Electrum plugins directory:
|
|
||||||
```bash
|
|
||||||
cp bal-electrum-plugin-dev.zip ~/.electrum/plugins/
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Install in Electrum (Tools → Plugins → Add)
|
|
||||||
|
|
||||||
3. Make changes to the source code in `bal_electrum_plugin/` directory
|
|
||||||
|
|
||||||
4. Re-zip and reinstall to test changes:
|
|
||||||
```bash
|
|
||||||
cd ~/.electrum/plugins/
|
|
||||||
unzip bal-electrum-plugin-dev.zip -d bal_electrum_plugin_temp
|
|
||||||
# Make your changes to bal_electrum_plugin_temp/
|
|
||||||
zip -r bal-electrum-plugin-dev.zip bal_electrum_plugin_temp/
|
|
||||||
rm -rf bal_electrum_plugin_temp
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install test dependencies
|
|
||||||
pip install pytest
|
|
||||||
|
|
||||||
# Run all tests
|
|
||||||
pytest tests/
|
|
||||||
|
|
||||||
# Run specific test
|
|
||||||
pytest tests/test_heirs.py -v
|
|
||||||
```
|
|
||||||
|
|
||||||
### Code Style
|
|
||||||
|
|
||||||
This project follows:
|
|
||||||
- **PEP 8** style guide
|
|
||||||
- 4 spaces for indentation
|
|
||||||
- 79 characters line length
|
|
||||||
- Descriptive variable and function names
|
|
||||||
- Type hints for public functions
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
### Plugin Not Showing in Electrum
|
|
||||||
|
|
||||||
❌ **Problem**: The plugin doesn't appear in Electrum's plugin list
|
|
||||||
|
|
||||||
✅ **Solutions**:
|
|
||||||
1. Verify you're using Electrum 4.0.0 or later
|
|
||||||
2. Check that the zip file contains the `bal_electrum_plugin/` directory
|
|
||||||
3. Ensure the directory structure is correct inside the zip
|
|
||||||
4. Restart Electrum after installation
|
|
||||||
5. Check Electrum logs for errors (Help → Debug → Console)
|
|
||||||
|
|
||||||
### Locktime Not Working
|
|
||||||
|
|
||||||
❌ **Problem**: Locktime conditions aren't being enforced
|
|
||||||
|
|
||||||
✅ **Solutions**:
|
|
||||||
1. Verify locktime format: `3d` (days), `2y` (years), or timestamp
|
|
||||||
2. Check that the locktime is in the future
|
|
||||||
3. Ensure your Bitcoin node supports locktime transactions
|
|
||||||
4. Test with a small amount first
|
|
||||||
5. Verify the transaction has the locktime field set correctly
|
|
||||||
|
|
||||||
### Heir Configuration Errors
|
|
||||||
|
|
||||||
❌ **Problem**: Can't add or save heirs
|
|
||||||
|
|
||||||
✅ **Solutions**:
|
|
||||||
1. Verify Bitcoin addresses are valid (use testnet for testing)
|
|
||||||
2. Check that percentages sum to exactly 100%
|
|
||||||
3. Ensure locktime values are valid formats
|
|
||||||
4. Enable debug mode to see detailed error messages
|
|
||||||
5. Check plugin logs for specific error details
|
|
||||||
|
|
||||||
### Common Error Messages
|
|
||||||
|
|
||||||
| Error | Cause | Solution |
|
|
||||||
|-------|-------|----------|
|
|
||||||
| `Invalid checksum` | Bad Bitcoin address | Verify the address |
|
|
||||||
| `Percentage must sum to 100%` | Invalid heir distribution | Adjust percentages |
|
|
||||||
| `Locktime in the past` | Invalid locktime value | Use future date/time |
|
|
||||||
| `Plugin not found` | Incorrect zip structure | Check zip contents |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
|
||||||
|
|
||||||
We welcome contributions from the community! Here's how to help:
|
|
||||||
|
|
||||||
### Ways to Contribute
|
|
||||||
|
|
||||||
1. **Report bugs** - Open an issue on [Gitea](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/issues)
|
|
||||||
2. **Suggest features** - Share your ideas for new functionality
|
|
||||||
3. **Write documentation** - Improve README, add examples, create tutorials
|
|
||||||
4. **Submit pull requests** - Fix bugs or add new features
|
|
||||||
5. **Test the plugin** - Try different scenarios and report issues
|
|
||||||
6. **Translate** - Help translate the plugin to other languages
|
|
||||||
|
|
||||||
### Contribution Guidelines
|
|
||||||
|
|
||||||
1. **Fork the repository** and create your feature branch
|
|
||||||
2. **Add tests** for new functionality
|
|
||||||
3. **Follow PEP 8** style guide
|
|
||||||
4. **Write clear commit messages** following [Conventional Commits](https://www.conventionalcommits.org/)
|
|
||||||
5. **Update documentation** for new features
|
|
||||||
6. **Open a Pull Request** to the `main` branch
|
|
||||||
7. **Respond to feedback** during code review
|
|
||||||
|
|
||||||
### Development Workflow
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Fork the repository on Gitea
|
|
||||||
|
|
||||||
# Clone your fork
|
|
||||||
git clone https://bitcoin-after.life/gitea/YOUR_USERNAME/bal-electrum-plugin.git
|
|
||||||
cd bal-electrum-plugin
|
|
||||||
|
|
||||||
# Add upstream remote
|
|
||||||
git remote add upstream https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin.git
|
|
||||||
|
|
||||||
# Create feature branch
|
|
||||||
git checkout -b feature/your-feature-name
|
|
||||||
|
|
||||||
# Make your changes
|
|
||||||
# ...
|
|
||||||
|
|
||||||
# Test your changes
|
|
||||||
pytest tests/
|
|
||||||
|
|
||||||
# Commit changes
|
|
||||||
git add .
|
|
||||||
git commit -m "feat: add new feature description"
|
|
||||||
|
|
||||||
# Push to your fork
|
|
||||||
git push origin feature/your-feature-name
|
|
||||||
|
|
||||||
# Open Pull Request on Gitea
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📜 License
|
|
||||||
|
|
||||||
This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.
|
|
||||||
|
|
||||||
### License Summary
|
|
||||||
- Free to use and modify
|
|
||||||
- Commercial use allowed
|
|
||||||
- No warranty provided
|
|
||||||
- Attribution required
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🙏 Acknowledgments
|
|
||||||
|
|
||||||
- **Electrum developers** - For creating the amazing Electrum wallet
|
|
||||||
- **Bitcoin community** - For continuous innovation and support
|
|
||||||
- **All contributors** - For improving this plugin
|
|
||||||
- **Early users** - For testing and feedback
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📞 Support & Contact
|
|
||||||
|
|
||||||
### Getting Help
|
|
||||||
|
|
||||||
- **Documentation**: This README file
|
|
||||||
- **Issues**: [Gitea Issues](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/issues)
|
|
||||||
- **Discussions**: [Gitea Discussions](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/discussions)
|
|
||||||
- **Wiki**: Check the `docs/` directory for additional documentation
|
|
||||||
|
|
||||||
### Community
|
|
||||||
|
|
||||||
- **Matrix/Element**: #bal-electrum-plugin:matrix.org
|
|
||||||
- **Telegram**: @bal_electrum_plugin
|
|
||||||
- **Email**: support@bal-electrum-plugin.org
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Version Information
|
|
||||||
|
|
||||||
**Current Version**: 1.0.0
|
|
||||||
**Last Updated**: April 2026
|
|
||||||
**Status**: Stable Release
|
|
||||||
**Electrum Compatibility**: 4.0.0+
|
|
||||||
|
|
||||||
### Version History
|
|
||||||
|
|
||||||
| Version | Date | Changes |
|
|
||||||
|---------|------|---------|
|
|
||||||
| 1.0.0 | Apr 2026 | Initial release with heir and locktime features |
|
|
||||||
| 0.9.0 | Mar 2026 | Beta testing phase |
|
|
||||||
| 0.1.0 | Feb 2026 | Initial development |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔗 Links
|
|
||||||
|
|
||||||
- **Repository**: [https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin)
|
|
||||||
- **Releases**: [https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/releases](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/releases)
|
|
||||||
- **Issues**: [https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/issues](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/issues)
|
|
||||||
- **Discussions**: [https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/discussions](https://bitcoin-after.life/gitea/bitcoinafterlife/bal-electrum-plugin/discussions)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**⚠️ Important**: This plugin deals with real Bitcoin transactions. Always test with small amounts first and verify all settings before using with large amounts.
|
|
||||||
|
|
||||||
**🔒 Security**: Never share your seed phrase or private keys. This plugin only creates transactions, it doesn't store your keys.
|
|
||||||
|
|||||||
2
bal.py
2
bal.py
@@ -48,7 +48,7 @@ class BalConfig:
|
|||||||
|
|
||||||
class BalPlugin(BasePlugin):
|
class BalPlugin(BasePlugin):
|
||||||
_version=None
|
_version=None
|
||||||
__version__ = "0.2.8" #AUTOMATICALLY GENERATED DO NOT EDIT
|
__version__ = "0.2.7" #AUTOMATICALLY GENERATED DO NOT EDIT
|
||||||
def version(self):
|
def version(self):
|
||||||
if not self._version:
|
if not self._version:
|
||||||
try:
|
try:
|
||||||
|
|||||||
46
heirs.py
46
heirs.py
@@ -30,11 +30,9 @@ from electrum.transaction import (
|
|||||||
PartialTransaction,
|
PartialTransaction,
|
||||||
PartialTxInput,
|
PartialTxInput,
|
||||||
PartialTxOutput,
|
PartialTxOutput,
|
||||||
TxOutput,
|
|
||||||
TxOutpoint,
|
TxOutpoint,
|
||||||
# TxOutput,
|
# TxOutput,
|
||||||
)
|
)
|
||||||
from electrum.payment_identifier import PaymentIdentifier
|
|
||||||
from electrum.util import (
|
from electrum.util import (
|
||||||
bfh,
|
bfh,
|
||||||
read_json_file,
|
read_json_file,
|
||||||
@@ -46,6 +44,7 @@ from electrum.util import (
|
|||||||
from .util import Util
|
from .util import Util
|
||||||
from .willexecutors import Willexecutors
|
from .willexecutors import Willexecutors
|
||||||
from electrum.util import BitcoinException
|
from electrum.util import BitcoinException
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .simple_config import SimpleConfig
|
from .simple_config import SimpleConfig
|
||||||
|
|
||||||
@@ -72,22 +71,28 @@ def reduce_outputs(in_amount, out_amount, fee, outputs):
|
|||||||
output.value = math.floor((in_amount - fee) / out_amount * output.value)
|
output.value = math.floor((in_amount - fee) / out_amount * output.value)
|
||||||
|
|
||||||
|
|
||||||
def create_op_return_script(data_hex: str) -> bytes:
|
"""
|
||||||
"""Crea scriptpubkey OP_RETURN in bytes"""
|
#TODO: put this method inside wallet.db to replace or complete get_locktime_for_new_transaction
|
||||||
data = bytes.fromhex(data_hex)
|
def get_current_height(network:'Network'):
|
||||||
|
#if no network or not up to date, just set locktime to zero
|
||||||
|
if not network:
|
||||||
|
return 0
|
||||||
|
chain = network.blockchain()
|
||||||
|
if chain.is_tip_stale():
|
||||||
|
return 0
|
||||||
|
# figure out current block height
|
||||||
|
chain_height = chain.height() # learnt from all connected servers, SPV-checked
|
||||||
|
server_height = network.get_server_height() # height claimed by main server, unverified
|
||||||
|
# note: main server might be lagging (either is slow, is malicious, or there is an SPV-invisible-hard-fork)
|
||||||
|
# - if it's lagging too much, it is the network's job to switch away
|
||||||
|
if server_height < chain_height - 10:
|
||||||
|
# the diff is suspiciously large... give up and use something non-fingerprintable
|
||||||
|
return 0
|
||||||
|
# discourage "fee sniping"
|
||||||
|
height = min(chain_height, server_height)
|
||||||
|
return height
|
||||||
|
"""
|
||||||
|
|
||||||
if len(data) > 80:
|
|
||||||
raise ValueError("OP_RETURN data too big (max 80 bytes)")
|
|
||||||
|
|
||||||
# Costruzione manuale: OP_RETURN + push data
|
|
||||||
if len(data) <= 75:
|
|
||||||
# Formato più comune: OP_RETURN + 1-byte length + data
|
|
||||||
script = b'\x6a' + bytes([len(data)]) + data
|
|
||||||
else:
|
|
||||||
# Per dati più grandi (fino a 80) si usa OP_PUSHDATA1
|
|
||||||
script = b'\x6a\x4c' + bytes([len(data)]) + data
|
|
||||||
|
|
||||||
return script
|
|
||||||
|
|
||||||
def prepare_transactions(locktimes, available_utxos, fees, wallet):
|
def prepare_transactions(locktimes, available_utxos, fees, wallet):
|
||||||
available_utxos = sorted(
|
available_utxos = sorted(
|
||||||
@@ -162,13 +167,6 @@ def prepare_transactions(locktimes, available_utxos, fees, wallet):
|
|||||||
outputs.append(change)
|
outputs.append(change)
|
||||||
for i in range(0, 100):
|
for i in range(0, 100):
|
||||||
random.shuffle(outputs)
|
random.shuffle(outputs)
|
||||||
|
|
||||||
#op_return_text = "Hello Bal!"
|
|
||||||
|
|
||||||
## Convert text to hex
|
|
||||||
#op_return_hex = op_return_text.encode('utf-8').hex()
|
|
||||||
#op_return_script = create_op_return_script(op_return_hex)
|
|
||||||
#outputs.append(PartialTxOutput(value=0, scriptpubkey=op_return_script))
|
|
||||||
tx = PartialTransaction.from_io(
|
tx = PartialTransaction.from_io(
|
||||||
used_utxos,
|
used_utxos,
|
||||||
outputs,
|
outputs,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "BAL",
|
"name": "BAL",
|
||||||
"fullname": "Bitcoin After Life",
|
"fullname": "Bitcoin After Life",
|
||||||
"description": "Provides free and decentralized inheritance support<br> Version: 0.2.8",
|
"description": "Provides free and decentralized inheritance support<br> Version: 0.2.10",
|
||||||
"author":"Svatantrya",
|
"author":"Svatantrya",
|
||||||
"available_for": ["qt"],
|
"available_for": ["qt"],
|
||||||
"icon":"icons/bal32x32.png"
|
"icon":"icons/bal32x32.png"
|
||||||
|
|||||||
539
qt.py
539
qt.py
@@ -390,6 +390,26 @@ class shown_cv:
|
|||||||
|
|
||||||
|
|
||||||
class BalWindow(Logger):
|
class BalWindow(Logger):
|
||||||
|
"""Main application window for Bitcoin After Life inheritance management.
|
||||||
|
|
||||||
|
This class provides the primary UI for:
|
||||||
|
- Managing inheritance plans
|
||||||
|
- Building Wills
|
||||||
|
- Handling heir configurations
|
||||||
|
- Network operations
|
||||||
|
- Transaction signing and broadcasting
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
bal_plugin (BalPlugin): Reference to the plugin instance
|
||||||
|
config (dict): Application configuration
|
||||||
|
network (Network): Network interface
|
||||||
|
wallet (Abstract_Wallet): Wallet interface
|
||||||
|
heirs (list): List of heir configurations
|
||||||
|
will (Will): Current Will being managed
|
||||||
|
building_will (bool): Flag indicating Will construction in progress
|
||||||
|
stop_build (bool): Flag to stop Will construction
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, bal_plugin: "BalPlugin", window: "ElectrumWindow"):
|
def __init__(self, bal_plugin: "BalPlugin", window: "ElectrumWindow"):
|
||||||
Logger.__init__(self)
|
Logger.__init__(self)
|
||||||
self.bal_plugin = bal_plugin
|
self.bal_plugin = bal_plugin
|
||||||
@@ -1272,6 +1292,17 @@ class _LockTimeEditor:
|
|||||||
|
|
||||||
|
|
||||||
class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
|
class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
|
||||||
|
"""HeirsLockTimeEdit class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = HeirsLockTimeEdit(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
valueEdited = pyqtSignal()
|
valueEdited = pyqtSignal()
|
||||||
locktime_threshold = 50000000
|
locktime_threshold = 50000000
|
||||||
|
|
||||||
@@ -1334,6 +1365,17 @@ class HeirsLockTimeEdit(QWidget, _LockTimeEditor):
|
|||||||
self.editor.set_locktime(x, force)
|
self.editor.set_locktime(x, force)
|
||||||
|
|
||||||
|
|
||||||
|
"""LockTimeRawEdit class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = LockTimeRawEdit(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
|
class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
|
||||||
def __init__(self, parent=None, time_edit=None):
|
def __init__(self, parent=None, time_edit=None):
|
||||||
QLineEdit.__init__(self, parent)
|
QLineEdit.__init__(self, parent)
|
||||||
@@ -1418,6 +1460,17 @@ class LockTimeRawEdit(QLineEdit, _LockTimeEditor):
|
|||||||
out = min(out, self.max_allowed_value)
|
out = min(out, self.max_allowed_value)
|
||||||
self.setText(str(out))
|
self.setText(str(out))
|
||||||
|
|
||||||
|
"""LockTimeHeightEdit class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = LockTimeHeightEdit(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class LockTimeHeightEdit(LockTimeRawEdit):
|
class LockTimeHeightEdit(LockTimeRawEdit):
|
||||||
max_allowed_value = NLOCKTIME_BLOCKHEIGHT_MAX
|
max_allowed_value = NLOCKTIME_BLOCKHEIGHT_MAX
|
||||||
@@ -1448,6 +1501,17 @@ def get_max_allowed_timestamp() -> int:
|
|||||||
ts = 2**31 - 1 # INT32_MAX
|
ts = 2**31 - 1 # INT32_MAX
|
||||||
datetime.fromtimestamp(ts) # test if raises
|
datetime.fromtimestamp(ts) # test if raises
|
||||||
return ts
|
return ts
|
||||||
|
"""LockTimeDateEdit class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = LockTimeDateEdit(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
|
class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
|
||||||
@@ -1479,6 +1543,25 @@ class LockTimeDateEdit(QDateTimeEdit, _LockTimeEditor):
|
|||||||
self.setDateTime(dt)
|
self.setDateTime(dt)
|
||||||
|
|
||||||
|
|
||||||
|
"""Widget for editing percentage amounts.
|
||||||
|
|
||||||
|
Extends BTCAmountEdit to provide:
|
||||||
|
- Percentage-based input (0-100%)
|
||||||
|
- Validation of percentage values
|
||||||
|
- Conversion between percentage and satoshis
|
||||||
|
- Formatting for display
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
min_value (float): Minimum percentage (default 0.0)
|
||||||
|
max_value (float): Maximum percentage (default 100.0)
|
||||||
|
step (float): Increment step for percentage values
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
value(): Return current percentage as float
|
||||||
|
setValue(): Set percentage value
|
||||||
|
validate(): Validate percentage range
|
||||||
|
"""
|
||||||
|
|
||||||
_NOT_GIVEN = object() # sentinel value
|
_NOT_GIVEN = object() # sentinel value
|
||||||
|
|
||||||
|
|
||||||
@@ -1545,6 +1628,35 @@ class PercAmountEdit(BTCAmountEdit):
|
|||||||
painter.drawText(
|
painter.drawText(
|
||||||
textRect,
|
textRect,
|
||||||
int(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter),
|
int(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter),
|
||||||
|
"""Base class for dialog windows in Bitcoin After Life.
|
||||||
|
|
||||||
|
Provides common functionality for all dialog windows including:
|
||||||
|
- Window modal behavior
|
||||||
|
- Standard button layouts (OK, Cancel)
|
||||||
|
- Size policy management
|
||||||
|
- Parent window handling
|
||||||
|
|
||||||
|
Subclasses should implement:
|
||||||
|
- __init__: Initialize dialog components
|
||||||
|
- setup_ui: Create and configure UI elements
|
||||||
|
- connect_signals: Connect signal-slot connections
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
parent (QWidget): Parent window
|
||||||
|
plugin (BalPlugin): Plugin reference
|
||||||
|
title (str): Dialog window title
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
exec_(): Show dialog modally
|
||||||
|
accept(): Handle dialog acceptance
|
||||||
|
reject(): Handle dialog rejection
|
||||||
|
|
||||||
|
Example:
|
||||||
|
dialog = BalDialog(parent, plugin, "My Dialog")
|
||||||
|
if dialog.exec_() == QDialog.Accepted:
|
||||||
|
# Handle acceptance
|
||||||
|
"""
|
||||||
|
|
||||||
self.base_unit() + " or perc value",
|
self.base_unit() + " or perc value",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1552,6 +1664,33 @@ class PercAmountEdit(BTCAmountEdit):
|
|||||||
class BalDialog(WindowModalDialog):
|
class BalDialog(WindowModalDialog):
|
||||||
def __init__(self, parent, bal_plugin, title=None, icon="icons/bal32x32.png"):
|
def __init__(self, parent, bal_plugin, title=None, icon="icons/bal32x32.png"):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
"""Base class for multi-step wizard dialogs.
|
||||||
|
|
||||||
|
Provides infrastructure for:
|
||||||
|
- Multi-page workflows
|
||||||
|
- Navigation between steps
|
||||||
|
- Progress tracking
|
||||||
|
- Validation at each step
|
||||||
|
- Final submission
|
||||||
|
|
||||||
|
Subclasses implement specific wizards:
|
||||||
|
- BalWizardHeirsWidget: Heir configuration
|
||||||
|
- BalWizardWEDownloadWidget: Wallet export download
|
||||||
|
- BalWizardWEWidget: Wallet export configuration
|
||||||
|
- BalWizardLocktimeAndFeeWidget: Locktime and fee setup
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
current_page (int): Current step index
|
||||||
|
total_pages (int): Total number of steps
|
||||||
|
data (dict): Collected data across steps
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
next_page(): Move to next step
|
||||||
|
prev_page(): Move to previous step
|
||||||
|
validate_current(): Validate current step
|
||||||
|
finish(): Complete the wizard
|
||||||
|
"""
|
||||||
|
|
||||||
WindowModalDialog.__init__(self, parent, title)
|
WindowModalDialog.__init__(self, parent, title)
|
||||||
# WindowModalDialog.__init__(self,parent)
|
# WindowModalDialog.__init__(self,parent)
|
||||||
self.setWindowIcon(read_QIcon_from_bytes(bal_plugin.read_file(icon)))
|
self.setWindowIcon(read_QIcon_from_bytes(bal_plugin.read_file(icon)))
|
||||||
@@ -1640,6 +1779,17 @@ class BalWizardDialog(BalDialog):
|
|||||||
self.bal_window.update_all()
|
self.bal_window.update_all()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
"""BalWizardWidget class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = BalWizardWidget(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
|
|
||||||
self.bal_window.heir_list_widget.update_will_settings()
|
self.bal_window.heir_list_widget.update_will_settings()
|
||||||
@@ -1705,6 +1855,17 @@ class BalWizardWidget(QWidget):
|
|||||||
def _on_previous(self):
|
def _on_previous(self):
|
||||||
self.on_previous()
|
self.on_previous()
|
||||||
|
|
||||||
|
"""BalWizardHeirsWidget class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = BalWizardHeirsWidget(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
def get_content(self):
|
def get_content(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -1739,6 +1900,17 @@ class BalWizardHeirsWidget(BalWizardWidget):
|
|||||||
def export_to_file(self):
|
def export_to_file(self):
|
||||||
self.bal_window.export_heirs()
|
self.bal_window.export_heirs()
|
||||||
|
|
||||||
|
"""BalWizardWEDownloadWidget class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = BalWizardWEDownloadWidget(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
def add_heir(self):
|
def add_heir(self):
|
||||||
self.bal_window.new_heir_dialog()
|
self.bal_window.new_heir_dialog()
|
||||||
self.heir_list_widget.update()
|
self.heir_list_widget.update()
|
||||||
@@ -1826,6 +1998,17 @@ class BalWizardWEDownloadWidget(BalWizardWidget):
|
|||||||
if self.validate():
|
if self.validate():
|
||||||
return self.on_next()
|
return self.on_next()
|
||||||
|
|
||||||
|
"""BalWizardWEWidget class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = BalWizardWEWidget(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
def import_json_file(self, path):
|
def import_json_file(self, path):
|
||||||
data = read_json_file(path)
|
data = read_json_file(path)
|
||||||
data = self._validate(data)
|
data = self._validate(data)
|
||||||
@@ -1842,6 +2025,17 @@ class BalWizardWEWidget(BalWizardWidget):
|
|||||||
def get_content(self):
|
def get_content(self):
|
||||||
widget = QWidget()
|
widget = QWidget()
|
||||||
vbox = QVBoxLayout(widget)
|
vbox = QVBoxLayout(widget)
|
||||||
|
"""BalWizardLocktimeAndFeeWidget class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = BalWizardLocktimeAndFeeWidget(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
vbox.addWidget(
|
vbox.addWidget(
|
||||||
WillExecutorWidget(
|
WillExecutorWidget(
|
||||||
self,
|
self,
|
||||||
@@ -1951,6 +2145,30 @@ class BalWizardLocktimeAndFeeWidget(BalWizardWidget):
|
|||||||
_("Fees(sats/vbyte):"),
|
_("Fees(sats/vbyte):"),
|
||||||
self.heir_tx_fees,
|
self.heir_tx_fees,
|
||||||
("Fee to be used in the transaction"),
|
("Fee to be used in the transaction"),
|
||||||
|
"""Non-blocking progress dialog for long operations.
|
||||||
|
|
||||||
|
Provides:
|
||||||
|
- Indeterminate progress bar
|
||||||
|
- Status messages
|
||||||
|
- Cancel button for interruption
|
||||||
|
- Automatic cleanup
|
||||||
|
- Thread-safe operation
|
||||||
|
|
||||||
|
Used for operations like:
|
||||||
|
- Blockchain synchronization
|
||||||
|
- Transaction broadcasting
|
||||||
|
- Will construction
|
||||||
|
- Data loading
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
message (str): Current status message
|
||||||
|
can_cancel (bool): Whether operation can be cancelled
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
update_message(): Update status message
|
||||||
|
cancel(): Interrupt the operation
|
||||||
|
"""
|
||||||
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2015,7 +2233,17 @@ class BalWaitingDialog(BalDialog):
|
|||||||
self.thread.stop()
|
self.thread.stop()
|
||||||
|
|
||||||
def update_message(self, msg):
|
def update_message(self, msg):
|
||||||
print(msg)
|
"""BalBlockingWaitingDialog class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = BalBlockingWaitingDialog(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
self.message_label.setText(msg)
|
self.message_label.setText(msg)
|
||||||
|
|
||||||
def update(self, msg):
|
def update(self, msg):
|
||||||
@@ -2035,6 +2263,17 @@ class BalBlockingWaitingDialog(BalDialog):
|
|||||||
vbox = QVBoxLayout(self)
|
vbox = QVBoxLayout(self)
|
||||||
vbox.addWidget(self.message_label)
|
vbox.addWidget(self.message_label)
|
||||||
self.finished.connect(self.deleteLater) # see #3956
|
self.finished.connect(self.deleteLater) # see #3956
|
||||||
|
"""bal_checkbox class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = bal_checkbox(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
# show popup
|
# show popup
|
||||||
self.show()
|
self.show()
|
||||||
# refresh GUI; needed for popup to appear and for message_label to get drawn
|
# refresh GUI; needed for popup to appear and for message_label to get drawn
|
||||||
@@ -2070,6 +2309,18 @@ class BalBuildWillDialog(BalDialog):
|
|||||||
COLOR_OK = "#05ad05"
|
COLOR_OK = "#05ad05"
|
||||||
|
|
||||||
def __init__(self, bal_window, parent=None):
|
def __init__(self, bal_window, parent=None):
|
||||||
|
"""Initialize the Build Will dialog.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bal_window (BalWindow): The main application window
|
||||||
|
parent (QWidget, optional): Parent widget. Defaults to None.
|
||||||
|
|
||||||
|
Initializes:
|
||||||
|
- Main UI components (message label, container widget)
|
||||||
|
- Message queue system with debounce timer
|
||||||
|
- Layout management
|
||||||
|
- Network connection
|
||||||
|
"""
|
||||||
if not parent:
|
if not parent:
|
||||||
parent = bal_window.window
|
parent = bal_window.window
|
||||||
BalDialog.__init__(self, parent, bal_window.bal_plugin, _("Building Will"))
|
BalDialog.__init__(self, parent, bal_window.bal_plugin, _("Building Will"))
|
||||||
@@ -2077,16 +2328,43 @@ class BalBuildWillDialog(BalDialog):
|
|||||||
self.updatemessage.connect(self.msg_update)
|
self.updatemessage.connect(self.msg_update)
|
||||||
self.bal_window = bal_window
|
self.bal_window = bal_window
|
||||||
self.bal_plugin = bal_window.bal_plugin
|
self.bal_plugin = bal_window.bal_plugin
|
||||||
|
|
||||||
|
# Main message label
|
||||||
self.message_label = QLabel(_("Building Will:"))
|
self.message_label = QLabel(_("Building Will:"))
|
||||||
self.vbox = QVBoxLayout(self)
|
self.vbox = QVBoxLayout(self)
|
||||||
self.vbox.addWidget(self.message_label,0)
|
self.vbox.addWidget(self.message_label, 0)
|
||||||
|
|
||||||
|
# Container for dynamic messages
|
||||||
self.qwidget = QWidget(self)
|
self.qwidget = QWidget(self)
|
||||||
self.vbox.addWidget(self.qwidget,1)
|
self.vbox.addWidget(self.qwidget, 1)
|
||||||
self.labelsbox=QVBoxLayout(self.qwidget)
|
|
||||||
|
# Layout for messages with reduced spacing
|
||||||
|
self.labelsbox = QVBoxLayout(self.qwidget)
|
||||||
|
self.labelsbox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.labelsbox.setSpacing(4) # Reduced spacing between messages
|
||||||
|
|
||||||
|
# Set minimum dimensions
|
||||||
self.setMinimumWidth(600)
|
self.setMinimumWidth(600)
|
||||||
self.setMinimumHeight(100)
|
self.setMinimumHeight(100)
|
||||||
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||||
self.labels = []
|
|
||||||
|
# Message queue implementation for efficient updates
|
||||||
|
self._message_queue = [] # Thread-safe message queue
|
||||||
|
self._message_timer = QTimer(self)
|
||||||
|
self._message_timer.setSingleShot(True)
|
||||||
|
self._message_timer.setInterval(50) # Debounce interval: 50ms
|
||||||
|
self._message_timer.timeout.connect(self._process_message_queue)
|
||||||
|
|
||||||
|
# Other initialization
|
||||||
|
self.labels = [] # Immediate message storage
|
||||||
|
self.check_row = None
|
||||||
|
self.inval_row = None
|
||||||
|
self.build_row = None
|
||||||
|
self.sign_row = None
|
||||||
|
self.push_row = None
|
||||||
|
self.network = Network.get_instance()
|
||||||
|
self._stopping = False
|
||||||
|
|
||||||
self.check_row = None
|
self.check_row = None
|
||||||
self.inval_row = None
|
self.inval_row = None
|
||||||
self.build_row = None
|
self.build_row = None
|
||||||
@@ -2535,27 +2813,103 @@ class BalBuildWillDialog(BalDialog):
|
|||||||
w.setParent(None)
|
w.setParent(None)
|
||||||
w.deleteLater()
|
w.deleteLater()
|
||||||
|
|
||||||
#def msg_update(self):
|
|
||||||
# self.clear_layout(self.labelsbox)
|
|
||||||
# for label in self.labels:
|
|
||||||
# label=label.replace("\n","<br>")
|
|
||||||
# qlabel=QLabel(label)
|
|
||||||
# qlabel.setWordWrap(True)
|
|
||||||
# self.labelsbox.addWidget(qlabel)
|
|
||||||
|
|
||||||
# self.labelsbox.activate()
|
|
||||||
# self.qwidget.setMinimumSize(self.labelsbox.sizeHint())
|
|
||||||
# self.qwidget.adjustSize()
|
|
||||||
# from PyQt6.QtWidgets import QApplication
|
|
||||||
# QApplication.processEvents()
|
|
||||||
#
|
|
||||||
# self.adjustSize()
|
|
||||||
def msg_update(self):
|
def msg_update(self):
|
||||||
full_text = "<br><br>".join(self.labels).replace("\n", "<br>")
|
"""Updates the UI with new messages using a debounced queue system.
|
||||||
self.message_label.setText(full_text)
|
|
||||||
self.message_label.adjustSize()
|
This method implements the following logic:
|
||||||
#self.setMinimumHeight(len(self.labels)*40)
|
1. Adds all pending messages to the queue
|
||||||
self.resize(self.sizeHint())
|
2. Clears the immediate message storage
|
||||||
|
3. Starts the debounce timer if not already active
|
||||||
|
|
||||||
|
The actual UI update happens in _process_message_queue after the
|
||||||
|
debounce interval to prevent excessive UI updates.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Thread-safe operation - can be called from any thread
|
||||||
|
"""
|
||||||
|
self._message_queue.extend(self.labels)
|
||||||
|
self.labels = [] # Clear immediate labels after queuing
|
||||||
|
if not self._message_timer.isActive():
|
||||||
|
self._message_timer.start()
|
||||||
|
|
||||||
|
def _process_message_queue(self):
|
||||||
|
"""Processes queued messages with debounce for efficient UI updates.
|
||||||
|
|
||||||
|
This method:
|
||||||
|
1. Clears the existing layout
|
||||||
|
2. Processes all queued messages
|
||||||
|
3. Updates the UI once with all new messages
|
||||||
|
4. Resets the queue
|
||||||
|
5. Adjusts dialog height based on content
|
||||||
|
|
||||||
|
The debounce interval (50ms) ensures rapid message bursts are
|
||||||
|
processed in a single batch, reducing UI flicker.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Called automatically by QTimer after debounce interval
|
||||||
|
"""
|
||||||
|
if not self._message_queue:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Clear existing layout
|
||||||
|
self.clear_layout(self.labelsbox)
|
||||||
|
|
||||||
|
# Process all queued messages
|
||||||
|
for text in self._message_queue:
|
||||||
|
try:
|
||||||
|
# Format text for rich display
|
||||||
|
formatted_text = text.replace("\n", "<br>")
|
||||||
|
|
||||||
|
# Create label with proper settings
|
||||||
|
label = QLabel(formatted_text)
|
||||||
|
label.setWordWrap(True)
|
||||||
|
label.setTextFormat(Qt.TextFormat.RichText)
|
||||||
|
label.setOpenExternalLinks(False) # Security
|
||||||
|
|
||||||
|
# Set size policy
|
||||||
|
label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
|
||||||
|
# Add to layout
|
||||||
|
self.labelsbox.addWidget(label)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Log errors without interrupting processing
|
||||||
|
"""Table widget for managing heir configurations.
|
||||||
|
|
||||||
|
Provides a tabular interface for:
|
||||||
|
- Adding, editing, and removing heirs
|
||||||
|
- Configuring inheritance percentages
|
||||||
|
- Setting heir addresses and conditions
|
||||||
|
- Validating heir configurations
|
||||||
|
- Sorting and filtering heir list
|
||||||
|
|
||||||
|
Inherits from MyTreeView for:
|
||||||
|
- Column-based data display
|
||||||
|
- Sorting functionality
|
||||||
|
- Filtering capabilities
|
||||||
|
- Model-view architecture
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
bal_window (BalWindow): Reference to main window
|
||||||
|
main_window (QMainWindow): Parent window
|
||||||
|
model (QStandardItemModel): Data model for heirs
|
||||||
|
|
||||||
|
Columns:
|
||||||
|
NAME: Heir name
|
||||||
|
ADDRESS: Bitcoin address
|
||||||
|
AMOUNT: Inheritance percentage
|
||||||
|
|
||||||
|
Example:
|
||||||
|
widget = HeirListWidget(window, parent)
|
||||||
|
widget.update() # Refresh heir list
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logging.error(f"Error creating label in BalBuildWillDialog: {e}")
|
||||||
|
|
||||||
|
# Reset queue and update dimensions
|
||||||
|
self._message_queue = []
|
||||||
|
self.setMinimumHeight(min(30 * (len(self.labels) + 2), 400)) # Max height limit
|
||||||
|
|
||||||
|
|
||||||
def get_text(self):
|
def get_text(self):
|
||||||
@@ -2829,6 +3183,34 @@ class HeirListWidget(MyTreeView, MessageBoxMixin):
|
|||||||
|
|
||||||
return toolbar
|
return toolbar
|
||||||
|
|
||||||
|
"""Widget for previewing Will documents before signing.
|
||||||
|
|
||||||
|
Provides functionality to:
|
||||||
|
- Render Will in human-readable format
|
||||||
|
- Highlight important clauses and conditions
|
||||||
|
- Show inheritance distribution
|
||||||
|
- Validate Will structure
|
||||||
|
- Generate PDF preview
|
||||||
|
|
||||||
|
Uses MyTreeView for:
|
||||||
|
- Column-based preview display
|
||||||
|
- Sorting and filtering
|
||||||
|
- Efficient rendering of large documents
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
bal_window (BalWindow): Reference to main window
|
||||||
|
main_window (QMainWindow): Parent window
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
update(): Refresh preview display
|
||||||
|
validate(): Check Will structure
|
||||||
|
export_pdf(): Generate PDF document
|
||||||
|
|
||||||
|
Example:
|
||||||
|
preview = PreviewList(window, parent)
|
||||||
|
preview.update()
|
||||||
|
"""
|
||||||
|
|
||||||
def update_will_settings(self):
|
def update_will_settings(self):
|
||||||
try:
|
try:
|
||||||
self.heir_locktime.set_locktime(self.bal_window.will_settings["locktime"])
|
self.heir_locktime.set_locktime(self.bal_window.will_settings["locktime"])
|
||||||
@@ -3106,6 +3488,17 @@ class PreviewList(MyTreeView):
|
|||||||
close_window.build_will_task()
|
close_window.build_will_task()
|
||||||
|
|
||||||
will = {}
|
will = {}
|
||||||
|
"""PreviewDialog class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = PreviewDialog(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
for wid, w in self.bal_window.willitems.items():
|
for wid, w in self.bal_window.willitems.items():
|
||||||
if (
|
if (
|
||||||
w.get_status("VALID")
|
w.get_status("VALID")
|
||||||
@@ -3187,6 +3580,17 @@ class PreviewDialog(BalDialog, MessageBoxMixin):
|
|||||||
def update(self):
|
def update(self):
|
||||||
self.transactions_list.update()
|
self.transactions_list.update()
|
||||||
|
|
||||||
|
"""WillDetailDialog class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = WillDetailDialog(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
def is_hidden(self):
|
def is_hidden(self):
|
||||||
return self.isMinimized() or self.isHidden()
|
return self.isMinimized() or self.isHidden()
|
||||||
|
|
||||||
@@ -3287,6 +3691,17 @@ class WillDetailDialog(BalDialog):
|
|||||||
toggle = _("Unhide")
|
toggle = _("Unhide")
|
||||||
self.toggle_replace_button.setText(f"{toggle} {_('replaced')}")
|
self.toggle_replace_button.setText(f"{toggle} {_('replaced')}")
|
||||||
self.update()
|
self.update()
|
||||||
|
"""WillWidget class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = WillWidget(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def toggle_invalidated(self):
|
def toggle_invalidated(self):
|
||||||
self.bal_window.bal_plugin.hide_invalidated()
|
self.bal_window.bal_plugin.hide_invalidated()
|
||||||
@@ -3378,6 +3793,17 @@ class WillWidget(QWidget):
|
|||||||
decoded_amount = Util.decode_amount(
|
decoded_amount = Util.decode_amount(
|
||||||
self.will[w].we["base_fee"], self.parent.decimal_point
|
self.will[w].we["base_fee"], self.parent.decimal_point
|
||||||
)
|
)
|
||||||
|
"""WillExecutorListWidget class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = WillExecutorListWidget(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
detaillayout.addWidget(
|
detaillayout.addWidget(
|
||||||
qlabel(
|
qlabel(
|
||||||
@@ -3609,6 +4035,31 @@ class WillExecutorListWidget(MyTreeView):
|
|||||||
items[self.Columns.BASE_FEE].setData(
|
items[self.Columns.BASE_FEE].setData(
|
||||||
url, self.ROLE_HEIR_KEY + self.Columns.BASE_FEE
|
url, self.ROLE_HEIR_KEY + self.Columns.BASE_FEE
|
||||||
)
|
)
|
||||||
|
"""Widget for executing Will transactions.
|
||||||
|
|
||||||
|
Handles:
|
||||||
|
- Transaction construction
|
||||||
|
- Fee calculation
|
||||||
|
- Signature collection
|
||||||
|
- Transaction broadcasting
|
||||||
|
- Status monitoring
|
||||||
|
|
||||||
|
Coordinates with:
|
||||||
|
- WillExecutorListWidget: List of executors
|
||||||
|
- WillExecutorDialog: Detailed executor configuration
|
||||||
|
- BalWindow: Main application window
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
will (Will): Will being executed
|
||||||
|
executors (list): List of executor configurations
|
||||||
|
network (Network): Network interface
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
execute(): Start transaction execution
|
||||||
|
stop(): Interrupt execution
|
||||||
|
validate(): Check transaction validity
|
||||||
|
"""
|
||||||
|
|
||||||
items[self.Columns.INFO].setData(
|
items[self.Columns.INFO].setData(
|
||||||
url, self.ROLE_HEIR_KEY + self.Columns.INFO
|
url, self.ROLE_HEIR_KEY + self.Columns.INFO
|
||||||
)
|
)
|
||||||
@@ -3713,6 +4164,17 @@ class WillExecutorWidget(QWidget, MessageBoxMixin):
|
|||||||
self.willexecutors_list.update,
|
self.willexecutors_list.update,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
"""WillExecutorDialog class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = WillExecutorDialog(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
def update_willexecutors(self, wes=None):
|
def update_willexecutors(self, wes=None):
|
||||||
if not wes:
|
if not wes:
|
||||||
wes = self.willexecutors_list
|
wes = self.willexecutors_list
|
||||||
@@ -3748,6 +4210,17 @@ class WillExecutorDialog(BalDialog, MessageBoxMixin):
|
|||||||
self.setMinimumSize(1000, 200)
|
self.setMinimumSize(1000, 200)
|
||||||
|
|
||||||
vbox = QVBoxLayout(self)
|
vbox = QVBoxLayout(self)
|
||||||
|
"""CheckAliveException class for Bitcoin After Life.
|
||||||
|
|
||||||
|
This class provides functionality for managing various Bitcoin After Life features.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
See class implementation for attributes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
instance = CheckAliveException(required parameters)
|
||||||
|
"""
|
||||||
|
|
||||||
self.will_executor_list_widget = WillExecutorWidget(
|
self.will_executor_list_widget = WillExecutorWidget(
|
||||||
self, self.bal_window, self.willexecutors_list
|
self, self.bal_window, self.willexecutors_list
|
||||||
)
|
)
|
||||||
@@ -3774,4 +4247,20 @@ class CheckAliveException(Exception):
|
|||||||
def __init__(self,timestamp_to_check):
|
def __init__(self,timestamp_to_check):
|
||||||
self.timestamp_to_check = timestamp_to_check
|
self.timestamp_to_check = timestamp_to_check
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Explicit cleanup to prevent memory leaks.
|
||||||
|
|
||||||
|
This destructor ensures proper cleanup of:
|
||||||
|
- Message queue timer
|
||||||
|
- All widgets in the layout
|
||||||
|
- Network connections
|
||||||
|
|
||||||
|
Called automatically when the dialog is destroyed.
|
||||||
|
"""
|
||||||
|
if hasattr(self, '_message_timer') and self._message_timer:
|
||||||
|
self._message_timer.stop()
|
||||||
|
self._message_timer.deleteLater()
|
||||||
|
self.clear_layout(self.labelsbox)
|
||||||
|
|
||||||
return "Check alive expired please update it: {}".format(datetime.fromtimestamp(self.timestamp_to_check).isoformat())
|
return "Check alive expired please update it: {}".format(datetime.fromtimestamp(self.timestamp_to_check).isoformat())
|
||||||
|
|||||||
13
util.py
13
util.py
@@ -494,16 +494,3 @@ class Util:
|
|||||||
del will[txid]["tx_fees"]
|
del will[txid]["tx_fees"]
|
||||||
have_to_update = True
|
have_to_update = True
|
||||||
return have_to_update
|
return have_to_update
|
||||||
|
|
||||||
def text_to_hex(text: str) -> str:
|
|
||||||
"""Convert text to hexadecimal string"""
|
|
||||||
hex_string = text.encode('utf-8').hex()
|
|
||||||
return hex_string
|
|
||||||
|
|
||||||
|
|
||||||
def hex_to_text(hex_string: str) -> str:
|
|
||||||
"""Convert hexadecimal string back to text (for verification)"""
|
|
||||||
try:
|
|
||||||
return bytes.fromhex(hex_string).decode('utf-8')
|
|
||||||
except Exception:
|
|
||||||
return "Error: Invalid hex string"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user