From c4d7f08f2609ed3d8d2e9d9e5dffeb45a810a151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Novo?= <34069419+TeknoPT@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:06:15 +0000 Subject: [PATCH] Fixing Neo Address Retrivial - Added a way to retrive the user NEO address - This update is to ensure NEO address is proper retrived and --- Assets/Neo2Legacy/Core/NeoAPI.cs | 2 +- Assets/Neo2Legacy/Core/NeoKeys.cs | 57 +++++++++++--- Assets/Neo2Legacy/Core/Opcodes.cs | 8 +- Assets/Neo2Legacy/Core/Transaction.cs | 4 +- Assets/Neo2Legacy/Utils/NeoUtils.cs | 17 ++++- Assets/Neo2Legacy/Utils/Script.cs | 8 +- .../src/Cryptography/ECDsa/ECDsa.cs | 18 +++++ .../Phantasma.Core/src/Domain/WalletLink.cs | 36 +++++++++ Assets/Scripts/Wallet/AccountManager.cs | 45 ++++++++++- .../Wallet/AccountManagerClasses/Account.cs | 74 ++++++++++++++++++- .../WalletGUIFolder/WalletGUI.Password.cs | 2 + .../Wallet/WalletLink/WalletConnector.cs | 21 ++++-- ProjectSettings/ProjectVersion.txt | 4 +- 13 files changed, 260 insertions(+), 36 deletions(-) diff --git a/Assets/Neo2Legacy/Core/NeoAPI.cs b/Assets/Neo2Legacy/Core/NeoAPI.cs index 03b91f52..5141544c 100644 --- a/Assets/Neo2Legacy/Core/NeoAPI.cs +++ b/Assets/Neo2Legacy/Core/NeoAPI.cs @@ -284,7 +284,7 @@ public static byte[] GenerateScript(UInt160 scriptHash, object[] args, bool addN public void GenerateInputsOutputs(Action errorHandlingCallback, UnspentEntries unspent, NeoKeys key, string symbol, IEnumerable targets, out List inputs, out List outputs, decimal system_fee = 0, bool allowSameSourceAndDest = false) { - var from_script_hash = new UInt160(key.signatureHash.ToArray()); + var from_script_hash = new UInt160(key.signatureScriptN2.ToArray()); var info = GetAssetsInfo(); var targetAssetID = NeoUtils.ReverseHex(info[symbol]).HexToBytes(); if (targets != null) diff --git a/Assets/Neo2Legacy/Core/NeoKeys.cs b/Assets/Neo2Legacy/Core/NeoKeys.cs index ebe1695a..d36eac23 100644 --- a/Assets/Neo2Legacy/Core/NeoKeys.cs +++ b/Assets/Neo2Legacy/Core/NeoKeys.cs @@ -1,11 +1,21 @@ using Poltergeist.PhantasmaLegacy.Cryptography; using Poltergeist.PhantasmaLegacy.Neo2; using System; +using System.Diagnostics.Contracts; using System.Linq; using System.Text; +using NBitcoin.DataEncoders; using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; using Phantasma.Core.Cryptography.ECDsa; using Phantasma.Core.Cryptography; +using Phantasma.Core.Numerics; +using Phantasma.Core.Utils; +using Poltergeist.Neo2.Utils; +using UnityEngine; +using ECCurve = System.Security.Cryptography.ECCurve; +using ECPoint = System.Security.Cryptography.ECPoint; namespace Poltergeist.Neo2.Core { @@ -16,28 +26,37 @@ public class NeoKeys public readonly byte[] CompressedPublicKey; public readonly UInt160 PublicKeyHash; public readonly string Address; + public readonly string AddressN3; public readonly string WIF; - public readonly UInt160 signatureHash; - public readonly byte[] signatureScript; + public readonly UInt160 signatureHashN2; + public readonly UInt160 signatureHashN3; + public readonly byte[] signatureScriptN2; + public readonly byte[] signatureScriptN3; public NeoKeys(byte[] privateKey) { if (privateKey.Length != 32 && privateKey.Length != 96 && privateKey.Length != 104) throw new ArgumentException(); this.PrivateKey = new byte[32]; + privateKey= privateKey[^32..]; Buffer.BlockCopy(privateKey, privateKey.Length - 32, PrivateKey, 0, 32); + this.PrivateKey = privateKey[^32..]; - this.CompressedPublicKey = ECDsa.GetPublicKey(privateKey, true, ECDsaCurve.Secp256r1); - + this.CompressedPublicKey = ECDsa.GetPublicKey(this.PrivateKey, true, ECDsaCurve.Secp256r1); + this.PublicKeyHash = NeoUtils.ToScriptHash(this.CompressedPublicKey); - this.signatureScript = CreateSignatureScript(this.CompressedPublicKey); - signatureHash = NeoUtils.ToScriptHash(signatureScript); - - this.PublicKey = ECDsa.GetPublicKey(privateKey, false, ECDsaCurve.Secp256r1).Skip(1).ToArray(); - - this.Address = NeoUtils.ToAddress(signatureHash); + this.signatureScriptN2 = CreateSignatureScript(this.CompressedPublicKey); + signatureHashN2 = NeoUtils.ToScriptHash(signatureScriptN2); + + this.signatureScriptN3 = CreateSignatureScriptN3(this.CompressedPublicKey); + signatureHashN3 = NeoUtils.ToScriptHash(signatureScriptN3); + + this.PublicKey = ECDsa.GetPublicKey(this.PrivateKey, false, ECDsaCurve.Secp256r1).Skip(1).ToArray(); + + this.Address = NeoUtils.ToAddress(signatureHashN2); + this.AddressN3 = NeoUtils.ToAddressN3(signatureHashN3); this.WIF = GetWIF(); } @@ -105,6 +124,24 @@ public static byte[] CreateSignatureScript(byte[] bytes) return script; } + + public static byte[] CreateSignatureScriptN3(byte[] bytes) + { + var sb = new ScriptBuilder(); + sb.EmitPush(EncodePoint(bytes)); + sb.Emit(OpCode.SYSCALL, BitConverter.GetBytes(666101590)); + var endScript = sb.ToArray(); + + return endScript; + } + + public static byte[] EncodePoint(byte[] bytes) + { + byte[] data = new byte[33]; + Array.Copy(bytes, 0, data, 33 - bytes.Length, bytes.Length); + data[0] = (byte)0x03; + return data; + } private string GetWIF() { diff --git a/Assets/Neo2Legacy/Core/Opcodes.cs b/Assets/Neo2Legacy/Core/Opcodes.cs index 9d9d019b..1f53a13a 100644 --- a/Assets/Neo2Legacy/Core/Opcodes.cs +++ b/Assets/Neo2Legacy/Core/Opcodes.cs @@ -80,9 +80,9 @@ public enum OpCode : byte PUSHBYTES73 = 0x49, PUSHBYTES74 = 0x4A, PUSHBYTES75 = 0x4B, - PUSHDATA1 = 0x4C, // The next byte contains the number of bytes to be pushed onto the stack. - PUSHDATA2 = 0x4D, // The next two bytes contain the number of bytes to be pushed onto the stack. - PUSHDATA4 = 0x4E, // The next four bytes contain the number of bytes to be pushed onto the stack. + PUSHDATA1 = 0x0C, // The next byte contains the number of bytes to be pushed onto the stack. + PUSHDATA2 = 0x0D, // The next two bytes contain the number of bytes to be pushed onto the stack. + PUSHDATA4 = 0x0E, // The next four bytes contain the number of bytes to be pushed onto the stack. PUSHM1 = 0x4F, // The number -1 is pushed onto the stack. PUSH1 = 0x51, // The number 1 is pushed onto the stack. PUSHT = PUSH1, @@ -111,7 +111,7 @@ public enum OpCode : byte CALL = 0x65, RET = 0x66, APPCALL = 0x67, - SYSCALL = 0x68, + SYSCALL = 0x41, TAILCALL = 0x69, diff --git a/Assets/Neo2Legacy/Core/Transaction.cs b/Assets/Neo2Legacy/Core/Transaction.cs index f0d1b8d9..60b223e2 100644 --- a/Assets/Neo2Legacy/Core/Transaction.cs +++ b/Assets/Neo2Legacy/Core/Transaction.cs @@ -265,11 +265,11 @@ public void Sign(NeoKeys key, IEnumerable witnesses = null) var signature = CryptoUtils.Sign(txdata, privkey, pubkey); var invocationScript = new byte[] { (byte)OpCode.PUSHBYTES64 }.Concat(signature).ToArray(); - var verificationScript = new byte[key.signatureScript.Length]; + var verificationScript = new byte[key.signatureScriptN2.Length]; for (int i=0; i data = stackalloc byte[21]; + data[0] = version; + scriptHash.ToArray().CopyTo(data[1..]); + return data.ToArray().Base58CheckEncode(); + } public static UInt160 ToScriptHash(this byte[] script) { diff --git a/Assets/Neo2Legacy/Utils/Script.cs b/Assets/Neo2Legacy/Utils/Script.cs index fabd5f11..27e42bbd 100644 --- a/Assets/Neo2Legacy/Utils/Script.cs +++ b/Assets/Neo2Legacy/Utils/Script.cs @@ -55,12 +55,8 @@ public ScriptBuilder EmitPush(byte[] data) { if (data == null) throw new ArgumentNullException(); - if (data.Length <= (int)OpCode.PUSHBYTES75) - { - ms.WriteByte((byte)data.Length); - ms.Write(data, 0, data.Length); - } - else if (data.Length < 0x100) + + if (data.Length < 0x100) { Emit(OpCode.PUSHDATA1); ms.WriteByte((byte)data.Length); diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs index 0d634a40..b2c97e61 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs @@ -6,6 +6,7 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; +using Poltergeist.PhantasmaLegacy.Ethereum.Hex.HexConvertors.Extensions; namespace Phantasma.Core.Cryptography.ECDsa { @@ -34,10 +35,27 @@ public static byte[] GetPublicKey(byte[] privateKey, bool compressed, ECDsaCurve var d = new BigInteger(1, privateKey); var q = dom.G.Multiply(d); + var publicParams = new ECPublicKeyParameters(q, dom); return publicParams.Q.GetEncoded(compressed); } + + public static ECDomainParameters GetDomain(ECDsaCurve curve) + { + X9ECParameters ecCurve; + switch (curve) + { + case ECDsaCurve.Secp256k1: + ecCurve = SecNamedCurves.GetByName("secp256k1"); + break; + default: + ecCurve = SecNamedCurves.GetByName("secp256r1"); + break; + } + + return new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); + } public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve) { diff --git a/Assets/Phantasma/Phantasma.Core/src/Domain/WalletLink.cs b/Assets/Phantasma/Phantasma.Core/src/Domain/WalletLink.cs index b85ae65d..cee9ee9f 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Domain/WalletLink.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Domain/WalletLink.cs @@ -94,6 +94,11 @@ public struct NexusResult : IAPIResult public string nexus; } + public struct N3Address : IAPIResult + { + public string address; + } + public struct WalletVersion : IAPIResult { public string version; @@ -154,6 +159,7 @@ private Connection ValidateRequest(string[] args) protected abstract void GetPeer(Action callback); protected abstract void GetNexus(Action callback); + protected abstract void GetN3Address(Action callback); protected abstract void GetWalletVersion(Action callback); @@ -319,6 +325,30 @@ private void HandleGetNexus(string[] args, Connection connection, int id, Action } #endregion + #region N3 Address + + private void HandleGetN3Address(string[] args, Connection connection, int id, Action callback) + { + DataNode answer; + bool success = false; + + if (args.Length > 1) + { + answer = APIUtils.FromAPIResult(new Error() { message = $"getN3Address: Invalid amount of arguments: {args.Length}" }); + callback(id, answer, success); + _isPendingRequest = false; + return; + } + + GetN3Address((address) => { + success = true; + answer = APIUtils.FromAPIResult(new N3Address() { address = address }); + callback(id, answer, success); + _isPendingRequest = false; + }); + } + #endregion + #region Peer private void HandleGetPeer(string[] args, Connection connection, int id, Action callback) { @@ -847,6 +877,12 @@ public void Execute(string cmd, Action callback) HandleGetPeer(args, connection, id, callback); return; } + + case "getN3Address": + { + HandleGetN3Address(args, connection, id, callback); + return; + } case "getWalletVersion": { diff --git a/Assets/Scripts/Wallet/AccountManager.cs b/Assets/Scripts/Wallet/AccountManager.cs index a965d5d3..a454e09e 100644 --- a/Assets/Scripts/Wallet/AccountManager.cs +++ b/Assets/Scripts/Wallet/AccountManager.cs @@ -3,6 +3,7 @@ using UnityEngine; using System; +using System.IO; using System.Linq; using Phantasma.SDK; using Poltergeist.Neo2.Core; @@ -737,7 +738,6 @@ void Start() var walletVersion = PlayerPrefs.GetInt(WalletVersionTag, 1); var wallets = PlayerPrefs.GetString(WalletTag, ""); - Accounts = new List(); if (walletVersion == 1 && !string.IsNullOrEmpty(wallets)) @@ -808,7 +808,24 @@ void Start() else if (!string.IsNullOrEmpty(wallets)) { var bytes = Base16.Decode(wallets); - Accounts = Serialization.Unserialize(bytes).ToList(); + try + { + ListaccountsTemp = new List(); + var reader = new BinaryReader(new MemoryStream(bytes)); + var size = reader.ReadVarInt(); + for (int i = 0; i < (int)size; i++) + { + var account = new Account(); + account.UnserializeData(reader); + accountsTemp.Add(account); + } + + Accounts = accountsTemp; // = Serialization.Unserialize(bytes).ToList(); + } + catch (Exception e) + { + Log.WriteError("Error deserializing accounts: " + e); + } } if (walletVersion == 2) @@ -830,8 +847,17 @@ void Start() public void SaveAccounts() { PlayerPrefs.SetInt(WalletVersionTag, 3); + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Accounts.ForEach(acc => acc.version = 3); + + writer.WriteVarInt(Accounts.Count); + foreach (var account in Accounts) + { + account.SerializeData(writer); + } - var bytes = Serialization.Serialize(Accounts.ToArray()); + var bytes = stream.ToArray();//Serialization.Serialize(Accounts.ToArray()); PlayerPrefs.SetString(WalletTag, Base16.Encode(bytes)); PlayerPrefs.Save(); } @@ -1863,6 +1889,7 @@ public void RefreshBalances(bool force, PlatformKind platforms = PlatformKind.No { var keys = PhantasmaKeys.FromWIF(wif); var ethKeys = EthereumKey.FromWIF(wif); + UpdateOpenAccount(); StartCoroutine(phantasmaApi.GetAccount(keys.Address.Text, (acc) => { var balanceMap = new Dictionary(); @@ -3236,6 +3263,7 @@ public int AddWallet(string name, string wif, string password, bool legacySeed) var neoKeys = NeoKeys.FromWIF(wif); account.neoAddress = neoKeys.Address.ToString(); + account.neoAddress = neoKeys.AddressN3.ToString(); var ethereumAddressUtil = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil(); account.ethAddress = ethereumAddressUtil.ConvertToChecksumAddress(EthereumKey.FromWIF(wif).Address); @@ -3982,5 +4010,16 @@ public void GetPhantasmaAddressInfo(string addressString, Action }); } + + public void UpdateOpenAccount() + { + NeoKeys neoKeys = NeoKeys.FromWIF(CurrentWif); + var SelectedAccount = CurrentAccount; + SelectedAccount.neoAddressN3 = neoKeys.AddressN3; + SelectedAccount.neoAddress = neoKeys.Address; + SelectedAccount.version = 3; + Accounts[CurrentIndex] = SelectedAccount; + SaveAccounts(); + } } } diff --git a/Assets/Scripts/Wallet/AccountManagerClasses/Account.cs b/Assets/Scripts/Wallet/AccountManagerClasses/Account.cs index e74fdc99..75336a4a 100644 --- a/Assets/Scripts/Wallet/AccountManagerClasses/Account.cs +++ b/Assets/Scripts/Wallet/AccountManagerClasses/Account.cs @@ -1,8 +1,12 @@ using System; +using System.IO; +using JetBrains.Annotations; +using Phantasma.Core.Domain; +using Phantasma.Core.Utils; namespace Poltergeist { - public struct Account + public struct Account : ISerializable { public string name; public PlatformKind platforms; @@ -16,7 +20,9 @@ public struct Account public string iv; public string password; // Not used after account upgrade to version 2. public string misc; - + public string neoAddressN3; + public int version; + public override string ToString() { return $"{name.ToUpper()} [{platforms}]"; @@ -26,5 +32,69 @@ public string GetWif(string passwordHash) { return String.IsNullOrEmpty(passwordHash) ? WIF : AccountManager.DecryptString(WIF, passwordHash, iv); } + + public void SerializeData(BinaryWriter writer) + { + writer.WriteVarString(name); + uint platformsVal = (uint)Convert.ChangeType(platforms, typeof(uint)); + writer.WriteVarInt(platformsVal); + writer.WriteVarString(phaAddress); + writer.WriteVarString(neoAddress); + writer.WriteVarString(ethAddress); + writer.WriteVarString(WIF); + writer.Write((byte)(((bool)passwordProtected) ? 1 : 0)); + writer.Write((int)passwordIterations); + writer.WriteVarString(salt); + writer.WriteVarString(iv); + writer.WriteVarString(password); + writer.WriteVarString(misc); + if (version == 3) + { + writer.WriteVarString(!string.IsNullOrEmpty(neoAddressN3) ? neoAddressN3 : ""); + writer.Write((int)version); + } + } + + public void UnserializeData(BinaryReader reader) + { + name = reader.ReadVarString(); + var platformsVal = (uint)reader.ReadVarInt(); + platforms = PlatformKind.Parse(platformsVal.ToString()); + phaAddress = reader.ReadVarString(); + neoAddress = reader.ReadVarString(); + ethAddress = reader.ReadVarString(); + WIF = reader.ReadVarString(); + passwordProtected = reader.ReadByte() != 0; + passwordIterations = reader.ReadInt32(); + salt = reader.ReadVarString(); + iv = reader.ReadVarString(); + password = reader.ReadVarString(); + misc = reader.ReadVarString(); + var headerReader = reader.BaseStream.Position; + try + { + neoAddressN3 = reader.ReadVarString(); + version = reader.ReadInt32() != 3 ? 2 : 3; + + if (version == 2) + { + reader.BaseStream.Position = headerReader; + neoAddressN3 = ""; + version = 2; + return; + } + + if (string.IsNullOrEmpty(neoAddressN3) || !neoAddressN3.StartsWith("N") || neoAddressN3.Length < 21 ) + { + return; + } + } + catch (Exception) + { + reader.BaseStream.Position = headerReader; + neoAddressN3 = null; + version = 2; + } + } } } diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Password.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Password.cs index 8a1b12f7..31f300a7 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Password.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Password.cs @@ -2,6 +2,7 @@ using UnityEngine; using Phantasma.SDK; using Phantasma.Core.Cryptography; +using Poltergeist.Neo2.Core; namespace Poltergeist { @@ -24,6 +25,7 @@ private void TryPassword(string password, string description, PlatformKind platf if (PhantasmaKeys.FromWIF(wif).Address.ToString() == accountManager.CurrentAccount.phaAddress) { accountManager.CurrentPasswordHash = passwordHash; + accountManager.UpdateOpenAccount(); callback(PromptResult.Success); } else diff --git a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs index 8e2f7f41..45970dc5 100644 --- a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs +++ b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs @@ -158,9 +158,9 @@ protected override void GetAccount(string platform, int version, Action callback) @@ -181,6 +187,11 @@ protected override void GetNexus(Action callback) { callback(AccountManager.Instance.Settings.nexusName); } + + protected override void GetN3Address(Action callback) + { + callback(AccountManager.Instance.CurrentAccount.neoAddressN3); + } protected override void GetWalletVersion(Action callback) { diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 49431cff..0ab53b0c 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2022.3.7f1 -m_EditorVersionWithRevision: 2022.3.7f1 (b16b3b16c7a0) \ No newline at end of file +m_EditorVersion: 2022.3.16f1 +m_EditorVersionWithRevision: 2022.3.16f1 (d2c21f0ef2f1)