WIP: multi-device storage test

This commit is contained in:
Jude Nelson
2017-02-20 21:12:12 -05:00
parent 6c36b50690
commit 059000cad4

View File

@@ -0,0 +1,368 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Blockstack
~~~~~
copyright: (c) 2014-2015 by Halfmoon Labs, Inc.
copyright: (c) 2016 by Blockstack.org
This file is part of Blockstack
Blockstack is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Blockstack is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Blockstack. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
os.environ["CLIENT_STORAGE_DRIVERS"] = "blockstack_server,disk"
os.environ["CLIENT_STORAGE_DRIVERS_REQUIRED_WRITE"] = "blockstack_server"
import testlib
import pybitcoin
import json
import time
import blockstack_client
import xmlrpclib
import blockstack
import traceback
import blockstack_zones
import base64
# activate multisig
"""
TEST ENV BLOCKSTACK_EPOCH_1_END_BLOCK 250
TEST ENV BLOCKSTACK_EPOCH_2_NAMESPACE_LIFETIME_MULTIPLIER 1
"""
wallets = [
testlib.Wallet( "5JesPiN68qt44Hc2nT8qmyZ1JDwHebfoh9KQ52Lazb1m1LaKNj9", 100000000000 ),
testlib.Wallet( "5KHqsiU9qa77frZb6hQy9ocV7Sus9RWJcQGYYBJJBb2Efj1o77e", 100000000000 ),
testlib.Wallet( "5JesPiN68qt44Hc2nT8qmyZ1JDwHebfoh9KQ52Lazb1m1LaKNj9", 100000000000 ),
testlib.Wallet( "5KHqsiU9qa77frZb6hQy9ocV7Sus9RWJcQGYYBJJBb2Efj1o77e", 100000000000 ),
testlib.Wallet( "5Kg5kJbQHvk1B64rJniEmgbD83FpZpbw2RjdAZEzTefs9ihN3Bz", 100000000000 ),
testlib.Wallet( "5JuVsoS9NauksSkqEjbUZxWwgGDQbMwPsEfoRBSpLpgDX1RtLX7", 100000000000 ),
testlib.Wallet( "5KEpiSRr1BrT8vRD7LKGCEmudokTh1iMHbiThMQpLdwBwhDJB1T", 100000000000 ),
testlib.Wallet( "5K9LmMQskQ9jP1p7dyieLDAeB6vsAj4GK8dmGNJAXS1qHDqnWhP", 100000000000 ),
testlib.Wallet( "5KcNen67ERBuvz2f649t9F2o1ddTjC5pVUEqcMtbxNgHqgxG2gZ", 100000000000 ),
testlib.Wallet( "5J5uAKL8s62hddganFJaCkWi3Me7PFoc7fks9hAzjtWG1NDjmUK", 100000000000 ),
testlib.MultisigWallet(2, "5JfHdMq9XnZ9mwW5H6LsfVCn9u6iGAj2FCVYtfhcHn72Tphvm5P", "5JaqLZaKD7cgkfsxSZBNiu6gaFxo1XAiTXw1mhtatipNNCtZBZG", "5KNsAkiHRDZb5Yyedxov2Fncr6CcNPV52yqJbzQ8M2W6dkg2qJp"),
testlib.MultisigWallet(2, "5JPrkpfLT3rDf1Lgm1DpA2Cfepwf9wCtEbDx1HSEdd4J2R5YMxZ", "5JiALcfvzFsKcvLnHf7ECgLdp6FxbcAXB1GPvEYP7HeigbDCQ9E", "5KScD5XL5Hj83Yjvm3u4HD78vSYwFRyq9StTLPnrWrCTGiqTvVP"),
testlib.MultisigWallet(2, '5JPR5iVN8KGMdU9JfzoTCsipXazUcZPRY8zp7f3g8FRff2HBaAV', '5KTTwEyATY8v12MjNdoeA1u2ZGqgjnBNcyZjk3YSkiVJWYxqBSm', '5KQ1s8UEYz3oyFRUejBvb1imMdtpoP98w6NQYGxQsSo3u6DmztZ'),
testlib.MultisigWallet(2, '5JpAkdEJuzF8E74UptksRLiB6Bf9QnwxGQutJTRWo5EAGVZfXmY', '5Hyc4wreVpZyzcfb56Zt1ymovda2xGucGZsAwoQz34iYK6aEKhR', '5JypKiQGiaD8AN6X86xtnuQYj7nnpLvp4VfcTVdDh4yFkLewAGx'),
testlib.MultisigWallet(2, '5KXzk8m7sfVEciwwtb5DTBNMrHFBgn8wEWtfyi3KPNjQazWyF3e', '5JDy1qYj2no1SSXMsn8suPP6gMVofLjCR5Qfz44KB2VM6Kd2EKq', '5KBpBME2Rk7gjxoYxaB1JBrumS9zk5U6GYNRyG6BX4KzP8aovwP'),
testlib.MultisigWallet(2, '5HqVVmKy1bqP8qZrTpk9akjzEyrC1N7JjcDD2nwaWdydBz7VjfJ', '5JT4QqGNqdvpxys7SbxDqeQYmfq24u4K8cMUsVMcNQLAXRwrZnR', '5KBiBDzJBM7V63UCwE5M6P4HbXFCzc6GaeYW4n7orhLmxFNmRrE'),
testlib.MultisigWallet(2, '5KNTRpgjo81QoM7tdWZerzgiWagDoaHJiPV5vFeLuSVUAGWgHsS', '5JRwonrfqvr7Z4aBKLzUpZh1cdSk52n5WdS2G28Yr2WCEXjtrqN', '5JGoa62nDaFwEMp4tianJqLcEQ4eaHtu4UU3AoPxfgeP54SR2CA'),
testlib.MultisigWallet(2, '5KDJe3ptRA1HP6HRwNpswSM3DMACMvCYbojrLXp4Zu7T98gs8ir', '5JE9Luhjrb9SVZk29AM4j3ns9zUeGs4Xuau9o3EB7QyKzerSnwH', '5JvC3DN6MnFJHHq5Hpisv47KSeCaFxGfBso6y8pMpvjb6AjPiL2'),
testlib.MultisigWallet(2, '5JHD24XrcDhcKvEXiviwpVzFuCqm9WL1aRf25iXDUj3f8ecPtKr', '5Jwv4w9RykjJYGF4GRtDCM83KsWsaPbpEcS5BZEi1bjDRLCNFn2', '5JZwEEWNNGFLkGcxFf73GXMchzDKDwc696hK94BizmgVADVFkLe'),
testlib.MultisigWallet(2, '5HqJVoH5iZN5B3RwMq2tgwqdxxAi9Do8d4Mt97EH6BENoa5FraE', '5JmYF8mhZkggDTope6DUPBMBgLqiVjGDWzYi3ZeczwgDX64XQd9', '5KcjdfeHvVg89mJWVmKwMGYzXFXC684AmR6EJmtkrk8ZPNoWLcx'),
testlib.MultisigWallet(2, '5K525PQyBG3xr84qY1HjLw98dQ4YYGwK5BUavVwsaZqbVc39mAy', '5JF84NuqyDircZzFsNRFfGeknw9gmJJVMbKHba2PMYuyeda5A2B', '5JmNEVANrcgJAZvUjkNWVQPyULirSN8M7UJDkGKKUy2mLXvh4GK'),
testlib.MultisigWallet(2, '5KhRKs5p1dAVF81MuEookKqNn4W1kNU5MUSuibnkZur4RMzLzo3', '5JCUwbYzcCFjHvJgQ5iiFGxxqDKSNtDBaeBjvU3s2tKFqe6cfeL', '5KdGj6PiD3yArpAytZ3szjbagh828bZ7yBpn82Gn4esZapqC2it')
]
consensus = "17ac43c1d8549c3181b200f1bf97eb7d"
wallet_keys = None
wallet_keys_2 = None
datasets = [
{"dataset_1": "My first dataset!"},
{"dataset_2": {"id": "abcdef", "desc": "My second dataset!", "data": [1, 2, 3, 4]}},
{"dataset_3": "My third datset!"}
]
dataset_change = "This is the mutated dataset"
def scenario( wallets, **kw ):
global put_result, wallet_keys, wallet_keys_2, datasets, zonefile_hash, dataset_change
# set up a second client
config_path = os.environ.get("BLOCKSTACK_CLIENT_CONFIG", None)
assert config_path
config_dir_2 = os.path.dirname(config_path) + '.2'
os.makedirs(config_dir_2)
config_path_2 = os.path.join(config_dir_2, 'client.ini')
res = testlib.peer_make_config(16300, config_dir_2)
if 'error' in res:
print "failed to set up {}".format(config_dir_2)
return False
wallet_keys_2 = testlib.blockstack_client_initialize_wallet( "0123456789abcdef", wallets[5].privkey, wallets[6].privkey, wallets[7].privkey )
wallet_keys = testlib.blockstack_client_initialize_wallet( "0123456789abcdef", wallets[2].privkey, wallets[3].privkey, wallets[4].privkey )
test_proxy = testlib.TestAPIProxy()
blockstack_client.set_default_proxy( test_proxy )
testlib.blockstack_namespace_preorder( "test", wallets[1].addr, wallets[0].privkey )
testlib.next_block( **kw )
testlib.blockstack_namespace_reveal( "test", wallets[1].addr, 52595, 250, 4, [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], 10, 10, wallets[0].privkey )
testlib.next_block( **kw )
testlib.blockstack_namespace_ready( "test", wallets[1].privkey )
testlib.next_block( **kw )
resp = testlib.blockstack_cli_register( "foo.test", "0123456789abcdef" )
if 'error' in resp:
print >> sys.stderr, json.dumps(resp, indent=4, sort_keys=True)
return False
# wait for the preorder to get confirmed
for i in xrange(0, 12):
testlib.next_block( **kw )
# wait for the poller to pick it up
print >> sys.stderr, "Waiting for the backend to submit the register"
time.sleep(10)
# wait for the register to get confirmed
for i in xrange(0, 12):
testlib.next_block( **kw )
print >> sys.stderr, "Waiting for the backend to acknowledge registration"
time.sleep(10)
# wait for initial update to get confirmed
for i in xrange(0, 12):
# tell serialization-checker that value_hash can be ignored here
print "BLOCKSTACK_SERIALIZATION_CHECK_IGNORE value_hash"
sys.stdout.flush()
testlib.next_block( **kw )
print >> sys.stderr, "Waiting for the backend to acknowledge update"
time.sleep(10)
# wait for zonefile/profile replication
for i in xrange(0, 12):
testlib.next_block( **kw )
print >> sys.stderr, "Waiting for the backend to replicate zonefile and profile"
time.sleep(10)
# make a few accounts
res = testlib.blockstack_cli_put_account("foo.test", "serviceFoo", "serviceFooID", "foo://bar.com", "0123456789abcdef", extra_data='foofield=foo!', wallet_keys=wallet_keys)
if 'error' in res:
res['test'] = 'Failed to create foo.test serviceFoo account'
print json.dumps(res, indent=4, sort_keys=True)
error = True
return
time.sleep(2)
res = testlib.blockstack_cli_put_account("foo.test", "serviceBar", "serviceBarID", "bar://baz.com", "0123456789abcdef", extra_data='barfield=bar!', wallet_keys=wallet_keys)
if 'error' in res:
res['test'] = 'Failed to create foo.test serviceBar account'
print json.dumps(res, indent=4, sort_keys=True)
error = True
return
time.sleep(2)
# put some data
put_result = testlib.blockstack_cli_put_mutable( "foo.test", "hello_world_1", json.dumps(datasets[0]), password="0123456789abcdef")
if 'error' in put_result:
print json.dumps(put_result, indent=4, sort_keys=True)
return False
time.sleep(2)
put_result = testlib.blockstack_cli_put_mutable( "foo.test", "hello_world_2", json.dumps(datasets[1]), password="0123456789abcdef")
if 'error' in put_result:
print json.dumps(put_result, indent=4, sort_keys=True)
return False
time.sleep(2)
put_result = testlib.blockstack_cli_put_mutable( "foo.test", "hello_world_3", json.dumps(datasets[2]), password="0123456789abcdef")
if 'error' in put_result:
print json.dumps(put_result, indent=4, sort_keys=True)
return False
time.sleep(2)
# increment data version too
datasets[0]['buf'] = []
for i in xrange(0, 5):
datasets[0]["dataset_change"] = dataset_change
datasets[0]['buf'].append(i)
put_result = testlib.blockstack_cli_put_mutable( "foo.test", "hello_world_1", json.dumps(datasets[0]), password="0123456789abcdef")
if 'error' in put_result:
print json.dumps(put_result, indent=4, sort_keys=True )
return False
time.sleep(2)
testlib.next_block( **kw )
def check( state_engine ):
global wallet_keys, datasets, zonefile_hash
# not revealed, but ready
ns = state_engine.get_namespace_reveal( "test" )
if ns is not None:
print "namespace not ready"
return False
ns = state_engine.get_namespace( "test" )
if ns is None:
print "no namespace"
return False
if ns['namespace_id'] != 'test':
print "wrong namespace"
return False
# not preordered
preorder = state_engine.get_name_preorder( "foo.test", pybitcoin.make_pay_to_address_script(wallets[2].addr), wallets[3].addr )
if preorder is not None:
print "still have preorder"
return False
# registered
name_rec = state_engine.get_name( "foo.test" )
if name_rec is None:
print "name does not exist"
return False
# owned
if name_rec['address'] != wallets[3].addr or name_rec['sender'] != pybitcoin.make_pay_to_address_script(wallets[3].addr):
print "name has wrong owner"
return False
srv = xmlrpclib.ServerProxy("http://localhost:%s" % blockstack.RPC_SERVER_PORT)
# zonefile and profile replicated to blockstack server
try:
zonefile_by_name_str = srv.get_zonefiles_by_names(['foo.test'])
zonefile_by_hash_str = srv.get_zonefiles([name_rec['value_hash']])
zonefile_by_name = json.loads(zonefile_by_name_str)
zonefile_by_hash = json.loads(zonefile_by_hash_str)
assert 'error' not in zonefile_by_name, json.dumps(zonefile_by_name, indent=4, sort_keys=True)
assert 'error' not in zonefile_by_hash, json.dumps(zonefile_by_hash, indent=4, sort_keys=True)
zf1 = None
zf2 = None
try:
zf1 = base64.b64decode( zonefile_by_name['zonefiles']['foo.test'] )
except:
print zonefile_by_name
raise
try:
zf2 = base64.b64decode( zonefile_by_hash['zonefiles'][name_rec['value_hash']] )
except:
print zonefile_by_hash
raise
assert zf1 == zf2
zonefile = blockstack_zones.parse_zone_file( zf1 )
user_pubkey = blockstack_client.user.user_zonefile_data_pubkey( zonefile )
assert user_pubkey is not None, "no zonefile public key"
profile_resp_txt = srv.get_profile("foo.test")
profile_resp = json.loads(profile_resp_txt)
assert 'error' not in profile_resp, "error:\n%s" % json.dumps(profile_resp, indent=4, sort_keys=True)
assert 'profile' in profile_resp, "missing profile:\n%s" % json.dumps(profile_resp, indent=4, sort_keys=True)
# profile will be in 'raw' form
raw_profile = profile_resp['profile']
profile = blockstack_client.storage.parse_mutable_data( raw_profile, user_pubkey )
except Exception, e:
traceback.print_exc()
print "Invalid profile"
return False
# have right data
test_proxy = testlib.TestAPIProxy()
blockstack_client.set_default_proxy( test_proxy )
for i in xrange(0, len(datasets)):
print "get hello_world_%s" % (i+1)
dat = blockstack_client.get_mutable( "hello_world_%s" % (i+1), blockchain_id="foo.test" )
if dat is None:
print "No data '%s'" % ("hello_world_%s" % (i+1))
return False
if 'error' in dat:
print json.dumps(dat, indent=4, sort_keys=True)
return False
if json.loads(dat['data']) != datasets[i]:
print "Mismatch %s: %s %s != %s %s" % (i, dat['data'], type(dat['data']), datasets[i], type(datasets[i]))
return False
profile, zonefile = blockstack_client.get_name_profile('foo.test')
if profile is None:
print 'No profile'
return False
if 'error' in zonefile:
print json.dumps(zonefile, indent=4, sort_keys=True)
return False
# accounts should all be there
if not profile.has_key('account'):
print 'profile:\n{}'.format(json.dumps(profile, indent=4, sort_keys=True))
return False
expected_account_info = [
{
"contentUrl": "foo://bar.com",
"foofield": "foo!",
"identifier": "serviceFooID",
"service": "serviceFoo"
},
{
"barfield": "bar!",
"contentUrl": "bar://baz.com",
"identifier": "serviceBarID",
"service": "serviceBar"
}
]
for account_info in expected_account_info:
found = False
for profile_account_info in profile['account']:
if profile_account_info == account_info:
found = True
break
if not found:
print "missing\n{}\nin\n{}".format(json.dumps(account_info, indent=4, sort_keys=True), json.dumps(profile, indent=4, sort_keys=True))
return False
# there should be no failures in the API log
api_log_path = os.path.join(os.path.dirname(test_proxy.conf['path']), "api_endpoint.log")
with open(api_log_path, "r") as f:
api_log = f.read()
if "Traceback (most recent call last)" in api_log:
print "exception thrown by client"
return False
if "Server did not save" in api_log:
print 'server did not save'
return False
return True