From dff1a4f20fa6cce906b33fd977deaeb0251ea439 Mon Sep 17 00:00:00 2001 From: Thomas Saunders Date: Fri, 3 Nov 2017 07:03:59 -0500 Subject: [PATCH] adding ability to import NEP5 token contract --- neo/Implementations/Wallets/peewee/Models.py | 9 +++- .../Wallets/peewee/UserWallet.py | 42 ++++++++++++++++--- neo/Prompt/Commands/Invoke.py | 2 +- neo/Prompt/Commands/Wallet.py | 35 ++++++++++++++-- neo/SmartContract/ApplicationEngine.py | 40 ++++++++++++++++++ neo/Wallets/Wallet.py | 13 +++++- prompt.py | 5 ++- protocol.testnet.json | 2 +- 8 files changed, 134 insertions(+), 14 deletions(-) diff --git a/neo/Implementations/Wallets/peewee/Models.py b/neo/Implementations/Wallets/peewee/Models.py index 3defe6615..d7396a336 100755 --- a/neo/Implementations/Wallets/peewee/Models.py +++ b/neo/Implementations/Wallets/peewee/Models.py @@ -42,10 +42,17 @@ class Contract(ModelBase): class Key(ModelBase): Id = PrimaryKeyField() - Name = CharField(unique=True, ) + Name = CharField(unique=True) Value = CharField() +class NEP5Token(ModelBase): + ContractHash = CharField(unique=True) + Name = CharField() + Symbol = CharField() + Decimals = IntegerField() + + class Transaction(ModelBase): Id = PrimaryKeyField() Hash = CharField(unique=True) diff --git a/neo/Implementations/Wallets/peewee/UserWallet.py b/neo/Implementations/Wallets/peewee/UserWallet.py index 5658c9335..babe36180 100755 --- a/neo/Implementations/Wallets/peewee/UserWallet.py +++ b/neo/Implementations/Wallets/peewee/UserWallet.py @@ -3,7 +3,6 @@ from neo.Wallets.Wallet import Wallet from neo.Wallets.Coin import Coin as WalletCoin from neo.SmartContract.Contract import Contract as WalletContract -from neo.Wallets.KeyPair import KeyPair as WalletKeyPair from neo.IO.Helper import Helper from neo.Core.Blockchain import Blockchain from neo.Core.CoinReference import CoinReference @@ -11,20 +10,20 @@ from neo.Core.TX.Transaction import Transaction as CoreTransaction from neo.Wallets.KeyPair import KeyPair as WalletKeyPair +from neo.Wallets.NEP5Token import NEP5Token as WalletNEP5Token from Crypto import Random from neo.Cryptography.Crypto import Crypto -import os from neo.UInt160 import UInt160 import binascii -import pdb from neo.Fixed8 import Fixed8 from neo.UInt160 import UInt160 from neo.UInt256 import UInt256 from .PWDatabase import PWDatabase -from neo.Implementations.Wallets.peewee.Models import Account, Address, Coin, Contract, Key, Transaction, \ - TransactionInfo +from neo.Implementations.Wallets.peewee.Models import Account, Address, Coin, \ + Contract, Key, Transaction, \ + TransactionInfo, NEP5Token from autologging import logged import json @@ -49,7 +48,7 @@ def BuildDatabase(self): PWDatabase.Initialize(self._path) db = PWDatabase.ContextDB() try: - db.create_tables([Account, Address, Coin, Contract, Key, Transaction, TransactionInfo, ], safe=True) + db.create_tables([Account, Address, Coin, Contract, Key, NEP5Token, Transaction, TransactionInfo, ], safe=True) except Exception as e: print("Couldnt build database %s " % e) self.__log.debug("couldnt build database %s " % e) @@ -163,6 +162,27 @@ def AddWatchOnly(self, script_hash): else: raise Exception("Address already exists in wallet") + + def AddNEP5Token(self,token): + + super(UserWallet, self).AddNEP5Token(token) + + try: + db_token = NEP5Token.get(ContractHash=token.ScriptHash.ToBytes()) + db_token.delete_instance() + except Exception as e: + pass + + db_token = NEP5Token.create( + ContractHash = token.ScriptHash.ToBytes(), + Name = token.name, + Symbol = token.symbol, + Decimals = token.decimals + ) + db_token.save() + return True + + def FindUnspentCoins(self, from_addr=None, use_standard=False, watch_only_val=0): return super(UserWallet, self).FindUnspentCoins(from_addr, use_standard, watch_only_val=watch_only_val) @@ -231,6 +251,16 @@ def LoadKeyPairs(self): return keypairs + def LoadNEP5Tokens(self): + tokens = {} + + for db_token in NEP5Token.select(): + token = WalletNEP5Token.FromDBInstance(db_token) + print("token %s " % json.dumps(token.ToJson(), indent=4)) + tokens[token.ScriptHash.ToBytes()] = token + + return tokens + def LoadStoredData(self, key): self.__log.debug("Looking for key %s " % key) try: diff --git a/neo/Prompt/Commands/Invoke.py b/neo/Prompt/Commands/Invoke.py index ce6850c9f..218a7f2ec 100644 --- a/neo/Prompt/Commands/Invoke.py +++ b/neo/Prompt/Commands/Invoke.py @@ -140,11 +140,11 @@ def TestInvokeContract(wallet, args, withdrawal_tx=None, parse_params=True): contract = BC.GetContract(args[0]) if contract: - descripe_contract(contract) verbose = False if 'verbose' in args: + descripe_contract(contract) verbose = True args.remove('verbose') diff --git a/neo/Prompt/Commands/Wallet.py b/neo/Prompt/Commands/Wallet.py index 548ad87d3..395d0f874 100644 --- a/neo/Prompt/Commands/Wallet.py +++ b/neo/Prompt/Commands/Wallet.py @@ -1,4 +1,7 @@ - +from neo.Core.Blockchain import Blockchain +from neo.Wallets.NEP5Token import NEP5Token +import binascii +import json def DeleteAddress(prompter, wallet, addr): @@ -21,8 +24,34 @@ def ImportWatchAddr(wallet, addr): script_hash = wallet.ToScriptHash(addr) - print("will import watch address %s %s " % (addr, script_hash)) - result = wallet.AddWatchOnly(script_hash) print("result %s " % result) + + +def ImportToken(wallet, contract_hash): + + if wallet is None: + print("please open a wallet") + return False + + contract = Blockchain.Default().GetContract(contract_hash) + + if contract: + hex_script = binascii.hexlify(contract.Code.Script) + token = NEP5Token(script= hex_script) + + result = token.Query(wallet) + + if result: + + print("queried token %s " % json.dumps(token.ToJson(), indent=4)) + + wallet.AddNEP5Token(token) + + else: + + print("Could not import token") + + + diff --git a/neo/SmartContract/ApplicationEngine.py b/neo/SmartContract/ApplicationEngine.py index 6986c366c..3937d2585 100644 --- a/neo/SmartContract/ApplicationEngine.py +++ b/neo/SmartContract/ApplicationEngine.py @@ -7,6 +7,12 @@ import os from autologging import logged +# used for ApplicationEngine.Run +from neo.Implementations.Blockchains.LevelDB.DBPrefix import DBPrefix +from neo.Implementations.Blockchains.LevelDB.DBCollection import DBCollection +from neo.Implementations.Blockchains.LevelDB.CachedScriptTable import CachedScriptTable +from neo.Core.State import ContractState,AssetState,AccountState,ValidatorState,StorageItem +from neo.SmartContract import TriggerType @logged class ApplicationEngine(ExecutionEngine): @@ -291,3 +297,37 @@ def GetPriceForSysCall(self): return 100 return 1 + + + + @staticmethod + def Run(script, container=None): + + from neo.Core.Blockchain import Blockchain + from neo.SmartContract.StateMachine import StateMachine + + bc = Blockchain.Default() + + sn = bc._db.snapshot() + + accounts = DBCollection(bc._db, sn, DBPrefix.ST_Account, AccountState) + assets = DBCollection(bc._db, sn, DBPrefix.ST_Asset, AssetState) + validators = DBCollection(bc._db, sn, DBPrefix.ST_Validator, ValidatorState) + contracts = DBCollection(bc._db, sn, DBPrefix.ST_Contract, ContractState) + storages = DBCollection(bc._db, sn, DBPrefix.ST_Storage, StorageItem) + + script_table = CachedScriptTable(contracts) + service = StateMachine(accounts, validators, assets, contracts, storages, None) + + engine = ApplicationEngine( + trigger_type=TriggerType.Application, + container=container, + table=script_table, + service=service, + gas=Fixed8.Zero(), + testMode=True + ) + + engine.LoadScript(script, False) + engine.Execute() + return engine diff --git a/neo/Wallets/Wallet.py b/neo/Wallets/Wallet.py index 5b5413792..6e5741323 100755 --- a/neo/Wallets/Wallet.py +++ b/neo/Wallets/Wallet.py @@ -40,7 +40,7 @@ class Wallet(object): _master_key = None _keys = {} # holds keypairs _contracts = {} # holds Contracts - + _tokens = {} # holds references to NEP5 tokens _watch_only = [] # holds set of hashes _coins = {} # holds Coin References @@ -110,6 +110,7 @@ def __init__(self, path, passwordKey, create): self._keys = self.LoadKeyPairs() self._contracts = self.LoadContracts() self._watch_only = self.LoadWatchOnly() + self._tokens = self.LoadNEP5Tokens() self._coins = self.LoadCoins() try: h = int(self.LoadStoredData('Height')) @@ -143,6 +144,12 @@ def AddWatchOnly(self, script_hash): self._watch_only.append(script_hash) + def AddNEP5Token(self, token): + if token.ScriptHash.ToBytes() in self._tokens.keys(): + print("Token already in wallet") + return + self._tokens[token.ScriptHash.ToBytes()] = token + def ChangePassword(self, password_old, password_new): if not self.ValidatePassword(password_old): return False @@ -332,6 +339,10 @@ def LoadCoins(self): # abstract pass + def LoadNEP5Tokens(self): + # abstract + pass + def ProcessBlocks(self): # start = time.clock() diff --git a/prompt.py b/prompt.py index 01ecd3692..aba03757a 100644 --- a/prompt.py +++ b/prompt.py @@ -20,7 +20,7 @@ from neo.Prompt.Commands.Withdraw import RequestWithdraw, RedeemWithdraw from neo.Prompt.Commands.LoadSmartContract import LoadContract, GatherContractDetails, ImportContractAddr, ImportMultiSigContractAddr from neo.Prompt.Commands.Send import construct_and_send, parse_and_sign -from neo.Prompt.Commands.Wallet import DeleteAddress, ImportWatchAddr +from neo.Prompt.Commands.Wallet import DeleteAddress, ImportWatchAddr,ImportToken from neo.Prompt.Utils import get_arg from neo.Prompt.Notify import SubscribeNotifications from neo.Settings import settings @@ -306,6 +306,9 @@ def do_import(self, arguments): elif item == 'multisig_addr': return ImportMultiSigContractAddr(self.Wallet, arguments[1:]) + elif item == 'token': + return ImportToken(self.Wallet, get_arg(arguments, 1)) + print("please specify something to import") return diff --git a/protocol.testnet.json b/protocol.testnet.json index 5a4616ac5..da1e1f16e 100644 --- a/protocol.testnet.json +++ b/protocol.testnet.json @@ -9,7 +9,7 @@ ], "VersionName": "/NEO-PYTHON:2.3.4/", "WsPort": 20334, - "theme": "light", + "theme": "dark", "themes": { "dark": { "Command": "#ff0066",