using System; using sharp.extensions; using sharp.hashing; using System.Text; using System.Collections.Generic; namespace sharp.cryptonote { public class CryptoNoteAddress { static IHash keccak256 = Hashes.createKECCAK_256(); static Dictionary configurations = initializeConfigurations(); static Dictionary initializeConfigurations(){ Dictionary r = new Dictionary(); r.Add(CryptoNoteCoin.XMR,new CoinConfiguration(95,4)); r.Add(CryptoNoteCoin.AEON,new CoinConfiguration(97,6)); return r; } byte[] address; public CryptoNoteAddress(string address) { if (address.Length < 11){ throw new ArgumentException("Invalid Cryptonote Address, len(..) < 11"); } byte[] prefix = new byte[8]; base58uint64decode2array(address.Substring(0, 11),prefix,0,8); CryptoNoteCoin prefixCoin = (CryptoNoteCoin)prefix[0]; if (address.Length != configurations[prefixCoin].PublicAddressLen){ throw new ArgumentException(String.Format("Invalid Cryptonote Address, len(..) != {0}",configurations[prefixCoin].PublicAddressLen)); } string[] parts = address.splitConstantWidth(11); byte[] binary = new byte[69]; for (int n = 0; n< 8;n++){ base58uint64decode2array(parts[n], binary, n * 8); } base58uint64decode2array(parts[8], binary, 64, 5); setup(binary); } public CryptoNoteAddress(byte[] address){ if (address.Length != 69){ throw new ArgumentException("Invalid Cryptonote Address, binary len(..) != 69"); } setup(address); } public CryptoNoteAddress(CryptoNoteCoin coin){ this.address = new byte[59]; this.address[0] = (byte)coin; updateChecksum(); } private void setup(byte[] a){ this.address = a.Segment(0); byte[] checksum = keccak256.compute(this.address.Segment(0, 65)).Segment(0,4); if (!checksum.ArrayEquals(this.address.Segment(65,4))){ throw new ArgumentException("CheckSum failed for CryptoNote Address"); } } private void updateChecksum(){ byte[] checksum = keccak256.compute(this.address.Segment(0, 65)); Array.Copy(checksum,0,this.address,65,4); } public CryptoNoteCoin CryptoNoteCoin { get { return (CryptoNoteCoin)this.address[0]; } set { this.address[0] = (byte)value; updateChecksum(); } } public byte[] PublicSpendKey { get { return this.address.Segment(1, 32); } set { if (value.Length != 32){ throw new ArgumentException("Invalid SpendKey, len != 32"); } Array.Copy(value,0,this.address,1,32); updateChecksum(); } } public byte[] PublicViewKey { get { return this.address.Segment(33, 32); } set { if (value.Length != 32){ throw new ArgumentException("Invalid ViewKey, len != 32"); } Array.Copy(value,0,this.address,33,32); updateChecksum(); } } public byte[] CheckSum { get { return this.address.Segment(65, 4); } } public byte[] toBytes() { return this.address.Segment(0); } public override string ToString() { StringBuilder sb = new StringBuilder(); for (int n = 0; n < 9;n++){ byte[] part = this.address.Segment(n * 8, n < 8 ? 8 : 5); if (BitConverter.IsLittleEndian){ Array.Reverse(part); } UInt64 ui64 = BitConverter.ToUInt64(part.Extend(8),0); if (n == 8){ sb.Append( Base58.encode(ui64).Substring(4) ); } else { sb.Append( Base58.encode(ui64) ); } } return sb.ToString(); } private static bool base58uint64decode2array(string b58, byte[] buffer, int pos, int length = 8) { byte[] t = BitConverter.GetBytes(Base58.decode(b58)); if (BitConverter.IsLittleEndian) { Array.Reverse(t); } Array.Copy(t, t.Length - length, buffer, pos, length); return true; } struct CoinConfiguration { public readonly int CheckSumSize, PublicAddressLen; public CoinConfiguration(int palen,int CheckSumSize){ this.PublicAddressLen = palen; this.CheckSumSize = CheckSumSize; } } } }