github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/integration/sawtooth_integration/tests/test_intkey_smoke.py (about) 1 # Copyright 2017 Intel Corporation 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # ------------------------------------------------------------------------------ 15 16 # pylint: disable = attribute-defined-outside-init 17 18 import unittest 19 import logging 20 import operator # used by verifier 21 # -- used by rest_api callers -- 22 import urllib.request 23 import urllib.error 24 import json 25 from base64 import b64decode 26 27 import cbor 28 29 from sawtooth_intkey.intkey_message_factory import IntkeyMessageFactory 30 from sawtooth_integration.tests.integration_tools import wait_for_rest_apis 31 32 33 LOGGER = logging.getLogger(__name__) 34 LOGGER.setLevel(logging.INFO) 35 36 WAIT = 300 37 INTKEY_PREFIX = '1cf126' 38 39 40 class TestIntkeySmoke(unittest.TestCase): 41 @classmethod 42 def setUpClass(cls): 43 wait_for_rest_apis(['rest-api:8008']) 44 45 def test_intkey_smoke(self): 46 ''' 47 After starting up a validator, intkey processor, and rest api, 48 generate three batches of transactions: a batch of 'set' transactions 49 (the "populate" batch), a batch of valid 'inc'/'dec' transactions, 50 and a batch of invalid of invalid 'inc'/'dec' transactions (invalid 51 because they target words not set by the populate batch). 52 53 First, verify that the state is initially empty. Next, load the 54 batches in the following order, verifying that the state is 55 as it should be after each load: 56 57 1. Send the batch of 'set' transactions, populating the state. 58 2. Send the batch of valid transactions, altering the state. 59 3. Send the batch of invalid transactions, leaving the state the same. 60 4. Send the batch of valid transactions, again altering the state. 61 5. Send the same batch of 'set' transactions from before. 62 Because the state has already been populated, these transactions 63 are invalid, and shouldn't affect the state. 64 6. Send the batch of valid transactions, again altering the state. 65 ''' 66 67 self.verifier = IntkeyTestVerifier() 68 69 populate, valid_txns, invalid_txns = self.make_txn_batches() 70 71 self.verify_empty_state() 72 73 batches = ( 74 populate, 75 valid_txns, 76 invalid_txns, 77 valid_txns, 78 populate, 79 valid_txns 80 ) 81 82 how_many_updates = 0 83 84 for batch in batches: 85 if batch == valid_txns: 86 how_many_updates += 1 87 self.post_and_verify(batch, how_many_updates) 88 89 # assertions 90 91 def post_and_verify(self, batch, how_many_updates): 92 batch = IntkeyMessageFactory().create_batch(batch) 93 LOGGER.info('Posting batch') 94 _post_batch(batch) 95 self.verify_state_after_n_updates(how_many_updates) 96 97 def verify_state_after_n_updates(self, num): 98 LOGGER.debug('Verifying state after %s updates', num) 99 expected_state = self.verifier.state_after_n_updates(num) 100 actual_state = _get_data() 101 LOGGER.info('Current state: %s', actual_state) 102 self.assertEqual( 103 expected_state, 104 actual_state, 105 'Updated state error') 106 107 def verify_empty_state(self): 108 LOGGER.debug('Verifying empty state') 109 self.assertEqual( 110 [], 111 _get_state(), 112 'Empty state error') 113 114 # utilities 115 116 def make_txn_batches(self): 117 LOGGER.debug('Making txn batches') 118 batches = self.verifier.make_txn_batches() 119 return batches 120 121 # rest_api calls 122 123 124 def _post_batch(batch): 125 headers = {'Content-Type': 'application/octet-stream'} 126 response = _query_rest_api( 127 '/batches', 128 data=batch, headers=headers, expected_code=202) 129 response = _submit_request('{}&wait={}'.format(response['link'], WAIT)) 130 return response 131 132 133 def _get_data(): 134 state = _get_state() 135 # state is a list of dictionaries: { data: ..., address: ... } 136 dicts = [cbor.loads(b64decode(entry['data'])) for entry in state] 137 data = {k: v for d in dicts for k, v in d.items()} # merge dicts 138 return data 139 140 141 def _get_state(): 142 response = _query_rest_api('/state?address={}'.format(INTKEY_PREFIX)) 143 return response['data'] 144 145 146 def _query_rest_api(suffix='', data=None, headers=None, expected_code=200): 147 if headers is None: 148 headers = {} 149 url = 'http://rest-api:8008' + suffix 150 return _submit_request(urllib.request.Request(url, data, headers), 151 expected_code=expected_code) 152 153 154 def _submit_request(request, expected_code=200): 155 conn = urllib.request.urlopen(request) 156 assert expected_code == conn.getcode() 157 158 response = conn.read().decode('utf-8') 159 return json.loads(response) 160 161 162 # separate file? 163 class IntkeyTestVerifier: 164 def __init__(self, 165 valid=('lark', 'thrush', 'jay', 'wren', 'finch'), 166 invalid=('cow', 'pig', 'sheep', 'goat', 'horse'), 167 verbs=('inc', 'inc', 'dec', 'inc', 'dec'), 168 incdec=(1, 2, 3, 5, 8), 169 initial=(415, 325, 538, 437, 651)): 170 self.valid = valid 171 self.invalid = invalid 172 self.verbs = verbs 173 self.incdec = incdec 174 self.initial = initial 175 self.sets = ['set' for _ in range(len(self.initial))] 176 177 def make_txn_batches(self): 178 populate = tuple(zip(self.sets, self.valid, self.initial)) 179 valid_txns = tuple(zip(self.verbs, self.valid, self.incdec)) 180 invalid_txns = tuple(zip(self.verbs, self.invalid, self.incdec)) 181 182 return populate, valid_txns, invalid_txns 183 184 def state_after_n_updates(self, num): 185 ops = { 186 'inc': operator.add, 187 'dec': operator.sub 188 } 189 190 expected_values = [ 191 ops[verb](init, (val * num)) 192 for verb, init, val 193 in zip(self.verbs, self.initial, self.incdec) 194 ] 195 196 return {word: val for word, val in zip(self.valid, expected_values)}