package com.ripple.core.coretypes;
import com.ripple.core.coretypes.hash.Hash160;
import com.ripple.core.coretypes.hash.Hash256;
import com.ripple.core.coretypes.hash.Index;
import com.ripple.core.coretypes.uint.UInt32;
import com.ripple.core.fields.AccountIDField;
import com.ripple.core.fields.Field;
import com.ripple.core.fields.Type;
import com.ripple.core.serialized.BinaryParser;
import com.ripple.core.serialized.BytesSink;
import com.ripple.core.serialized.TypeTranslator;
import com.ripple.crypto.ecdsa.IKeyPair;
import com.ripple.crypto.ecdsa.Seed;
import com.ripple.encodings.common.B16;
import java.util.HashMap;
import java.util.Map;
import static com.ripple.config.Config.getB58IdentiferCodecs;
/**
* Originally it was intended that AccountIDs would be variable length so that's
* why they are variable length encoded as top level field objects.
*
* Note however, that in practice, all account ids are just 160 bit hashes.
* Consider the fields TakerPaysIssuer and fixed length encoding of issuers in
* amount serializations.
*
* Thus, we extend Hash160 which affords us some functionality.
*/
public class AccountID extends Hash160 {
// We can set aliases, so fromString(x) will return a given AccountID
// this is currently only used for tests, and not recommended to be used
// elsewhere.
public static Map<String, AccountID> aliases = new HashMap<String, AccountID>();
//
public static AccountID NEUTRAL = fromInteger(1), XRP_ISSUER = fromInteger(0);
final public String address;
public AccountID(byte[] bytes) {
this(bytes, encodeAddress(bytes));
}
public AccountID(byte[] bytes, String address) {
super(bytes);
this.address = address;
}
// Static from* constructors
public static AccountID fromString(String value) {
if (value.length() == 160 / 4) {
return fromAddressBytes(B16.decode(value));
} else {
if (value.startsWith("r") && value.length() >= 26) {
return fromAddress(value);
}
AccountID accountID = accountForAlias(value);
if (accountID == null) {
throw new UnknownAlias("Alias unset: " + value);
}
return accountID;
}
}
static public AccountID fromAddress(String address) {
byte[] bytes = getB58IdentiferCodecs().decodeAddress(address);
return new AccountID(bytes, address);
}
public static AccountID fromKeyPair(IKeyPair kp) {
byte[] bytes = kp.public_key_160_hash();
return new AccountID(bytes, encodeAddress(bytes));
}
public static AccountID fromPassPhrase(String phrase) {
return fromKeyPair(Seed.fromPassPhrase(phrase).keyPair());
}
static public AccountID fromSeedString(String seed) {
return fromKeyPair(Seed.getKeyPair(seed));
}
static public AccountID fromSeedBytes(byte[] seed) {
return fromKeyPair(Seed.getKeyPair(seed));
}
static public AccountID fromInteger(Integer n) {
// The hash160 constructor will extend the 4bytes address
return fromBytes(new Hash160(new UInt32(n).toByteArray()).bytes());
}
public static AccountID fromBytes(byte[] bytes) {
return new AccountID(bytes, encodeAddress(bytes));
}
static public AccountID fromAddressBytes(byte[] bytes) {
return fromBytes(bytes);
}
@Override
public int hashCode() {
return address.hashCode();
}
@Override
public String toString() {
return address;
}
public Issue issue(String code) {
return new Issue(Currency.fromString(code), this);
}
public Issue issue(Currency c) {
return new Issue(c, this);
}
public boolean isNativeIssuer() {
return equals(XRP_ISSUER);
}
// SerializedType interface implementation
@Override
public Object toJSON() {
return toString();
}
@Override
public byte[] toBytes() {
return translate.toBytes(this);
}
@Override
public String toHex() {
return translate.toHex(this);
}
@Override
public void toBytesSink(BytesSink to) {
to.add(bytes());
}
@Override
public Type type() {
return Type.AccountID;
}
public Hash256 lineIndex(Issue issue) {
if (issue.isNative()) throw new AssertionError();
return Index.rippleState(this, issue.issuer(), issue.currency());
}
public static class Translator extends TypeTranslator<AccountID> {
@Override
public AccountID fromParser(BinaryParser parser, Integer hint) {
if (hint == null) {
hint = 20;
}
return AccountID.fromAddressBytes(parser.read(hint));
}
@Override
public String toString(AccountID obj) {
return obj.toString();
}
@Override
public AccountID fromString(String value) {
return AccountID.fromString(value);
}
}
//
static public Translator translate = new Translator();
// helpers
private static String encodeAddress(byte[] a) {
return getB58IdentiferCodecs().encodeAddress(a);
}
public static AccountID addAliasFromPassPhrase(String n, String n2) {
return aliases.put(n, fromPassPhrase(n2));
}
public static AccountID accountForAlias(String value) {
return aliases.get(value);
}
// Typed field definitions
public static AccountIDField accountField(final Field f) {
return new AccountIDField() {
@Override
public Field getField() {
return f;
}
};
}
static public AccountIDField Account = accountField(Field.Account);
static public AccountIDField Owner = accountField(Field.Owner);
static public AccountIDField Destination = accountField(Field.Destination);
static public AccountIDField Issuer = accountField(Field.Issuer);
static public AccountIDField Target = accountField(Field.Target);
static public AccountIDField RegularKey = accountField(Field.RegularKey);
// Exceptions
public static class UnknownAlias extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = -8042838677708510072L;
public UnknownAlias(String s) {
super(s);
}
}
}