Merge pull request #3572 from orblivion/json-schema
Add wallet json-schema, validate in one test.
This commit is contained in:
commit
42b2dbd92e
4 changed files with 204 additions and 1 deletions
|
@ -2,4 +2,5 @@ build:
|
||||||
rm types/v2/* -rf
|
rm types/v2/* -rf
|
||||||
touch types/v2/__init__.py
|
touch types/v2/__init__.py
|
||||||
cd types/v2/ && protoc --python_out=. -I ../../../../../types/v2/proto/ ../../../../../types/v2/proto/*.proto
|
cd types/v2/ && protoc --python_out=. -I ../../../../../types/v2/proto/ ../../../../../types/v2/proto/*.proto
|
||||||
|
cd types/v2/ && cp ../../../../../types/jsonschema/* ./
|
||||||
sed -e 's/^import\ \(.*\)_pb2\ /from . import\ \1_pb2\ /g' -i types/v2/*.py
|
sed -e 's/^import\ \(.*\)_pb2\ /from . import\ \1_pb2\ /g' -i types/v2/*.py
|
||||||
|
|
139
lbry/schema/types/v2/wallet.json
Normal file
139
lbry/schema/types/v2/wallet.json
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
{
|
||||||
|
"title": "Wallet",
|
||||||
|
"description": "An LBC wallet",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["name", "version", "accounts", "preferences"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Human readable name for this wallet",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"description": "Wallet spec version",
|
||||||
|
"type": "integer",
|
||||||
|
"$comment": "Should this be a string? We may need some sort of decimal type if we want exact decimal versions."
|
||||||
|
},
|
||||||
|
"accounts": {
|
||||||
|
"description": "Accounts associated with this wallet",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["address_generator", "certificates", "encrypted", "ledger", "modified_on", "name", "private_key", "public_key", "seed"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"address_generator": {
|
||||||
|
"description": "Higher level manager of either singular or deterministically generated addresses",
|
||||||
|
"type": "object",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"required": ["name", "change", "receiving"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "type of address generator: a deterministic chain of addresses",
|
||||||
|
"enum": ["deterministic-chain"],
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"change": {
|
||||||
|
"$ref": "#/$defs/address_manager",
|
||||||
|
"description": "Manager for deterministically generated change address (not used for single address)"
|
||||||
|
},
|
||||||
|
"receiving": {
|
||||||
|
"$ref": "#/$defs/address_manager",
|
||||||
|
"description": "Manager for deterministically generated receiving address (not used for single address)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"required": ["name"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "type of address generator: a single address",
|
||||||
|
"enum": ["single-address"],
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"certificates": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Channel keys. Mapping from public key address to pem-formatted private key.",
|
||||||
|
"additionalProperties": {"type": "string"}
|
||||||
|
},
|
||||||
|
"encrypted": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether private key and seed are encrypted with a password"
|
||||||
|
},
|
||||||
|
"ledger": {
|
||||||
|
"description": "Which network to use",
|
||||||
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
"lbc_mainnet",
|
||||||
|
"lbc_testnet"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"modified_on": {
|
||||||
|
"description": "last modified time in Unix Time",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"description": "Name for account, possibly human readable",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"private_key": {
|
||||||
|
"description": "Private key for address if `address_generator` is a single address. Root of chain of private keys for addresses if `address_generator` is a deterministic chain of addresses. Encrypted if `encrypted` is true.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"public_key": {
|
||||||
|
"description": "Public key for address if `address_generator` is a single address. Root of chain of public keys for addresses if `address_generator` is a deterministic chain of addresses.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"seed": {
|
||||||
|
"description": "Human readable representation of `private_key`. encrypted if `encrypted` is set to `true`",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"preferences": {
|
||||||
|
"description": "Timestamped application-level preferences. Values can be objects or of a primitive type.",
|
||||||
|
"$comment": "enable-sync is seen in example wallet. encrypt-on-disk is seen in example wallet. they both have a boolean `value` field. Do we want them explicitly defined here? local and shared seem to have at least a similar structure (type, value [yes, again], version), value being the free-form part. Should we define those here? Or can there be any key under preferences, and `value` be literally be anything in any form?",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["ts", "value"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"ts": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "When the item was set, in Unix time format.",
|
||||||
|
"$comment": "Do we want a string (decimal)?"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"$comment": "Sometimes this has been an object, sometimes just a boolean. I don't want to prescribe anything."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"address_manager": {
|
||||||
|
"description": "Manager for deterministically generated addresses",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["gap", "maximum_uses_per_address"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"gap": {
|
||||||
|
"description": "Maximum allowed consecutive generated addresses with no transactions",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"maximum_uses_per_address": {
|
||||||
|
"description": "Maximum number of uses for each generated address",
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
setup.py
5
setup.py
|
@ -62,7 +62,10 @@ setup(
|
||||||
extras_require={
|
extras_require={
|
||||||
'torrent': ['lbry-libtorrent'],
|
'torrent': ['lbry-libtorrent'],
|
||||||
'lint': ['pylint==2.10.0'],
|
'lint': ['pylint==2.10.0'],
|
||||||
'test': ['coverage'],
|
'test': [
|
||||||
|
'coverage',
|
||||||
|
'jsonschema==4.4.0',
|
||||||
|
],
|
||||||
'scribe': ['scribe @ git+https://github.com/lbryio/scribe.git'],
|
'scribe': ['scribe @ git+https://github.com/lbryio/scribe.git'],
|
||||||
},
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
import json
|
||||||
|
import jsonschema
|
||||||
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
|
||||||
|
import lbry.schema.types.v2 as schema_v2
|
||||||
from unittest import TestCase, mock
|
from unittest import TestCase, mock
|
||||||
from lbry.testcase import AsyncioTestCase
|
from lbry.testcase import AsyncioTestCase
|
||||||
from lbry.wallet import (
|
from lbry.wallet import (
|
||||||
|
@ -74,6 +78,62 @@ class TestWalletCreation(AsyncioTestCase):
|
||||||
decrypted = Wallet.unpack('password', encrypted)
|
decrypted = Wallet.unpack('password', encrypted)
|
||||||
self.assertEqual(decrypted['accounts'][0]['name'], 'An Account')
|
self.assertEqual(decrypted['accounts'][0]['name'], 'An Account')
|
||||||
|
|
||||||
|
def test_wallet_file_schema(self):
|
||||||
|
wallet_dict = {
|
||||||
|
'version': 1,
|
||||||
|
'name': 'Main Wallet',
|
||||||
|
'preferences': {},
|
||||||
|
'accounts': [
|
||||||
|
{
|
||||||
|
'certificates': {'x': 'y'},
|
||||||
|
'name': 'Account 1',
|
||||||
|
'ledger': 'lbc_mainnet',
|
||||||
|
'modified_on': 123,
|
||||||
|
'seed':
|
||||||
|
"carbon smart garage balance margin twelve chest sword toast envelope bottom stomac"
|
||||||
|
"h absent",
|
||||||
|
'encrypted': False,
|
||||||
|
'private_key':
|
||||||
|
'xprv9s21ZrQH143K42ovpZygnjfHdAqSd9jo7zceDfPRogM7bkkoNVv7'
|
||||||
|
'DRNLEoB8HoirMgH969NrgL8jNzLEegqFzPRWM37GXd4uE8uuRkx4LAe',
|
||||||
|
'public_key':
|
||||||
|
'xpub661MyMwAqRbcGWtPvbWh9sc2BCfw2cTeVDYF23o3N1t6UZ5wv3EMm'
|
||||||
|
'Dgp66FxHuDtWdft3B5eL5xQtyzAtkdmhhC95gjRjLzSTdkho95asu9',
|
||||||
|
'address_generator': {
|
||||||
|
'name': 'deterministic-chain',
|
||||||
|
'receiving': {'gap': 17, 'maximum_uses_per_address': 3},
|
||||||
|
'change': {'gap': 10, 'maximum_uses_per_address': 3}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'certificates': {'a': 'b'},
|
||||||
|
'name': 'Account 2',
|
||||||
|
'ledger': 'lbc_mainnet',
|
||||||
|
'modified_on': 123,
|
||||||
|
'seed':
|
||||||
|
"carbon smart garage balance margin twelve chest sword toast envelope bottom stomac"
|
||||||
|
"h absent",
|
||||||
|
'encrypted': True,
|
||||||
|
'private_key':
|
||||||
|
'xprv9s21ZrQH143K42ovpZygnjfHdAqSd9jo7zceDfPRogM7bkkoNVv7'
|
||||||
|
'DRNLEoB8HoirMgH969NrgL8jNzLEegqFzPRWM37GXd4uE8uuRkx4LAe',
|
||||||
|
'public_key':
|
||||||
|
'xpub661MyMwAqRbcGWtPvbWh9sc2BCfw2cTeVDYF23o3N1t6UZ5wv3EMm'
|
||||||
|
'Dgp66FxHuDtWdft3B5eL5xQtyzAtkdmhhC95gjRjLzSTdkho95asu9',
|
||||||
|
'address_generator': {
|
||||||
|
'name': 'single-address',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
storage = WalletStorage(default=wallet_dict)
|
||||||
|
wallet = Wallet.from_storage(storage, self.manager)
|
||||||
|
self.assertDictEqual(wallet_dict, wallet.to_dict())
|
||||||
|
with open(os.path.join(*schema_v2.__path__, 'wallet.json')) as f:
|
||||||
|
wallet_schema = json.load(f)
|
||||||
|
jsonschema.validate(schema=wallet_schema, instance=wallet.to_dict())
|
||||||
|
|
||||||
def test_no_password_but_encryption_preferred(self):
|
def test_no_password_but_encryption_preferred(self):
|
||||||
wallet_dict = {
|
wallet_dict = {
|
||||||
'version': 1,
|
'version': 1,
|
||||||
|
|
Loading…
Reference in a new issue