This commit is contained in:
Lex Berezhny 2018-06-12 11:53:29 -04:00 committed by Jack Robison
parent 7d3daa9fe6
commit 56175df121
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
16 changed files with 418 additions and 288 deletions

View file

@ -1,59 +0,0 @@
import asyncio
from binascii import hexlify
from orchstr8.testcase import IntegrationTestCase
from torba.constants import COIN
from lbrynet.wallet.transaction import Transaction, Input, Output
class BasicTransactionTests(IntegrationTestCase):
VERBOSE = True
async def test_sending_and_recieving(self):
self.assertEqual(await self.lbrycrd.get_balance(), 10.0)
self.assertEqual(self.manager.get_balance(), 0.0)
address = self.account.get_least_used_receiving_address()
sendtxid = await self.lbrycrd.send_to_address(address.decode(), 5.5)
await self.lbrycrd.generate(1)
await self.on_transaction(sendtxid)
self.assertAlmostEqual(await self.lbrycrd.get_balance(), 5.5, places=2)
self.assertEqual(self.manager.get_balance(), 5.5)
lbrycrd_address = await self.lbrycrd.get_raw_change_address()
tx = self.manager.send_amount_to_address(5, lbrycrd_address)
await self.broadcast(tx)
await self.on_transaction(tx.id.decode())
await self.lbrycrd.generate(1)
self.assertAlmostEqual(await self.lbrycrd.get_balance(), 11.5, places=2)
#self.assertEqual(self.manager.get_balance(), 0.5)
class AbandonClaimLookup(IntegrationTestCase):
async def skip_test_abandon_claim(self):
address = yield self.lbry.wallet.get_least_used_address()
yield self.lbrycrd.sendtoaddress(address, 0.0003 - 0.0000355)
yield self.lbrycrd.generate(1)
yield self.lbry.wallet.update_balance()
yield threads.deferToThread(time.sleep, 5)
print(self.lbry.wallet.get_balance())
claim = yield self.lbry.wallet.claim_new_channel('@test', 0.000096)
yield self.lbrycrd.generate(1)
print('='*10 + 'CLAIM' + '='*10)
print(claim)
yield self.lbrycrd.decoderawtransaction(claim['tx'])
abandon = yield self.lbry.wallet.abandon_claim(claim['claim_id'], claim['txid'], claim['nout'])
print('='*10 + 'ABANDON' + '='*10)
print(abandon)
yield self.lbrycrd.decoderawtransaction(abandon['tx'])
yield self.lbrycrd.generate(1)
yield self.lbrycrd.getrawtransaction(abandon['txid'])
yield self.lbry.wallet.update_balance()
yield threads.deferToThread(time.sleep, 5)
print('='*10 + 'FINAL BALANCE' + '='*10)
print(self.lbry.wallet.get_balance())

View file

@ -0,0 +1,111 @@
import asyncio
from binascii import hexlify, unhexlify
from orchstr8.testcase import IntegrationTestCase
from lbryschema.claim import ClaimDict
class BasicTransactionTests(IntegrationTestCase):
VERBOSE = True
async def test_sending_and_recieving(self):
self.assertEqual(await self.get_balance(), 0.0)
address = self.account.get_least_used_receiving_address()
sendtxid = await self.blockchain.send_to_address(address.decode(), 5.5)
await self.blockchain.generate(1)
await self.on_transaction(sendtxid)
self.assertAlmostEqual(await self.get_balance(), 5.5, places=2)
lbrycrd_address = await self.blockchain.get_raw_change_address()
tx = self.manager.send_amount_to_address(5, lbrycrd_address)
await self.broadcast(tx)
await self.on_transaction(tx.id.decode())
await self.blockchain.generate(1)
self.assertAlmostEqual(await self.get_balance(), 0.5, places=2)
example_claim_dict = {
"version": "_0_0_1",
"claimType": "streamType",
"stream": {
"source": {
"source": "d5169241150022f996fa7cd6a9a1c421937276a3275eb912790bd07ba7aec1fac5fd45431d226b8fb402691e79aeb24b",
"version": "_0_0_1",
"contentType": "video/mp4",
"sourceType": "lbry_sd_hash"
},
"version": "_0_0_1",
"metadata": {
"license": "LBRY Inc",
"description": "What is LBRY? An introduction with Alex Tabarrok",
"language": "en",
"title": "What is LBRY?",
"author": "Samuel Bryan",
"version": "_0_1_0",
"nsfw": False,
"licenseUrl": "",
"preview": "",
"thumbnail": "https://s3.amazonaws.com/files.lbry.io/logo.png"
}
}
}
class ClaimTransactionTests(IntegrationTestCase):
VERBOSE = True
async def test_creating_updating_and_abandoning_claim(self):
address = self.account.get_least_used_receiving_address()
sendtxid = await self.lbrycrd.send_to_address(address.decode(), 9.0)
await self.lbrycrd.generate(1)
await self.on_transaction(sendtxid)
self.assertAlmostEqual(self.manager.get_balance(), 9, places=2)
claim = ClaimDict.load_dict(example_claim_dict)
tx = self.manager.claim_name(b'foo', 5, hexlify(claim.serialized))
await self.broadcast(tx)
await self.on_transaction(tx.id.decode())
await self.lbrycrd.generate(1)
await asyncio.sleep(2)
self.assertAlmostEqual(self.manager.get_balance(), 9, places=2)
await asyncio.sleep(2)
response = await self.resolve('lbry://foo')
print(response)
#class AbandonClaimLookup(IntegrationTestCase):
#
# async def skip_test_abandon_claim(self):
# address = yield self.lbry.wallet.get_least_used_address()
# yield self.lbrycrd.sendtoaddress(address, 0.0003 - 0.0000355)
# yield self.lbrycrd.generate(1)
# yield self.lbry.wallet.update_balance()
# yield threads.deferToThread(time.sleep, 5)
# print(self.lbry.wallet.get_balance())
# claim = yield self.lbry.wallet.claim_new_channel('@test', 0.000096)
# yield self.lbrycrd.generate(1)
# print('='*10 + 'CLAIM' + '='*10)
# print(claim)
# yield self.lbrycrd.decoderawtransaction(claim['tx'])
# abandon = yield self.lbry.wallet.abandon_claim(claim['claim_id'], claim['txid'], claim['nout'])
# print('='*10 + 'ABANDON' + '='*10)
# print(abandon)
# yield self.lbrycrd.decoderawtransaction(abandon['tx'])
# yield self.lbrycrd.generate(1)
# yield self.lbrycrd.getrawtransaction(abandon['txid'])
#
# yield self.lbry.wallet.update_balance()
# yield threads.deferToThread(time.sleep, 5)
# print('='*10 + 'FINAL BALANCE' + '='*10)
# print(self.lbry.wallet.get_balance())

View file

@ -38,25 +38,25 @@ class TestAccount(unittest.TestCase):
) )
self.assertEqual( self.assertEqual(
account.private_key.extended_key_string(), account.private_key.extended_key_string(),
'LprvXPsFZUGgrX1X9HiyxABZSf6hWJK7kHv4zGZRyyiHbBq5Wu94cE1DMvttnpLYReTPNW4eYwX9dWMvTz3PrB' b'LprvXPsFZUGgrX1X9HiyxABZSf6hWJK7kHv4zGZRyyiHbBq5Wu94cE1DMvttnpLYReTPNW4eYwX9dWMvTz3PrB'
'wwbRafEeA1ZXL69U2egM4QJdq' b'wwbRafEeA1ZXL69U2egM4QJdq'
) )
self.assertEqual( self.assertEqual(
account.public_key.extended_key_string(), account.public_key.extended_key_string(),
'Lpub2hkYkGHXktBhLpwUhKKogyuJ1M7Gt9EkjFTVKyDqZiZpWdhLuCoT1eKDfXfysMFfG4SzfXXcA2SsHzrjHK' b'Lpub2hkYkGHXktBhLpwUhKKogyuJ1M7Gt9EkjFTVKyDqZiZpWdhLuCoT1eKDfXfysMFfG4SzfXXcA2SsHzrjHK'
'Ea5aoCNRBAhjT5NPLV6hXtvEi' b'Ea5aoCNRBAhjT5NPLV6hXtvEi'
) )
self.assertEqual( self.assertEqual(
account.receiving_keys.generate_next_address(), account.receiving_keys.generate_next_address(),
'bCqJrLHdoiRqEZ1whFZ3WHNb33bP34SuGx' b'bCqJrLHdoiRqEZ1whFZ3WHNb33bP34SuGx'
) )
private_key = account.get_private_key_for_address('bCqJrLHdoiRqEZ1whFZ3WHNb33bP34SuGx') private_key = account.get_private_key_for_address(b'bCqJrLHdoiRqEZ1whFZ3WHNb33bP34SuGx')
self.assertEqual( self.assertEqual(
private_key.extended_key_string(), private_key.extended_key_string(),
'LprvXTnmVLXGKvRGo2ihBE6LJ771G3VVpAx2zhTJvjnx5P3h6iZ4VJX8PvwTcgzJZ1hqXX61Wpn4pQoP6n2wgp' b'LprvXTnmVLXGKvRGo2ihBE6LJ771G3VVpAx2zhTJvjnx5P3h6iZ4VJX8PvwTcgzJZ1hqXX61Wpn4pQoP6n2wgp'
'S8xjzCM6H2uGzCXuAMy5H9vtA' b'S8xjzCM6H2uGzCXuAMy5H9vtA'
) )
self.assertIsNone(account.get_private_key_for_address('BcQjRlhDOIrQez1WHfz3whnB33Bp34sUgX')) self.assertIsNone(account.get_private_key_for_address(b'BcQjRlhDOIrQez1WHfz3whnB33Bp34sUgX'))
def test_load_and_save_account(self): def test_load_and_save_account(self):
account_data = { account_data = {
@ -86,12 +86,12 @@ class TestAccount(unittest.TestCase):
self.assertEqual(len(account.receiving_keys.addresses), 2) self.assertEqual(len(account.receiving_keys.addresses), 2)
self.assertEqual( self.assertEqual(
account.receiving_keys.addresses[0], account.receiving_keys.addresses[0],
'bCqJrLHdoiRqEZ1whFZ3WHNb33bP34SuGx' b'bCqJrLHdoiRqEZ1whFZ3WHNb33bP34SuGx'
) )
self.assertEqual(len(account.change_keys.addresses), 1) self.assertEqual(len(account.change_keys.addresses), 1)
self.assertEqual( self.assertEqual(
account.change_keys.addresses[0], account.change_keys.addresses[0],
'bFpHENtqugKKHDshKFq2Mnb59Y2bx4vKgL' b'bFpHENtqugKKHDshKFq2Mnb59Y2bx4vKgL'
) )
account_data['coin'] = 'lbc_mainnet' account_data['coin'] = 'lbc_mainnet'

View file

@ -0,0 +1,69 @@
import shutil
import tempfile
from twisted.internet import defer
from twisted.trial import unittest
from lbrynet import conf
from lbrynet.database.storage import SQLiteStorage
from lbrynet.wallet.transaction import Transaction, Output, Input
from lbrynet.wallet.coin import LBC
from lbrynet.wallet.manager import LbryWalletManager
from torba.baseaccount import Account
from torba.wallet import Wallet
class LedgerTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
conf.initialize_settings(False)
self.db_dir = tempfile.mkdtemp()
self.storage = SQLiteStorage(self.db_dir)
yield self.storage.setup()
self.manager = LbryWalletManager(self.storage)
self.ledger = self.manager.get_or_create_ledger(LBC.get_id())
self.coin = LBC(self.ledger)
self.wallet = Wallet('Main', [self.coin], [Account.from_seed(
self.coin, u'carbon smart garage balance margin twelve chest sword toast envelope botto'
u'm stomach absent', u'lbryum'
)])
self.account = self.wallet.default_account
yield self.storage.add_account(self.account)
@defer.inlineCallbacks
def tearDown(self):
yield self.storage.stop()
shutil.rmtree(self.db_dir)
class BasicAccountingTests(LedgerTestCase):
@defer.inlineCallbacks
def test_empty_state(self):
balance = yield self.account.get_balance()
self.assertEqual(balance, 0)
@defer.inlineCallbacks
def test_balance(self):
tx = Transaction().add_outputs([Output.pay_pubkey_hash(100, b'abc1')])
yield self.storage.add_tx_output(self.account, tx.outputs[0])
balance = yield self.storage.get_balance_for_account(self.account)
self.assertEqual(balance, 100)
@defer.inlineCallbacks
def test_get_utxo(self):
tx1 = Transaction().add_outputs([Output.pay_pubkey_hash(100, b'abc1')])
txo = tx1.outputs[0]
yield self.storage.add_tx_output(self.account, txo)
balance = yield self.storage.get_balance_for_account(self.account)
self.assertEqual(balance, 100)
utxos = yield self.storage.get_utxos(self.account, Output)
self.assertEqual(len(utxos), 1)
txi = Transaction().add_inputs([Input.spend(txo)]).inputs[0]
yield self.storage.add_tx_input(self.account, txi)
balance = yield self.storage.get_balance_for_account(self.account)
self.assertEqual(balance, 0)
utxos = yield self.storage.get_utxos(self.account, Output)
self.assertEqual(len(utxos), 0)

View file

@ -37,10 +37,10 @@ class TestPayClaimNamePubkeyHash(unittest.TestCase):
# pub key # pub key
b'be16e4b0f9bd8f6d47d02b3a887049c36d3b84cb' b'be16e4b0f9bd8f6d47d02b3a887049c36d3b84cb'
), ),
'b504636174734cdc080110011a7808011230080410011a084d616361726f6e6922002a003214416c6c207' b'b504636174734cdc080110011a7808011230080410011a084d616361726f6e6922002a003214416c6c207'
'269676874732072657365727665642e38004a0052005a001a42080110011a30add80aaf02559ba0985363' b'269676874732072657365727665642e38004a0052005a001a42080110011a30add80aaf02559ba0985363'
'6a0658c42b727cb5bb4ba8acedb4b7fe656065a47a31878dbf9912135ddb9e13806cc1479d220a696d616' b'6a0658c42b727cb5bb4ba8acedb4b7fe656065a47a31878dbf9912135ddb9e13806cc1479d220a696d616'
'7652f6a7065672a5c080110031a404180cc0fa4d3839ee29cca866baed25fafb43fca1eb3b608ee889d35' b'7652f6a7065672a5c080110031a404180cc0fa4d3839ee29cca866baed25fafb43fca1eb3b608ee889d35'
'1d3573d042c7b83e2e643db0d8e062a04e6e9ae6b90540a2f95fe28638d0f18af4361a1c2214f73de93f4' b'1d3573d042c7b83e2e643db0d8e062a04e6e9ae6b90540a2f95fe28638d0f18af4361a1c2214f73de93f4'
'299fb32c32f949e02198a8e91101abd6d7576a914be16e4b0f9bd8f6d47d02b3a887049c36d3b84cb88ac' b'299fb32c32f949e02198a8e91101abd6d7576a914be16e4b0f9bd8f6d47d02b3a887049c36d3b84cb88ac'
) )

View file

@ -1,16 +1,16 @@
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from twisted.trial import unittest from twisted.trial import unittest
from torba.account import Account from torba.baseaccount import Account
from torba.constants import CENT, COIN from torba.constants import CENT, COIN
from torba.wallet import Wallet from torba.wallet import Wallet
from torba.basetransaction import NULL_HASH
from lbrynet.wallet.coin import LBC from lbrynet.wallet.coin import LBC
from lbrynet.wallet.transaction import Transaction, Output, Input from lbrynet.wallet.transaction import Transaction, Output, Input
from lbrynet.wallet.manager import LbryWalletManager from lbrynet.wallet.manager import LbryWalletManager
NULL_HASH = '\x00'*32
FEE_PER_BYTE = 50 FEE_PER_BYTE = 50
FEE_PER_CHAR = 200000 FEE_PER_CHAR = 200000
@ -31,7 +31,7 @@ def get_transaction(txo=None):
.add_outputs([txo or Output.pay_pubkey_hash(CENT, NULL_HASH)]) .add_outputs([txo or Output.pay_pubkey_hash(CENT, NULL_HASH)])
def get_claim_transaction(claim_name, claim=''): def get_claim_transaction(claim_name, claim=b''):
return get_transaction( return get_transaction(
Output.pay_claim_name_pubkey_hash(CENT, claim_name, claim, NULL_HASH) Output.pay_claim_name_pubkey_hash(CENT, claim_name, claim, NULL_HASH)
) )
@ -70,15 +70,15 @@ class TestSizeAndFeeEstimation(unittest.TestCase):
def test_claim_name_transaction_size_and_fee(self): def test_claim_name_transaction_size_and_fee(self):
# fee based on claim name is the larger fee # fee based on claim name is the larger fee
claim_name = 'verylongname' claim_name = b'verylongname'
tx = get_claim_transaction(claim_name, '0'*4000) tx = get_claim_transaction(claim_name, b'0'*4000)
base_size = tx.size - 1 - tx.inputs[0].size base_size = tx.size - 1 - tx.inputs[0].size
self.assertEqual(tx.size, 4225) self.assertEqual(tx.size, 4225)
self.assertEqual(tx.base_size, base_size) self.assertEqual(tx.base_size, base_size)
self.assertEqual(self.coin.get_transaction_base_fee(tx), len(claim_name) * FEE_PER_CHAR) self.assertEqual(self.coin.get_transaction_base_fee(tx), len(claim_name) * FEE_PER_CHAR)
# fee based on total bytes is the larger fee # fee based on total bytes is the larger fee
claim_name = 'a' claim_name = b'a'
tx = get_claim_transaction(claim_name, '0'*4000) tx = get_claim_transaction(claim_name, b'0'*4000)
base_size = tx.size - 1 - tx.inputs[0].size base_size = tx.size - 1 - tx.inputs[0].size
self.assertEqual(tx.size, 4214) self.assertEqual(tx.size, 4214)
self.assertEqual(tx.base_size, base_size) self.assertEqual(tx.base_size, base_size)
@ -168,7 +168,7 @@ class TestTransactionSerialization(unittest.TestCase):
"00001976a914f521178feb733a719964e1da4a9efb09dcc39cfa88ac00000000" "00001976a914f521178feb733a719964e1da4a9efb09dcc39cfa88ac00000000"
) )
tx = Transaction(raw) tx = Transaction(raw)
self.assertEqual(hexlify(tx.id), b'666c3d15de1d6949a4fe717126c368e274b36957dce29fd401138c1e87e92a62') self.assertEqual(tx.id, b'666c3d15de1d6949a4fe717126c368e274b36957dce29fd401138c1e87e92a62')
self.assertEqual(tx.version, 1) self.assertEqual(tx.version, 1)
self.assertEqual(tx.locktime, 0) self.assertEqual(tx.locktime, 0)
self.assertEqual(len(tx.inputs), 1) self.assertEqual(len(tx.inputs), 1)
@ -238,11 +238,10 @@ class TestTransactionSigning(unittest.TestCase):
pubkey_hash2 = account.coin.address_to_hash160(address2) pubkey_hash2 = account.coin.address_to_hash160(address2)
tx = Transaction() \ tx = Transaction() \
.add_inputs([Input.spend(get_output(2*COIN, pubkey_hash1))]) \ .add_inputs([Input.spend(get_output(int(2*COIN), pubkey_hash1))]) \
.add_outputs([Output.pay_pubkey_hash(1.9*COIN, pubkey_hash2)]) \ .add_outputs([Output.pay_pubkey_hash(int(1.9*COIN), pubkey_hash2)]) \
.sign(account) .sign(account)
print(hexlify(tx.inputs[0].script.values['signature']))
self.assertEqual( self.assertEqual(
hexlify(tx.inputs[0].script.values['signature']), hexlify(tx.inputs[0].script.values['signature']),
b'304402200dafa26ad7cf38c5a971c8a25ce7d85a076235f146126762296b1223c42ae21e022020ef9eeb8' b'304402200dafa26ad7cf38c5a971c8a25ce7d85a076235f146126762296b1223c42ae21e022020ef9eeb8'

View file

@ -1,2 +1,4 @@
__coin__ = 'LBC'
from .coin import LBC, LBCRegTest from .coin import LBC, LBCRegTest
from .manager import LbryWalletManager from .manager import LbryWalletManager

View file

@ -1,67 +0,0 @@
from six import int2byte
from binascii import unhexlify
from torba.basecoin import BaseCoin
from .ledger import MainNetLedger, TestNetLedger, RegTestLedger
from .transaction import Transaction
class LBC(BaseCoin):
name = 'LBRY Credits'
symbol = 'LBC'
network = 'mainnet'
ledger_class = MainNetLedger
transaction_class = Transaction
secret_prefix = int2byte(0x1c)
pubkey_address_prefix = int2byte(0x55)
script_address_prefix = int2byte(0x7a)
extended_public_key_prefix = unhexlify('019c354f')
extended_private_key_prefix = unhexlify('019c3118')
default_fee_per_byte = 50
default_fee_per_name_char = 200000
def __init__(self, ledger, fee_per_byte=default_fee_per_byte,
fee_per_name_char=default_fee_per_name_char):
super(LBC, self).__init__(ledger, fee_per_byte)
self.fee_per_name_char = fee_per_name_char
def to_dict(self):
coin_dict = super(LBC, self).to_dict()
coin_dict['fee_per_name_char'] = self.fee_per_name_char
return coin_dict
def get_transaction_base_fee(self, tx):
""" Fee for the transaction header and all outputs; without inputs. """
return max(
super(LBC, self).get_transaction_base_fee(tx),
self.get_transaction_claim_name_fee(tx)
)
def get_transaction_claim_name_fee(self, tx):
fee = 0
for output in tx.outputs:
if output.script.is_claim_name:
fee += len(output.script.values['claim_name']) * self.fee_per_name_char
return fee
class LBCTestNet(LBC):
network = 'testnet'
ledger_class = TestNetLedger
pubkey_address_prefix = int2byte(111)
script_address_prefix = int2byte(196)
extended_public_key_prefix = unhexlify('043587cf')
extended_private_key_prefix = unhexlify('04358394')
class LBCRegTest(LBC):
network = 'regtest'
ledger_class = RegTestLedger
pubkey_address_prefix = int2byte(111)
script_address_prefix = int2byte(196)
extended_public_key_prefix = unhexlify('043587cf')
extended_private_key_prefix = unhexlify('04358394')

View file

@ -0,0 +1,11 @@
from torba.basedatabase import BaseDatabase
class WalletDatabase(BaseDatabase):
CREATE_TABLES_QUERY = (
BaseDatabase.CREATE_TX_TABLE +
BaseDatabase.CREATE_PUBKEY_ADDRESS_TABLE +
BaseDatabase.CREATE_TXO_TABLE +
BaseDatabase.CREATE_TXI_TABLE
)

View file

@ -1,28 +1,157 @@
import struct
from six import int2byte
from binascii import unhexlify
from torba.baseledger import BaseLedger from torba.baseledger import BaseLedger
from torba.baseheader import BaseHeaders, _ArithUint256
from torba.util import int_to_hex, rev_hex, hash_encode
from .network import Network from .network import Network
from .transaction import Transaction
from .database import WalletDatabase
class LBCLedger(BaseLedger): class Headers(BaseHeaders):
network_class = Network
header_size = 112 header_size = 112
@staticmethod
def _serialize(header):
return b''.join([
int_to_hex(header['version'], 4),
rev_hex(header['prev_block_hash']),
rev_hex(header['merkle_root']),
rev_hex(header['claim_trie_root']),
int_to_hex(int(header['timestamp']), 4),
int_to_hex(int(header['bits']), 4),
int_to_hex(int(header['nonce']), 4)
])
@staticmethod
def _deserialize(height, header):
version, = struct.unpack('<I', header[:4])
timestamp, bits, nonce = struct.unpack('<III', header[100:112])
return {
'version': version,
'prev_block_hash': hash_encode(header[4:36]),
'merkle_root': hash_encode(header[36:68]),
'claim_trie_root': hash_encode(header[68:100]),
'timestamp': timestamp,
'bits': bits,
'nonce': nonce,
'block_height': height,
}
def _calculate_next_work_required(self, height, first, last):
""" See: lbrycrd/src/lbry.cpp """
if height == 0:
return self.ledger.genesis_bits, self.ledger.max_target
if self.verify_bits_to_target:
bits = last['bits']
bitsN = (bits >> 24) & 0xff
assert 0x03 <= bitsN <= 0x1f, \
"First part of bits should be in [0x03, 0x1d], but it was {}".format(hex(bitsN))
bitsBase = bits & 0xffffff
assert 0x8000 <= bitsBase <= 0x7fffff, \
"Second part of bits should be in [0x8000, 0x7fffff] but it was {}".format(bitsBase)
# new target
retargetTimespan = self.ledger.target_timespan
nActualTimespan = last['timestamp'] - first['timestamp']
nModulatedTimespan = retargetTimespan + (nActualTimespan - retargetTimespan) // 8
nMinTimespan = retargetTimespan - (retargetTimespan // 8)
nMaxTimespan = retargetTimespan + (retargetTimespan // 2)
# Limit adjustment step
if nModulatedTimespan < nMinTimespan:
nModulatedTimespan = nMinTimespan
elif nModulatedTimespan > nMaxTimespan:
nModulatedTimespan = nMaxTimespan
# Retarget
bnPowLimit = _ArithUint256(self.ledger.max_target)
bnNew = _ArithUint256.SetCompact(last['bits'])
bnNew *= nModulatedTimespan
bnNew //= nModulatedTimespan
if bnNew > bnPowLimit:
bnNew = bnPowLimit
return bnNew.GetCompact(), bnNew._value
class Ledger(BaseLedger):
name = 'LBRY Credits'
symbol = 'LBC'
database_class = WalletDatabase
headers_class = Headers
network_class = Network
transaction_class = Transaction
default_fee_per_byte = 50
default_fee_per_name_char = 200000
def __init__(self, *args, **kwargs):
super(Ledger, self).__init__(*args, **kwargs)
self.fee_per_name_char = self.config.get('fee_per_name_char', self.default_fee_per_name_char)
def get_transaction_base_fee(self, tx):
""" Fee for the transaction header and all outputs; without inputs. """
return max(
super(Ledger, self).get_transaction_base_fee(tx),
self.get_transaction_claim_name_fee(tx)
)
def get_transaction_claim_name_fee(self, tx):
fee = 0
for output in tx.outputs:
if output.script.is_claim_name:
fee += len(output.script.values['claim_name']) * self.fee_per_name_char
return fee
def resolve(self, *uris):
return self.network.get_values_for_uris(*uris)
class MainNetLedger(Ledger):
network_name = 'mainnet'
secret_prefix = int2byte(0x1c)
pubkey_address_prefix = int2byte(0x55)
script_address_prefix = int2byte(0x7a)
extended_public_key_prefix = unhexlify('019c354f')
extended_private_key_prefix = unhexlify('019c3118')
max_target = 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff max_target = 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
genesis_hash = '9c89283ba0f3227f6c03b70216b9f665f0118d5e0fa729cedf4fb34d6a34f463' genesis_hash = '9c89283ba0f3227f6c03b70216b9f665f0118d5e0fa729cedf4fb34d6a34f463'
genesis_bits = 0x1f00ffff genesis_bits = 0x1f00ffff
target_timespan = 150 target_timespan = 150
class MainNetLedger(LBCLedger): class TestNetLedger(Ledger):
pass network_name = 'testnet'
pubkey_address_prefix = int2byte(111)
script_address_prefix = int2byte(196)
extended_public_key_prefix = unhexlify('043587cf')
extended_private_key_prefix = unhexlify('04358394')
class TestNetLedger(LBCLedger): class UnverifiedHeaders(Headers):
pass verify_bits_to_target = False
class RegTestLedger(LBCLedger): class RegTestLedger(Ledger):
network_name = 'regtest'
headers_class = UnverifiedHeaders
pubkey_address_prefix = int2byte(111)
script_address_prefix = int2byte(196)
extended_public_key_prefix = unhexlify('043587cf')
extended_private_key_prefix = unhexlify('04358394')
max_target = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff max_target = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
genesis_hash = '6e3fcf1299d4ec5d79c3a4c91d624a4acf9e2e173d95a1a0504f677669687556' genesis_hash = '6e3fcf1299d4ec5d79c3a4c91d624a4acf9e2e173d95a1a0504f677669687556'
genesis_bits = 0x207fffff genesis_bits = 0x207fffff
target_timespan = 1 target_timespan = 1
verify_bits_to_target = False

View file

@ -1,9 +1,15 @@
import os import os
from twisted.internet import defer from twisted.internet import defer
from lbrynet.database.storage import SQLiteStorage
from torba.basetransaction import NULL_HASH
from torba.constants import COIN from torba.constants import COIN
from torba.coinselection import CoinSelector
from torba.manager import WalletManager as BaseWalletManager from torba.manager import WalletManager as BaseWalletManager
from .transaction import Transaction, Output, Input
class BackwardsCompatibleNetwork: class BackwardsCompatibleNetwork:
def __init__(self, manager): def __init__(self, manager):
@ -18,6 +24,15 @@ class BackwardsCompatibleNetwork:
class LbryWalletManager(BaseWalletManager): class LbryWalletManager(BaseWalletManager):
def __init__(self, db, **kwargs):
super(LbryWalletManager, self).__init__(**kwargs)
self.db = db # type: SQLiteStorage
def create_ledger(self, ledger_class, *args, **kwargs):
if issubclass(ledger_class.database_class, self.db.__class__):
return ledger_class(*args, db=self.db, **kwargs)
return super(LbryWalletManager, self).create_ledger(ledger_class, *args, **kwargs)
@property @property
def wallet(self): def wallet(self):
return self return self
@ -54,15 +69,6 @@ class LbryWalletManager(BaseWalletManager):
) )
return wallet_manager return wallet_manager
def start(self):
return self.start_ledgers()
def stop(self):
return self.stop_ledgers()
def get_balance(self):
return float(self.default_account.get_balance()) / float(COIN)
def get_best_blockhash(self): def get_best_blockhash(self):
return defer.succeed('') return defer.succeed('')
@ -86,8 +92,9 @@ class LbryWalletManager(BaseWalletManager):
def get_info_exchanger(self): def get_info_exchanger(self):
return LBRYcrdAddressRequester(self) return LBRYcrdAddressRequester(self)
def resolve(self, *uris, **kwargs): def resolve(self, *uris):
return defer.succeed({}) ledger = self.default_account.coin.ledger # type: LBCLedger
return ledger.resolve(uris)
def get_name_claims(self): def get_name_claims(self):
return defer.succeed([]) return defer.succeed([])
@ -98,6 +105,46 @@ class LbryWalletManager(BaseWalletManager):
def get_history(self): def get_history(self):
return defer.succeed([]) return defer.succeed([])
def claim_name(self, name, amount, claim):
amount = int(amount * COIN)
account = self.default_account
coin = account.coin
ledger = coin.ledger
estimators = [
txo.get_estimator(coin) for txo in ledger.get_unspent_outputs()
]
cost_of_output = coin.get_input_output_fee(
Output.pay_pubkey_hash(COIN, NULL_HASH)
)
selector = CoinSelector(estimators, amount, cost_of_output)
spendables = selector.select()
if not spendables:
raise ValueError('Not enough funds to cover this transaction.')
claim_address = account.get_least_used_receiving_address()
outputs = [
Output.pay_claim_name_pubkey_hash(
amount, name, claim, coin.address_to_hash160(claim_address)
)
]
spent_sum = sum(s.effective_amount for s in spendables)
if spent_sum > amount:
change_address = account.get_least_used_change_address()
change_hash160 = coin.address_to_hash160(change_address)
outputs.append(Output.pay_pubkey_hash(spent_sum - amount, change_hash160))
tx = Transaction() \
.add_inputs([s.txi for s in spendables]) \
.add_outputs(outputs) \
.sign(account)
return tx
class ReservedPoints: class ReservedPoints:
def __init__(self, identifier, amount): def __init__(self, identifier, amount):

View file

@ -2,4 +2,6 @@ from torba.basenetwork import BaseNetwork
class Network(BaseNetwork): class Network(BaseNetwork):
pass
def get_values_for_uris(self, uris):
return self.rpc('blockchain.claimtrie.getvaluesforuris', uris)

View file

@ -1,80 +0,0 @@
from torba.basescript import BaseInputScript, BaseOutputScript, Template
from torba.basescript import PUSH_SINGLE, OP_DROP, OP_2DROP
class InputScript(BaseInputScript):
pass
class OutputScript(BaseOutputScript):
# lbry custom opcodes
OP_CLAIM_NAME = 0xb5
OP_SUPPORT_CLAIM = 0xb6
OP_UPDATE_CLAIM = 0xb7
CLAIM_NAME_OPCODES = (
OP_CLAIM_NAME, PUSH_SINGLE('claim_name'), PUSH_SINGLE('claim'),
OP_2DROP, OP_DROP
)
CLAIM_NAME_PUBKEY = Template('claim_name+pay_pubkey_hash', (
CLAIM_NAME_OPCODES + BaseOutputScript.PAY_PUBKEY_HASH.opcodes
))
CLAIM_NAME_SCRIPT = Template('claim_name+pay_script_hash', (
CLAIM_NAME_OPCODES + BaseOutputScript.PAY_SCRIPT_HASH.opcodes
))
SUPPORT_CLAIM_OPCODES = (
OP_SUPPORT_CLAIM, PUSH_SINGLE('claim_name'), PUSH_SINGLE('claim_id'),
OP_2DROP, OP_DROP
)
SUPPORT_CLAIM_PUBKEY = Template('support_claim+pay_pubkey_hash', (
SUPPORT_CLAIM_OPCODES + BaseOutputScript.PAY_PUBKEY_HASH.opcodes
))
SUPPORT_CLAIM_SCRIPT = Template('support_claim+pay_script_hash', (
SUPPORT_CLAIM_OPCODES + BaseOutputScript.PAY_SCRIPT_HASH.opcodes
))
UPDATE_CLAIM_OPCODES = (
OP_UPDATE_CLAIM, PUSH_SINGLE('claim_name'), PUSH_SINGLE('claim_id'), PUSH_SINGLE('claim'),
OP_2DROP, OP_2DROP
)
UPDATE_CLAIM_PUBKEY = Template('update_claim+pay_pubkey_hash', (
UPDATE_CLAIM_OPCODES + BaseOutputScript.PAY_PUBKEY_HASH.opcodes
))
UPDATE_CLAIM_SCRIPT = Template('update_claim+pay_script_hash', (
UPDATE_CLAIM_OPCODES + BaseOutputScript.PAY_SCRIPT_HASH.opcodes
))
templates = BaseOutputScript.templates + [
CLAIM_NAME_PUBKEY,
CLAIM_NAME_SCRIPT,
SUPPORT_CLAIM_PUBKEY,
SUPPORT_CLAIM_SCRIPT,
UPDATE_CLAIM_PUBKEY,
UPDATE_CLAIM_SCRIPT
]
@classmethod
def pay_claim_name_pubkey_hash(cls, claim_name, claim, pubkey_hash):
return cls(template=cls.CLAIM_NAME_PUBKEY, values={
'claim_name': claim_name,
'claim': claim,
'pubkey_hash': pubkey_hash
})
@property
def is_claim_name(self):
return self.template.name.startswith('claim_name+')
@property
def is_support_claim(self):
return self.template.name.startswith('support_claim+')
@property
def is_update_claim(self):
return self.template.name.startswith('update_claim+')
@property
def is_claim_involved(self):
return self.is_claim_name or self.is_support_claim or self.is_update_claim

View file

@ -1,34 +0,0 @@
import struct
from torba.basetransaction import BaseTransaction, BaseInput, BaseOutput
from torba.hash import hash160
from .script import InputScript, OutputScript
def claim_id_hash(txid, n):
return hash160(txid + struct.pack('>I', n))
class Input(BaseInput):
script_class = InputScript
class Output(BaseOutput):
script_class = OutputScript
@classmethod
def pay_claim_name_pubkey_hash(cls, amount, claim_name, claim, pubkey_hash):
script = cls.script_class.pay_claim_name_pubkey_hash(claim_name, claim, pubkey_hash)
return cls(amount, script)
class Transaction(BaseTransaction):
input_class = Input
output_class = Output
def get_claim_id(self, output_index):
output = self._outputs[output_index]
assert output.script.is_claim_name(), 'Not a name claim.'
return claim_id_hash(self.hash, output_index)