mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-04-23 03:20:19 +08:00
WIP: multi-device storage test
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user