Commit 1143b556 authored by Arcange's avatar Arcange
Browse files

Broadcast transactions implemented

parent 8dd9ad67
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Cryptography.ECDSA;
namespace HiveAPI.CS
{
public class CBase58 : Cryptography.ECDSA.Base58
{
public static byte[] DecodePrivateWif(string data)
{
if (data.All(Hexdigits.Contains))
return Hex.HexToBytes(data);
switch (data[0])
{
case '5':
case '6':
return Base58CheckDecode(data);
case 'K':
case 'L':
return CutLastBytes(Base58CheckDecode(data), 1);
default:
throw new NotImplementedException();
}
}
public static string EncodePrivateWif(byte[] source)
{
return Base58CheckEncode(0x80, source);
}
public static byte[] DecodePublicWif(string publicKey, string prefix)
{
if (!publicKey.StartsWith(prefix))
return new byte[0];
var buf = publicKey.Remove(0, prefix.Length);
var s = Decode(buf);
var checksum = BitConverter.ToInt32(s, s.Length - CheckSumSizeInBytes);
var dec = RemoveCheckSum(s);
var hash = Ripemd160Manager.GetHash(dec);
var newChecksum = BitConverter.ToInt32(hash, 0);
if (checksum != newChecksum)
throw new ArithmeticException(nameof(checksum));
return dec;
}
public static string EncodePublicWif(byte[] publicKey, string prefix)
{
var checksum = Ripemd160Manager.GetHash(publicKey);
var s = AddLastBytes(publicKey, CheckSumSizeInBytes);
Array.Copy(checksum, 0, s, s.Length - CheckSumSizeInBytes, CheckSumSizeInBytes);
var pubdata = Encode(s);
return prefix + pubdata;
}
public static byte[] Base58CheckDecode(string data)
{
var s = Decode(data);
var dec = CutLastBytes(s, CheckSumSizeInBytes);
var checksum = DoubleHash(dec);
for (var i = 0; i < CheckSumSizeInBytes; i++)
{
if (checksum[i] != s[s.Length - CheckSumSizeInBytes + i])
throw new ArithmeticException("Invalide data");
}
return CutFirstBytes(dec, 1);
}
public static string Base58CheckEncode(byte version, byte[] data)
{
var s = AddFirstBytes(data, 1);
s[0] = version;
var checksum = DoubleHash(s);
s = AddLastBytes(s, CheckSumSizeInBytes);
Array.Copy(checksum, 0, s, s.Length - CheckSumSizeInBytes, CheckSumSizeInBytes);
return Encode(s);
}
public static string GetSubWif(string name, string password, string role)
{
var seed = name + role + password;
seed = Regex.Replace(seed, @"\s+", " ");
var brainKey = Encoding.ASCII.GetBytes(seed);
var hashSha256 = Sha256Manager.GetHash(brainKey);
return EncodePrivateWif(hashSha256);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
......@@ -8,7 +7,7 @@ using Newtonsoft.Json.Linq;
namespace HiveAPI.CS
{
class CHiveAPI
public class CHiveAPI
{
#region Constants
const string STR_RESULT = "result";
......@@ -63,54 +62,18 @@ namespace HiveAPI.CS
return t.Result;
}
}
private object ProcessResult(Dictionary<string, JObject> oDic)
{
if (oDic[STR_RESULT] != null)
{
return oDic[STR_RESULT];
}
if (oDic[STR_ERROR] != null)
{
throw new Exception((string)oDic[STR_ERROR]);
}
throw new Exception(STR_FORMAT);
}
private object ProcessResult(Dictionary<string, JArray> oDic)
{
if (oDic[STR_RESULT] != null)
{
return oDic[STR_RESULT];
}
if (oDic[STR_ERROR] != null)
{
throw new Exception((string)oDic[STR_ERROR]);
}
throw new Exception(STR_FORMAT);
}
private object ProcessResult(Dictionary<string, JToken> oDic)
{
if (oDic[STR_RESULT] != null)
{
return oDic[STR_RESULT];
}
if (oDic[STR_ERROR] != null)
{
throw new Exception((string)oDic[STR_ERROR]);
}
throw new Exception(STR_FORMAT);
}
private object ProcessResult(Dictionary<string, JValue> oDic)
{
if (oDic[STR_RESULT] != null)
{
return oDic[STR_RESULT];
}
if (oDic[STR_ERROR] != null)
{
throw new Exception((string)oDic[STR_ERROR]);
}
throw new Exception(STR_FORMAT);
}
private object ProcessResult(JObject obj)
{
if (obj[STR_RESULT] != null)
{
return obj[STR_RESULT];
}
if (obj[STR_ERROR] != null)
{
throw new Exception(obj[STR_ERROR].ToString());
}
throw new Exception(STR_FORMAT);
}
#endregion
#region protected methods
......@@ -124,37 +87,35 @@ namespace HiveAPI.CS
}
protected JObject call_api(string strMethod)
{
return (JObject)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JObject>>(SendRequest(strMethod)));
return (JObject)ProcessResult(JsonConvert.DeserializeObject<JObject>(SendRequest(strMethod)));
}
protected JObject call_api(string strMethod, ArrayList arrParams)
{
return (JObject)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JObject>>(SendRequest(strMethod, arrParams)));
}
protected JArray call_api_array(string strMethod, ArrayList arrParams)
{
return (JArray)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JArray>>(SendRequest(strMethod, arrParams)));
return (JObject)ProcessResult(JsonConvert.DeserializeObject<JObject>(SendRequest(strMethod, arrParams)));
}
protected JArray call_api_array(string strMethod)
protected JArray call_api_array(string strMethod)
{
return (JArray)ProcessResult(JsonConvert.DeserializeObject<JObject>(SendRequest(strMethod)));
}
protected JArray call_api_array(string strMethod, ArrayList arrParams)
{
return (JArray)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JArray>>(SendRequest(strMethod)));
return (JArray)ProcessResult(JsonConvert.DeserializeObject<JObject>(SendRequest(strMethod, arrParams)));
}
protected JValue call_api_value(string strMethod)
{
return (JValue)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JValue>>(SendRequest(strMethod)));
return (JValue)ProcessResult(JsonConvert.DeserializeObject<JObject> (SendRequest(strMethod)));
}
protected JValue call_api_value(string strMethod, ArrayList arrParams)
{
return (JValue)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JValue>>(SendRequest(strMethod, arrParams)));
return (JValue)ProcessResult(JsonConvert.DeserializeObject<JObject>(SendRequest(strMethod, arrParams)));
}
protected JToken call_api_token(string strMethod, ArrayList arrParams)
{
return (JToken)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JToken>>(SendRequest(strMethod, arrParams)));
return (JToken)ProcessResult(JsonConvert.DeserializeObject<JObject>(SendRequest(strMethod, arrParams)));
}
protected JToken call_api_token(string strMethod)
{
return (JToken)ProcessResult(JsonConvert.DeserializeObject<Dictionary<string, JToken>>(SendRequest(strMethod)));
return (JToken)ProcessResult(JsonConvert.DeserializeObject<JObject> (SendRequest(strMethod)));
}
#endregion
}
......
......@@ -780,9 +780,9 @@ namespace HiveAPI.CS
return call_api(MethodBase.GetCurrentMethod().Name, arrParams);
}
// Transfers into savings happen immediately, transfers from savings take 72 hours
// Transfers into savings happen immediately
//
public JObject transfer_to_savings(string from, string to, string amount, string memo, bool broadcast = false)
public JObject transfer_to_savings(string from, string to, string amount, string memo, bool broadcast = true)
{
ArrayList arrParams = new ArrayList();
arrParams.Add(from);
......@@ -793,9 +793,10 @@ namespace HiveAPI.CS
}
// @param request_id - an unique ID assigned by from account, the id is used to cancel the operation and can be reused after the transfer completes
// Transfers transfers from savings take 72 hours
// request_id - an unique ID assigned by from account, the id is used to cancel the operation and can be reused after the transfer completes
//
public JObject transfer_from_savings(string from, UInt32 request_id, string to, string amount, string memo, bool broadcast = false)
public JObject transfer_from_savings(string from, UInt32 request_id, string to, string amount, string memo, bool broadcast = true)
{
ArrayList arrParams = new ArrayList();
arrParams.Add(from);
......@@ -806,10 +807,10 @@ namespace HiveAPI.CS
return call_api(MethodBase.GetCurrentMethod().Name, arrParams);
}
// @param request_id the id used in transfer_from_savings
// @param from the account that initiated the transfer
// @param request_id the id used in transfer_from_savings
//
public JObject cancel_transfer_from_savings(string from, UInt32 request_id, bool broadcast = false)
public JObject cancel_transfer_from_savings(string from, UInt32 request_id, bool broadcast = true)
{
ArrayList arrParams = new ArrayList();
arrParams.Add(from);
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Reflection;
using Cryptography.ECDSA;
using Newtonsoft.Json.Linq;
namespace HiveAPI.CS
{
class CHived : CHiveAPI
public class CHived : CHiveAPI
{
#region Constants
const string DATABASE_API = "database_api.";
......@@ -15,6 +17,8 @@ namespace HiveAPI.CS
const string BLOCK_API = "block_api.";
const string CONDENSER_API = "condenser_api.";
const string HIVE_API = "hive.";
const string CHAINID = "beeab0de00000000000000000000000000000000000000000000000000000000";
#endregion
#region Constructors
......@@ -23,7 +27,73 @@ namespace HiveAPI.CS
}
#endregion
#region Public Methods
#region private Methods
private class CTransaction
{
public UInt16 ref_block_num;
public UInt32 ref_block_prefix;
public DateTime expiration;
public Object[] operations;
public Object[] extensions = { };
public string[] signatures = { };
}
private class CtransactionData
{
public CTransaction tx;
public string txid;
}
private CtransactionData SignTransaction(CTransaction oTransaction, String[] astrPrivateKeys)
{
CSerializer oSerializer = new CSerializer();
byte[] msg = oSerializer.Serialize(oTransaction);
using (MemoryStream oStream = new MemoryStream())
{
byte[] oChainID = Hex.HexToBytes(CHAINID);
oStream.Write(oChainID, 0, oChainID.Length);
oStream.Write(msg, 0, msg.Length);
byte[] oDigest = Sha256Manager.GetHash(oStream.ToArray());
foreach (string key in astrPrivateKeys)
{
Array.Resize(ref oTransaction.signatures, oTransaction.signatures.Length + 1);
oTransaction.signatures[oTransaction.signatures.Length-1] = Hex.ToString(Secp256K1Manager.SignCompressedCompact(oDigest, CBase58.DecodePrivateWif(key)));
}
}
return new CtransactionData { tx = oTransaction, txid = Hex.ToString(Sha256Manager.GetHash(msg)).Substring(0, 40) };
}
private CtransactionData CreateTransaction(Object[] aOperations, string[] astrPrivateKeys)
{
JObject oDGP = get_dynamic_global_properties();
CTransaction oTransaction = new CTransaction {
ref_block_num = Convert.ToUInt16((UInt32)oDGP["head_block_number"] & 0xFFFF),
ref_block_prefix = BitConverter.ToUInt32(Hex.HexToBytes(oDGP["head_block_id"].ToString()), 4),
expiration = Convert.ToDateTime(oDGP["time"]).AddSeconds(30),
operations = aOperations
};
return SignTransaction(oTransaction, astrPrivateKeys);
}
#endregion
#region public Methods
public string broadcast_transaction(Object[] operations, string[] keys)
{
CtransactionData oTransaction = CreateTransaction(operations, keys);
for(int i = 0; i < oTransaction.tx.operations.Length; i++)
{
object op = oTransaction.tx.operations[i];
oTransaction.tx.operations[i] = new Op { name = op.GetType().Name, payload = op };
}
ArrayList arrParams = new ArrayList();
arrParams.Add(oTransaction.tx);
call_api(CONDENSER_API + MethodBase.GetCurrentMethod().Name, arrParams);
return oTransaction.txid;
}
public JObject get_info()
{
return call_api(HIVE_API + MethodBase.GetCurrentMethod().Name);
......@@ -263,7 +333,7 @@ namespace HiveAPI.CS
//
// from - the absolute sequence number, -1 means most recent, limit Is the number of operations before from.
// limit - the maximum number of items that can be queried (0 to 1000], must be less than from
public JToken get_account_history(string account, UInt64 from , UInt32 limit)
public JToken get_account_history(string account, Int64 from , UInt32 limit)
{
ArrayList arrParams = new ArrayList();
arrParams.Add(account);
......
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Cryptography.ECDSA;
using Newtonsoft.Json;
namespace HiveAPI.CS
{
[AttributeUsage(AttributeTargets.Field)] public class OptionalField : Attribute {}
public class AssetJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
[JsonConverter(typeof(AssetJsonConverter))] public class Asset
{
public decimal amount;
public string symbol;
private string CheckSymbol(string strSymbol)
{
string[] aSymbols = { "HIVE", "HBD", "TESTS", "TBD", "VESTS" };
if (string.IsNullOrEmpty(strSymbol) || !aSymbols.Contains(strSymbol))
{
throw new Exception(string.Format("Invalid asset symbol: {0}", strSymbol));
}
return strSymbol;
}
public Asset(decimal strAmount, string strSymbol)
{
amount = strAmount;
symbol = CheckSymbol(strSymbol);
}
public Asset(string str)
{
string[] astr = str.Split(' ');
if (astr.Length < 2)
{
throw new Exception("Invalid asset string");
}
if (string.IsNullOrEmpty(astr[0]))
{
throw new Exception("Invalid asset amount (null or empty)");
}
amount = decimal.Parse(astr[0], CultureInfo.InvariantCulture);
symbol = CheckSymbol(astr[1]);
}
public int GetPrecision()
{
string[] asymbols = { "HIVE", "HBD", "TESTS", "TBD" };
return asymbols.Contains(symbol) ? 3 : 6;
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0:0." + new string('0', GetPrecision()) + "} {1}", amount, symbol);
}
}
public class PublicKeyJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
{
return new PublicKey((string)reader.Value);
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
[JsonConverter(typeof(PublicKeyJsonConverter))] public class PublicKey
{
public string key;
public PublicKey(string strKey )
{
key = strKey;
}
public override string ToString()
{
return key;
}
public Byte[] Decode()
{
return Base58.RemoveCheckSum(Base58.Decode(key.Substring(3)));
}
}
public class Price
{
public Asset @base;
public Asset quote;
}
public class AccountAuthsJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
{
Dictionary<string, UInt16> auths = (Dictionary<string, UInt16>)value;
writer.WriteStartArray();
foreach (KeyValuePair<string, UInt16> auth in auths)
{
writer.WriteStartArray();
writer.WriteValue(auth.Key);
writer.WriteValue(auth.Value);
writer.WriteEndArray();
}
writer.WriteEndArray();
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
{
return new PublicKey((string)reader.Value);
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
public class KeyAuthsJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
{
Dictionary<PublicKey, UInt16> auths = (Dictionary<PublicKey, UInt16>)value;
writer.WriteStartArray();
foreach (KeyValuePair<PublicKey, UInt16> auth in auths)
{
writer.WriteStartArray();
writer.WriteValue(auth.Key.ToString());
writer.WriteValue(auth.Value);
writer.WriteEndArray();
}
writer.WriteEndArray();
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
{
return new PublicKey((string)reader.Value);
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
public class Authority
{
public UInt32 weight_threshold;
[JsonConverter(typeof(AccountAuthsJsonConverter))] public Dictionary<string, UInt16> account_auths = new Dictionary<string, UInt16>();
[JsonConverter(typeof(KeyAuthsJsonConverter))] public Dictionary<PublicKey, UInt16> key_auths = new Dictionary<PublicKey, UInt16>();
}
public class ChainProperties
{
public Asset account_creation_fee;
public UInt32 maximum_block_size;
public UInt16 hbd_interest_rate;
}
public class WitnessProperties
{
public Asset account_creation_fee;
public UInt32 account_voidsidy_budget;
public UInt32 account_voidsidy_decay;
public UInt32 maximum_block_size;
public UInt16 hbd_interest_rate;
public Price hbd_exchange_rate;
public string url;
public PublicKey new_signing_key;
}
public class OpJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Op op = (Op)value;
writer.WriteStartArray();
writer.WriteValue(op.name);
serializer.Serialize(writer,op.payload);
writer.WriteEndArray();
}
}
[JsonConverter(typeof(OpJsonConverter))] public class Op
{
public string name;
public object payload;
}
public class COperations
{
public interface IOperationID
{
int opid { get; }
}
public class vote : IOperationID
{
public int opid => 0;
public string voter;
public string author;
public string permlink;