github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/sdk/examples/intkey_java/IntegerKeyHandler.java (about) 1 /* Copyright 2016, 2017 Intel Corporation 2 Licensed under the Apache License, Version 2.0 (the "License"); 3 you may not use this file except in compliance with the License. 4 You may obtain a copy of the License at 5 6 http://www.apache.org/licenses/LICENSE-2.0 7 8 Unless required by applicable law or agreed to in writing, software 9 distributed under the License is distributed on an "AS IS" BASIS, 10 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 See the License for the specific language governing permissions and 12 limitations under the License. 13 ------------------------------------------------------------------------------*/ 14 15 package sawtooth.examples.intkey; 16 17 import com.google.protobuf.ByteString; 18 19 import co.nstant.in.cbor.CborBuilder; 20 import co.nstant.in.cbor.CborDecoder; 21 import co.nstant.in.cbor.CborEncoder; 22 import co.nstant.in.cbor.CborException; 23 import co.nstant.in.cbor.model.DataItem; 24 25 import sawtooth.sdk.processor.State; 26 import sawtooth.sdk.processor.TransactionHandler; 27 import sawtooth.sdk.processor.Utils; 28 import sawtooth.sdk.processor.exceptions.InternalError; 29 import sawtooth.sdk.processor.exceptions.InvalidTransactionException; 30 import sawtooth.sdk.protobuf.TpProcessRequest; 31 32 import java.io.ByteArrayInputStream; 33 import java.io.ByteArrayOutputStream; 34 import java.io.IOException; 35 import java.io.UnsupportedEncodingException; 36 import java.util.AbstractMap; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.HashMap; 41 import java.util.Map; 42 import java.util.logging.Logger; 43 44 45 public class IntegerKeyHandler implements TransactionHandler { 46 47 private final Logger logger = Logger.getLogger(IntegerKeyHandler.class.getName()); 48 private String intkeyNameSpace; 49 50 private static final long MIN_VALUE = 0; 51 private static final long MAX_VALUE = 4294967295L; 52 private static final long MAX_NAME_LENGTH = 20; 53 54 /** 55 * constructor. 56 */ 57 public IntegerKeyHandler() { 58 try { 59 this.intkeyNameSpace = Utils.hash512( 60 this.transactionFamilyName().getBytes("UTF-8")).substring(0, 6); 61 } catch (UnsupportedEncodingException usee) { 62 usee.printStackTrace(); 63 this.intkeyNameSpace = ""; 64 } 65 } 66 67 @Override 68 public String transactionFamilyName() { 69 return "intkey"; 70 } 71 72 @Override 73 public String getVersion() { 74 return "1.0"; 75 } 76 77 @Override 78 public Collection<String> getNameSpaces() { 79 ArrayList<String> namespaces = new ArrayList<String>(); 80 namespaces.add(this.intkeyNameSpace); 81 return namespaces; 82 } 83 84 /** 85 * Helper function to decode the Payload of a transaction. 86 * Convert the co.nstant.in.cbor.model.Map to a HashMap. 87 */ 88 public Map<String, String> decodePayload(byte[] bytes) throws CborException { 89 ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 90 co.nstant.in.cbor.model.Map data = 91 (co.nstant.in.cbor.model.Map) new CborDecoder(bais).decodeNext(); 92 DataItem[] keys = data.getKeys().toArray(new DataItem[0]); 93 Map<String, String> result = new HashMap(); 94 for (int i = 0; i < keys.length; i++) { 95 result.put( 96 keys[i].toString(), 97 data.get(keys[i]).toString()); 98 } 99 return result; 100 } 101 102 /** 103 * Helper function to decode State retrieved from the address of the name. 104 * Convert the co.nstant.in.cbor.model.Map to a HashMap. 105 */ 106 public Map<String, Long> decodeState(byte[] bytes) throws CborException { 107 ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 108 co.nstant.in.cbor.model.Map data = 109 (co.nstant.in.cbor.model.Map) new CborDecoder(bais).decodeNext(); 110 DataItem[] keys = data.getKeys().toArray(new DataItem[0]); 111 Map<String, Long> result = new HashMap(); 112 for (int i = 0; i < keys.length; i++) { 113 result.put( 114 keys[i].toString(), 115 Long.decode(data.get(keys[i]).toString())); 116 } 117 return result; 118 } 119 120 /** 121 * Helper function to encode the State that will be stored at the address of 122 * the name. 123 */ 124 public Map.Entry<String, ByteString> encodeState(String address, String name, Long value) 125 throws CborException { 126 ByteArrayOutputStream boas = new ByteArrayOutputStream(); 127 new CborEncoder(boas).encode(new CborBuilder() 128 .addMap() 129 .put(name, value) 130 .end() 131 .build()); 132 133 return new AbstractMap.SimpleEntry<String, ByteString>( 134 address, 135 ByteString.copyFrom(boas.toByteArray())); 136 } 137 138 @Override 139 public void apply(TpProcessRequest transactionRequest, 140 State state) throws InvalidTransactionException, InternalError { 141 /* 142 * IntKey state will be stored at an address of the name 143 * with the key being the name and the value an integer. so { "foo": 20, "bar": 26} 144 * would be a possibility if the hashing algorithm hashes foo and bar to the 145 * same address 146 */ 147 try { 148 if (transactionRequest.getPayload().size() == 0) { 149 throw new InvalidTransactionException("Payload is required."); 150 } 151 152 Map updateMap = this.decodePayload(transactionRequest.getPayload().toByteArray()); 153 154 // validate name 155 String name = updateMap.get("Name").toString(); 156 157 if (name.length() == 0) { 158 throw new InvalidTransactionException("Name is required"); 159 } 160 161 if (name.length() > MAX_NAME_LENGTH) { 162 throw new InvalidTransactionException( 163 "Name must be a string of no more than " 164 + Long.toString(MAX_NAME_LENGTH) + " characters"); 165 } 166 167 // validate verb 168 String verb = updateMap.get("Verb").toString(); 169 170 if (verb.length() == 0) { 171 throw new InvalidTransactionException("Verb is required"); 172 } 173 174 if (!Arrays.asList("set", "dec", "inc").contains(verb)) { 175 throw new InvalidTransactionException( 176 "Verb must be set, inc, or dec, not " + verb); 177 } 178 179 // validate value 180 Long value = null; 181 182 try { 183 value = Long.decode(updateMap.get("Value").toString()); 184 } catch (NumberFormatException ex) { 185 throw new InvalidTransactionException( 186 "Value must be an integer"); 187 } 188 189 if (value == null) { 190 throw new InvalidTransactionException("Value is required"); 191 } 192 193 if (value > MAX_VALUE || value < MIN_VALUE) { 194 throw new InvalidTransactionException( 195 "Value must be an integer " 196 + "no less than " + Long.toString(MIN_VALUE) 197 + " and no greater than " + Long.toString(MAX_VALUE)); 198 } 199 200 String address = null; 201 202 try { 203 String hashedName = Utils.hash512(name.getBytes("UTF-8")); 204 address = this.intkeyNameSpace + hashedName.substring(hashedName.length() - 64); 205 } catch (UnsupportedEncodingException usee) { 206 usee.printStackTrace(); 207 throw new InternalError("Internal Error, " + usee.toString()); 208 } 209 210 Collection<String> addresses = new ArrayList<String>(0); 211 212 if (verb.equals("set")) { 213 // The ByteString is cbor encoded dict/hashmap 214 Map<String, ByteString> possibleAddressValues = state.getState(Arrays.asList(address)); 215 byte[] stateValueRep = possibleAddressValues.get(address).toByteArray(); 216 Map<String, Long> stateValue = null; 217 if (stateValueRep.length > 0) { 218 stateValue = this.decodeState(stateValueRep); 219 if (stateValue.containsKey(name)) { 220 throw new InvalidTransactionException("Verb is set but Name already in state, " 221 + "Name: " + name + " Value: " + stateValue.get(name).toString()); 222 } 223 } 224 225 if (value < 0) { 226 throw new InvalidTransactionException("Verb is set but Value is less than 0"); 227 } 228 229 // 'set' passes checks so store it in the state 230 Map.Entry<String, ByteString> entry = this.encodeState(address, name, value); 231 232 Collection<Map.Entry<String, ByteString>> addressValues = Arrays.asList(entry); 233 addresses = state.setState(addressValues); 234 } 235 if (verb.equals("inc")) { 236 Map<String, ByteString> possibleValues = state.getState(Arrays.asList(address)); 237 byte[] stateValueRep = possibleValues.get(address).toByteArray(); 238 if (stateValueRep.length == 0) { 239 throw new InvalidTransactionException("Verb is inc but Name is not in state"); 240 } 241 Map<String, Long> stateValue = this.decodeState(stateValueRep); 242 if (!stateValue.containsKey(name)) { 243 throw new InvalidTransactionException("Verb is inc but Name is not in state"); 244 } 245 if (stateValue.get(name) + value > MAX_VALUE) { 246 throw new InvalidTransactionException( 247 "Inc would set Value to greater than " + Long.toString(MAX_VALUE)); 248 } 249 // Increment the value in state by value 250 Map.Entry<String, ByteString> entry = 251 this.encodeState(address, name, stateValue.get(name) + value); 252 Collection<Map.Entry<String, ByteString>> addressValues = Arrays.asList(entry); 253 addresses = state.setState(addressValues); 254 } 255 if (verb.equals("dec")) { 256 Map<String, ByteString> possibleAddressResult = state.getState(Arrays.asList(address)); 257 byte[] stateValueRep = possibleAddressResult.get(address).toByteArray(); 258 259 if (stateValueRep.length == 0) { 260 throw new InvalidTransactionException("Verb is dec but Name is not in state"); 261 } 262 Map<String, Long> stateValue = this.decodeState(stateValueRep); 263 if (!stateValue.containsKey(name)) { 264 throw new InvalidTransactionException("Verb is dec but Name is not in state"); 265 } 266 if (stateValue.get(name) - value < MIN_VALUE) { 267 throw new InvalidTransactionException( 268 "Dec would set Value to less than " + Long.toString(MIN_VALUE)); 269 } 270 271 // Decrement the value in state by value 272 Map.Entry<String, ByteString> entry = 273 this.encodeState(address, name, stateValue.get(name) - value); 274 275 Collection<Map.Entry<String, ByteString>> addressValues = Arrays.asList(entry); 276 addresses = state.setState(addressValues); 277 } 278 // if the 'set', 'inc', or 'dec' set to state didn't work 279 if (addresses.size() == 0) { 280 throw new InternalError("State error!."); 281 } 282 logger.info("Verb: " + verb + " Name: " + name + " value: " + value); 283 284 } catch (CborException ce) { 285 throw new InternalError("Cbor Error" + ce.toString()); 286 } 287 } 288 }