Introducing the class AccountUpdateOperation

This commit is contained in:
Nelson R. Perez 2016-12-03 18:35:11 -05:00
parent 8b81c30fa9
commit 5c449da04f
11 changed files with 210 additions and 27 deletions

View file

@ -0,0 +1,69 @@
package com.luminiasoft.bitshares;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedLong;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* Class used to encapsulate operations related to the account_update_operation.
*/
public class AccountUpdateOperation extends BaseOperation {
public static final String KEY_ACCOUNT = "account";
public static final String KEY_OWNER = "owner";
public static final String KEY_ACTIVE = "active";
public static final String KEY_FEE = "fee";
public static final String KEY_EXTENSIONS = "extensions";
private UserAccount account;
private AssetAmount fee;
private Authority owner;
private Authority active;
private Extensions extensions;
public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AssetAmount fee){
super(OperationType.account_update_operation);
this.account = account;
this.owner = owner;
this.active = active;
this.fee = fee;
extensions = new Extensions();
}
public AccountUpdateOperation(UserAccount account, Authority owner, Authority active){
this(account, owner, active, new AssetAmount(UnsignedLong.valueOf(0), new Asset("1.3.0")));
}
public void setFee(AssetAmount fee){
this.fee = fee;
}
@Override
public String toJsonString() {
Gson gson = new Gson();
return gson.toJson(this);
}
@Override
public JsonElement toJsonObject() {
JsonObject accountUpdate = new JsonObject();
accountUpdate.add(KEY_FEE, fee.toJsonObject());
accountUpdate.add(KEY_ACCOUNT, account.toJsonObject());
accountUpdate.add(KEY_OWNER, owner.toJsonObject());
accountUpdate.add(KEY_ACTIVE, active.toJsonObject());
accountUpdate.add(KEY_EXTENSIONS, extensions.toJsonObject());
return accountUpdate;
}
@Override
public byte[] toBytes() {
byte[] feeBytes = fee.toBytes();
byte[] accountBytes = account.toBytes();
byte[] ownerBytes = owner.toBytes();
byte[] activeBytes = active.toBytes();
byte[] extensionBytes = extensions.toBytes();
return Bytes.concat(feeBytes, accountBytes, ownerBytes, activeBytes, extensionBytes);
}
}

View file

@ -1,27 +1,32 @@
package com.luminiasoft.bitshares; package com.luminiasoft.bitshares;
import com.google.common.primitives.Bytes;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.luminiasoft.bitshares.errors.MalformedAddressException; import com.luminiasoft.bitshares.errors.MalformedAddressException;
import com.luminiasoft.bitshares.interfaces.ByteSerializable; import com.luminiasoft.bitshares.interfaces.ByteSerializable;
import com.luminiasoft.bitshares.interfaces.JsonSerializable; import com.luminiasoft.bitshares.interfaces.JsonSerializable;
import java.util.HashMap; import java.nio.ByteBuffer;
import java.util.*;
/** /**
* Created by nelson on 11/30/16. * Created by nelson on 11/30/16.
*/ */
public class Authority implements JsonSerializable { public class Authority implements JsonSerializable, ByteSerializable {
private long weight_threshold; private long weight_threshold;
private HashMap<Address, Long> address_auths; private HashMap<UserAccount, Integer> account_auths;
private HashMap<UserAccount, Long> account_auths; private HashMap<PublicKey, Integer> key_auths;
private HashMap<PublicKey, Long> key_auths; private Extensions extensions;
public Authority(HashMap<String, Long> keyAuths) throws MalformedAddressException { public Authority(long weight_threshold, HashMap<String, Integer> keyAuths) throws MalformedAddressException {
key_auths = new HashMap<PublicKey, Long>(); this.weight_threshold = weight_threshold;
key_auths = new HashMap<PublicKey, Integer>();
for(String key : keyAuths.keySet()){ for(String key : keyAuths.keySet()){
Address address = new Address(key); Address address = new Address(key);
key_auths.put(address.getPublicKey(), keyAuths.get(key)); key_auths.put(address.getPublicKey(), keyAuths.get(key));
} }
account_auths = new HashMap<UserAccount, Integer>();
extensions = new Extensions();
} }
@Override @Override
@ -33,4 +38,32 @@ public class Authority implements JsonSerializable {
public JsonElement toJsonObject() { public JsonElement toJsonObject() {
return null; return null;
} }
@Override
public byte[] toBytes() {
List<Byte> byteArray = new ArrayList<Byte>();
// Adding number of authorities
byteArray.add(Byte.valueOf((byte) (account_auths.size() + key_auths.size())));
// Weight threshold
byteArray.addAll(Bytes.asList(Util.revertInteger(new Integer((int) weight_threshold))));
// Number of account authorities
byteArray.add((byte) account_auths.size());
//TODO: Add account authorities
// Number of key authorities
byteArray.add((byte) key_auths.size());
for(PublicKey publicKey : key_auths.keySet()){
byteArray.addAll(Bytes.asList(publicKey.toBytes()));
byteArray.addAll(Bytes.asList(Util.revertShort(key_auths.get(publicKey).shortValue())));
}
// Adding number of extensions
byteArray.add((byte) extensions.size());
return Bytes.toArray(byteArray);
}
} }

View file

@ -14,7 +14,9 @@ public abstract class BaseOperation implements ByteSerializable, JsonSerializabl
this.type = type; this.type = type;
} }
public abstract byte getId(); public byte getId() {
return (byte) this.type.ordinal();
}
public abstract byte[] toBytes(); public abstract byte[] toBytes();
} }

View file

@ -1,8 +0,0 @@
package com.luminiasoft.bitshares;
/**
* Created by nelson on 11/9/16.
*/
public class Extension {
//TODO: Give this class a proper implementation
}

View file

@ -0,0 +1,41 @@
package com.luminiasoft.bitshares;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.luminiasoft.bitshares.interfaces.ByteSerializable;
import com.luminiasoft.bitshares.interfaces.JsonSerializable;
import java.util.ArrayList;
/**
* Created by nelson on 11/9/16.
*/
public class Extensions implements JsonSerializable, ByteSerializable {
private ArrayList<JsonSerializable> extensions;
public Extensions(){
extensions = new ArrayList<>();
}
@Override
public String toJsonString() {
return null;
}
@Override
public JsonElement toJsonObject() {
JsonArray array = new JsonArray();
for(JsonSerializable o : extensions)
array.add(o.toJsonObject());
return array;
}
@Override
public byte[] toBytes() {
return new byte[0];
}
public int size(){
return extensions.size();
}
}

View file

@ -49,7 +49,7 @@ public class Main {
// test.testTransactionBroadcastSequence(); // test.testTransactionBroadcastSequence();
// test.testAccountLookupDeserialization(); // test.testAccountLookupDeserialization();
// test.testPrivateKeyManipulations(); // test.testPrivateKeyManipulations();
test.testPublicKeyManipulations(); // test.testPublicKeyManipulations();
// test.testGetAccountByName(); // test.testGetAccountByName();
// test.testGetRequiredFees(); // test.testGetRequiredFees();
// test.testRandomNumberGeneration(); // test.testRandomNumberGeneration();
@ -61,5 +61,6 @@ public class Main {
// test.testingInvoiceGeneration(); // test.testingInvoiceGeneration();
// test.testCompression(); // test.testCompression();
// test.testCreateBinFile(); // test.testCreateBinFile();
test.testAccountUpdateOperationSerialization();
} }
} }

View file

@ -743,4 +743,21 @@ public class Test {
byte[] fileOutput = FileBin.getBytesFromBrainKey(Main.BRAIN_KEY, "123456","bithon-83"); byte[] fileOutput = FileBin.getBytesFromBrainKey(Main.BRAIN_KEY, "123456","bithon-83");
System.out.println("fileOutput " + Arrays.toString(fileOutput)); System.out.println("fileOutput " + Arrays.toString(fileOutput));
} }
public void testAccountUpdateOperationSerialization(){
UserAccount account = new UserAccount("1.2.138632");
AssetAmount fee = new AssetAmount(UnsignedLong.valueOf("4294967295"), new Asset("1.3.0"));
HashMap<String, Integer> keyAuths = new HashMap<>();
keyAuths.put("BTS8RiFgs8HkcVPVobHLKEv6yL3iXcC9SWjbPVS15dDAXLG9GYhnY", 65535);
try {
Authority owner = new Authority(1, keyAuths);
Authority active = new Authority(1, keyAuths);
AccountUpdateOperation operation = new AccountUpdateOperation(account, owner, active, fee);
byte[] serializedOperation = operation.toBytes();
System.out.println("serialized operation");
System.out.println(Util.bytesToHex(serializedOperation));
} catch (MalformedAddressException e) {
System.out.println("MalformedAddressException. Msg: "+e.getMessage());
}
}
} }

View file

@ -38,7 +38,7 @@ public class Transaction implements ByteSerializable, JsonSerializable {
private ECKey privateKey; private ECKey privateKey;
private BlockData blockData; private BlockData blockData;
private List<BaseOperation> operations; private List<BaseOperation> operations;
private List<Extension> extensions; private List<Extensions> extensions;
/** /**
* Transaction constructor. * Transaction constructor.
@ -50,7 +50,7 @@ public class Transaction implements ByteSerializable, JsonSerializable {
this.privateKey = DumpedPrivateKey.fromBase58(null, wif).getKey(); this.privateKey = DumpedPrivateKey.fromBase58(null, wif).getKey();
this.blockData = block_data; this.blockData = block_data;
this.operations = operation_list; this.operations = operation_list;
this.extensions = new ArrayList<Extension>(); this.extensions = new ArrayList<Extensions>();
} }
/** /**
@ -63,7 +63,7 @@ public class Transaction implements ByteSerializable, JsonSerializable {
this.privateKey = privateKey; this.privateKey = privateKey;
this.blockData = blockData; this.blockData = blockData;
this.operations = operationList; this.operations = operationList;
this.extensions = new ArrayList<Extension>(); this.extensions = new ArrayList<Extensions>();
} }
public ECKey getPrivateKey(){ public ECKey getPrivateKey(){
@ -143,8 +143,8 @@ public class Transaction implements ByteSerializable, JsonSerializable {
//Adding the number of extensions //Adding the number of extensions
byteArray.add((byte) this.extensions.size()); byteArray.add((byte) this.extensions.size());
for(Extension extension : extensions){ for(Extensions extensions : this.extensions){
//TODO: Implement the extension serialization //TODO: Implement the extensions serialization
} }
// Adding a last zero byte to match the result obtained by the python-graphenelib code // Adding a last zero byte to match the result obtained by the python-graphenelib code
// I'm not exactly sure what's the meaning of this last zero byte, but for now I'll just // I'm not exactly sure what's the meaning of this last zero byte, but for now I'll just

View file

@ -44,11 +44,6 @@ public class TransferOperation extends BaseOperation {
this.fee = newFee; this.fee = newFee;
} }
@Override
public byte getId() {
return (byte) this.type.ordinal();
}
public UserAccount getFrom(){ public UserAccount getFrom(){
return this.from; return this.from;
} }
@ -77,6 +72,7 @@ public class TransferOperation extends BaseOperation {
@Override @Override
public String toJsonString() { public String toJsonString() {
//TODO: Evaluate using simple Gson class to return a simple string representation and drop the TransferSerializer class
GsonBuilder gsonBuilder = new GsonBuilder(); GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferSerializer()); gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferSerializer());
return gsonBuilder.create().toJson(this); return gsonBuilder.create().toJson(this);

View file

@ -46,6 +46,7 @@ public class TransferTransactionBuilder extends TransactionBuilder {
return this; return this;
} }
//TODO: Add support for multiple transfer operations in a single transaction
public TransferTransactionBuilder addOperation(TransferOperation transferOperation){ public TransferTransactionBuilder addOperation(TransferOperation transferOperation){
if(operations == null){ if(operations == null){
operations = new ArrayList<BaseOperation>(); operations = new ArrayList<BaseOperation>();

View file

@ -1,5 +1,6 @@
package com.luminiasoft.bitshares; package com.luminiasoft.bitshares;
import com.google.common.primitives.Bytes;
import org.tukaani.xz.LZMA2Options; import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.LZMAInputStream; import org.tukaani.xz.LZMAInputStream;
import org.tukaani.xz.LZMAOutputStream; import org.tukaani.xz.LZMAOutputStream;
@ -7,6 +8,7 @@ import org.tukaani.xz.LZMAOutputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.tukaani.xz.XZOutputStream; import org.tukaani.xz.XZOutputStream;
@ -99,4 +101,33 @@ public class Util {
} }
return null; return null;
} }
/**
* Returns an array of bytes with the underlying data used to represent an integer in the reverse form.
* This is useful for endianess switches, meaning that if you give this function a big-endian integer
* it will return it's little-endian bytes.
* @param input An Integer value.
* @return The array of bytes that represent this value in the reverse format.
*/
public static byte[] revertInteger(Integer input){
return ByteBuffer.allocate(Integer.BYTES).putInt(Integer.reverseBytes(input)).array();
}
/**
* Same operation as in the revertInteger function, but in this case for a short (2 bytes) value.
* @param input A Short value
* @return The array of bytes that represent this value in the reverse format.
*/
public static byte[] revertShort(Short input){
return ByteBuffer.allocate(Short.BYTES).putShort(Short.reverseBytes(input)).array();
}
/**
* Same operation as in the revertInteger function, but in this case for a long (8 bytes) value.
* @param input A Long value
* @return The array of bytes that represent this value in the reverse format.
*/
public static byte[] revertLong(Long input){
return ByteBuffer.allocate(Long.BYTES).putLong(Long.reverseBytes(input)).array();
}
} }