예제 #1
0
        public static void GenerateNFTDummyScript(string symbol, string name, string description, string jsonURL, string imgURL, out byte[] script, out ContractInterface abi)
        {
            if (!jsonURL.EndsWith("\\"))
            {
                jsonURL += '\\';
            }

            if (!imgURL.EndsWith("\\"))
            {
                imgURL += '\\';
            }

            var sb = new StringBuilder();

            GenerateStringScript(sb, "getName", name);
            GenerateStringScript(sb, "getDescription", description);
            GenerateStringScript(sb, "getInfoURL", jsonURL);
            GenerateStringScript(sb, "getImageURL", imgURL);

            var asm = sb.ToString().Split('\n');

            DebugInfo debugInfo;
            Dictionary <string, int> labels;

            script = CodeGen.Assembler.AssemblerUtils.BuildScript(asm, "dummy", out debugInfo, out labels);

            var standardABI = GetNFTStandard();

            var methods = standardABI.Methods.Select(x => new ContractMethod(x.name, x.returnType, labels[x.name], x.parameters));

            abi = new ContractInterface(methods, Enumerable.Empty <ContractEvent>());
        }
예제 #2
0
        private static VMObject ExecuteScript(Chain chain, byte[] script, ContractInterface abi, string methodName, params object[] args)
        {
            var method = abi.FindMethod(methodName);

            if (method == null)
            {
                throw new Exception("ABI is missing: " + method.name);
            }

            var changeSet = new StorageChangeSetContext(chain.Storage);
            var oracle    = chain.Nexus.GetOracleReader();
            var vm        = new RuntimeVM(-1, script, (uint)method.offset, chain, Address.Null, Timestamp.Now, null, changeSet, oracle, ChainTask.Null, true);

            //var vm = new GasMachine(script, (uint)method.offset);

            // TODO maybe this needs to be in inverted order?
            foreach (var arg in args)
            {
                vm.Stack.Push(VMObject.FromObject(arg));
            }

            var result = vm.Execute();

            if (result == ExecutionState.Halt)
            {
                return(vm.Stack.Pop());
            }

            throw new Exception("Script execution failed for: " + method.name);
        }
예제 #3
0
        public TokenInfo(string symbol, string name, Address owner, BigInteger maxSupply, int decimals, TokenFlags flags, byte[] script, ContractInterface ABI)
        {
            Throw.IfNullOrEmpty(symbol, nameof(symbol));
            Throw.IfNullOrEmpty(name, nameof(name));
            Throw.If(decimals < 0, "decimals can't be negative");
            Throw.If(flags == TokenFlags.None, "token must have flags set");
            Throw.If(script == null || script.Length == 0, "token script can't be empty");

            Throw.If(maxSupply < 0, "negative supply");
            Throw.If(maxSupply == 0 && flags.HasFlag(TokenFlags.Finite), "finite requires a supply");
            Throw.If(maxSupply > 0 && !flags.HasFlag(TokenFlags.Finite), "infinite requires no supply");

            if (!flags.HasFlag(TokenFlags.Fungible))
            {
                Throw.If(flags.HasFlag(TokenFlags.Divisible), "non-fungible token must be indivisible");
            }

            if (flags.HasFlag(TokenFlags.Divisible))
            {
                Throw.If(decimals <= 0, "divisible token must have decimals");
            }
            else
            {
                Throw.If(decimals > 0, "indivisible token can't have decimals");
            }

            this.Symbol    = symbol;
            this.Name      = name;
            this.Owner     = owner;
            this.Flags     = flags;
            this.Decimals  = decimals;
            this.MaxSupply = maxSupply;
            this.Script    = script;
            this.ABI       = ABI;
        }
예제 #4
0
        private static ExecutionState Nexus_CreateToken(RuntimeVM vm)
        {
            vm.ExpectStackSize(7);

            var owner = vm.PopAddress();

            var symbol    = vm.PopString("symbol");
            var name      = vm.PopString("name");
            var maxSupply = vm.PopNumber("maxSupply");
            var decimals  = (int)vm.PopNumber("decimals");
            var flags     = vm.PopEnum <TokenFlags>("flags");
            var script    = vm.PopBytes("script");

            ContractInterface abi;

            if (vm.ProtocolVersion >= 4)
            {
                var abiBytes = vm.PopBytes("abi bytes");
                abi = ContractInterface.FromBytes(abiBytes);
            }
            else
            {
                abi = new ContractInterface();
            }

            vm.CreateToken(owner, symbol, name, maxSupply, decimals, flags, script, abi);

            return(ExecutionState.Running);
        }
예제 #5
0
        public override ContractInterface GenerateCode(CodeGenerator output)
        {
            var abi = base.GenerateCode(output);

            var nftStandard = NFTUtils.GetNFTStandard();

            // convert ABI parameters
            var methods = new List <ContractMethod>();

            foreach (var method in abi.Methods)
            {
                if (nftStandard.HasMethod(method.name))
                {
                    var convertedMethod = new ContractMethod(method.name, method.returnType, method.offset, new[] { new ContractParameter("tokenID", VMType.Number) });
                    methods.Add(convertedMethod);
                }
                else
                {
                    methods.Add(method);
                }
            }

            abi = new ContractInterface(methods, abi.Events);
            return(abi);
        }
예제 #6
0
        public CustomContract(string name, byte[] script, ContractInterface abi) : base()
        {
            Throw.IfNull(script, nameof(script));
            this.Script = script;

            _name = name;

            this.ABI = abi;
        }
예제 #7
0
        public void UnserializeData(BinaryReader reader)
        {
            this.MintCount = reader.ReadBigInteger();
            this.MaxSupply = reader.ReadBigInteger();
            this.Mode      = (TokenSeriesMode)reader.ReadByte();
            this.Script    = reader.ReadByteArray();

            var bytes = reader.ReadByteArray();

            this.ABI = ContractInterface.FromBytes(bytes);

            this.ROM = reader.ReadByteArray();
        }
예제 #8
0
        public void UnserializeData(BinaryReader reader)
        {
            Symbol    = reader.ReadVarString();
            Name      = reader.ReadVarString();
            Owner     = reader.ReadAddress();
            Flags     = (TokenFlags)reader.ReadUInt32();
            Decimals  = reader.ReadInt32();
            MaxSupply = reader.ReadBigInteger();
            Script    = reader.ReadByteArray();

            var abiBytes = reader.ReadByteArray();

            this.ABI = ContractInterface.FromBytes(abiBytes);
        }
예제 #9
0
        private static ExecutionState Constructor_ABI(RuntimeVM vm)
        {
            return(Constructor_Object <byte[], ContractInterface>(vm, bytes =>
            {
                Throw.If(bytes == null, "invalid abi");

                using (var stream = new MemoryStream(bytes))
                {
                    using (var reader = new BinaryReader(stream))
                    {
                        return ContractInterface.Unserialize(reader);
                    }
                }
            }));
        }
예제 #10
0
        private static ContractInterface CreateInterfaceContract(ServiceDef owner, Type type)
        {
            if (!interfacedefs.TryGetValue(type, out ContractInterface contract))
            {
                var mm = type.GetMethods().Where(_m => _m.GetCustomAttributes <ContractAttribute>().Any());

                contract = new ContractInterface()
                {
                    Owner = owner,
                    Type  = type
                };
                contract.Methods    = mm.Select(_m => CreateMethodContract(contract, _m)).ToArray();
                interfacedefs[type] = contract;
            }
            return(contract);
        }
예제 #11
0
        protected override void ProcessABI(ContractInterface abi, DebugInfo debugInfo)
        {
            base.ProcessABI(abi, debugInfo);

            // here we lookup the script start offset for each method based on debug info obtained from the assembler
            foreach (var abiMethod in abi.Methods)
            {
                var method = this.Methods[abiMethod.name];
                abiMethod.offset = debugInfo.FindOffset([email protected]);

                if (abiMethod.offset < 0)
                {
                    throw new Exception("Could not calculate script offset for method: " + abiMethod.name);
                }
            }
        }
예제 #12
0
        private static ExecutionState Nexus_CreateTokenSeries(RuntimeVM vm)
        {
            vm.ExpectStackSize(5);

            var from      = vm.PopAddress();
            var symbol    = vm.PopString("symbol");
            var seriesID  = vm.PopNumber("series ID");
            var maxSupply = vm.PopNumber("max supply");
            var mode      = vm.PopEnum <TokenSeriesMode>("mode");
            var script    = vm.PopBytes("script");
            var abiBytes  = vm.PopBytes("abi bytes");

            var abi = ContractInterface.FromBytes(abiBytes);

            vm.CreateTokenSeries(symbol, from, seriesID, maxSupply, mode, script, abi);

            return(ExecutionState.Running);
        }
예제 #13
0
        private static VMObject ExecuteScript(byte[] script, ContractInterface abi, string methodName)
        {
            var method = abi.FindMethod(methodName);

            if (method == null)
            {
                throw new Exception("ABI is missing: " + methodName);
            }

            var vm     = new GasMachine(script, (uint)method.offset);
            var result = vm.Execute();

            if (result == ExecutionState.Halt)
            {
                return(vm.Stack.Pop());
            }

            throw new Exception("Script execution failed for: " + methodName);
        }
예제 #14
0
        public void RegisterScript(Address target, byte[] script, byte[] abiBytes)
        {
            Runtime.Expect(target.IsUser, "must be user address");
            Runtime.Expect(target != Runtime.GenesisAddress, "address must not be genesis");
            Runtime.Expect(Runtime.IsWitness(target), "invalid witness");

            var stake = Runtime.GetStake(target);

            Runtime.Expect(stake >= UnitConversion.GetUnitValue(DomainSettings.StakingTokenDecimals), "must have something staked");

            Runtime.Expect(script.Length < 1024, "invalid script length");

            Runtime.Expect(!_scriptMap.ContainsKey(target), "address already has a script");

            var abi = ContractInterface.FromBytes(abiBytes);

            Runtime.Expect(abi.MethodCount > 0, "unexpected empty contract abi");

            var witnessTriggerName = AccountTrigger.OnWitness.ToString();

            if (abi.HasMethod(witnessTriggerName))
            {
                var witnessCheck = Runtime.InvokeTrigger(false, script, NativeContractKind.Account, abi, witnessTriggerName, Address.Null) != TriggerResult.Failure;
                Runtime.Expect(!witnessCheck, "script does not handle OnWitness correctly, case #1");

                witnessCheck = Runtime.InvokeTrigger(false, script, NativeContractKind.Account, abi, witnessTriggerName, target) != TriggerResult.Failure;
                Runtime.Expect(witnessCheck, "script does not handle OnWitness correctly, case #2");
            }

            _scriptMap.Set(target, script);
            _abiMap.Set(target, abiBytes);

            var constructor = abi.FindMethod(SmartContract.ConstructorName);

            if (constructor != null)
            {
                Runtime.CallContext(target.Text, constructor, target);
            }

            // TODO? Runtime.Notify(EventKind.AddressRegister, target, script);
        }
예제 #15
0
        private static void ValidateABI(RuntimeVM vm, string contractName, ContractInterface abi, bool isNative)
        {
            var offsets = new HashSet <int>();
            var names   = new HashSet <string>();

            foreach (var method in abi.Methods)
            {
                vm.Expect(ValidationUtils.IsValidMethod(method.name, method.returnType), "invalid method: " + method.name);
                var normalizedName = method.name.ToLower();
                vm.Expect(!names.Contains(normalizedName), $"duplicated method name in {contractName}: {normalizedName}");

                names.Add(normalizedName);

                if (!isNative)
                {
                    vm.Expect(method.offset >= 0, $"invalid offset in {contractName} contract abi for method {method.name}");
                    vm.Expect(!offsets.Contains(method.offset), $"duplicated offset in {contractName} contract abi for method {method.name}");
                    offsets.Add(method.offset);
                }
            }
        }
예제 #16
0
        public SmartContract GetContractByName(StorageContext storage, string name)
        {
            if (Nexus.IsNativeContract(name) || ValidationUtils.IsValidTicker(name))
            {
                return(Nexus.GetContractByName(storage, name));
            }

            var address   = SmartContract.GetAddressForName(name);
            var scriptKey = GetContractKey(address, "script");

            if (!storage.Has(scriptKey))
            {
                return(null);
            }

            var script = storage.Get(scriptKey);

            var abiKey   = GetContractKey(address, "abi");
            var abiBytes = storage.Get(abiKey);
            var abi      = ContractInterface.FromBytes(abiBytes);

            return(new CustomContract(name, script, abi));
        }
예제 #17
0
        private static ContractMethod CreateMethodContract(ContractInterface owner, MethodInfo m)
        {
            /*  MethodInfo taskm = null;
             *
             * if (!m.ReturnType.IsDerived(typeof(Task)))
             * {
             *     taskm = owner.Owner.Type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(_m => _m.Name == m.Name && _m.ReturnType.IsDerived(typeof(Task))).SingleOrDefault();
             *
             *     if (taskm != null)
             *     {
             *         if (!taskm.GetParameters().SequenceEqual(m.GetParameters(), new cmp_ParameterInfo_type()))
             *         {
             *             throw new ReflectionError($"error with parameters for {m.Name} for private task method");
             *         }
             *         if (m.ReturnType != typeof(void))
             *         {
             *             if (taskm.ReturnType.GetGenericArguments().Single()!=m.ReturnType)
             *             {
             *                 throw new ReflectionError($"error with return type for {m.Name} for private task method");
             *             }
             *         }
             *     }
             *     else
             *     {
             *         taskm = m;
             *     }
             * }*/
            var r = new ContractMethod()
            {
                Owner      = owner,
                MethodInfo = m
            };

            r.ReturnContract = new ContractReturn(m.ReturnType);
            r.Parameters     = m.GetParameters().Select(_p => CreateParameterContract(r, _p)).ToArray();
            return(r);
        }
예제 #18
0
        public bool DeployContractScript(StorageContext storage, Address contractOwner, string name, Address contractAddress, byte[] script, ContractInterface abi)
        {
            var scriptKey = GetContractKey(contractAddress, "script");

            if (storage.Has(scriptKey))
            {
                return(false);
            }

            storage.Put(scriptKey, script);

            var ownerBytes = contractOwner.ToByteArray();
            var ownerKey   = GetContractKey(contractAddress, "owner");

            storage.Put(ownerKey, ownerBytes);

            var abiBytes = abi.ToByteArray();
            var abiKey   = GetContractKey(contractAddress, "abi");

            storage.Put(abiKey, abiBytes);

            var nameBytes = Encoding.ASCII.GetBytes(name);
            var nameKey   = GetContractKey(contractAddress, "name");

            storage.Put(nameKey, nameBytes);

            var contractList = new StorageList(GetContractListKey(), storage);

            contractList.Add <Address>(contractAddress);

            return(true);
        }
예제 #19
0
        public static void FetchProperty(StorageContext storage, Chain chain, string methodName, byte[] script, ContractInterface abi, Action <string, VMObject> callback)
        {
            if (abi.HasMethod(methodName))
            {
                var result = ExecuteScript(storage, chain, script, abi, methodName);

                string propName = methodName;

                if (propName.StartsWith("is"))
                {
                    propName = propName.Substring(2);
                }
                else
                if (propName.StartsWith("get"))
                {
                    propName = propName.Substring(3);
                }

                callback(propName, result);
            }
        }
예제 #20
0
        private static void DeployOrUpgrade(string[] args, SpookSettings settings, NexusAPI api, BigInteger minFee, bool isUpgrade)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected file name");
            }

            DoChecks(api);

            var fileName = args[0];

            if (!File.Exists(fileName))
            {
                throw new CommandException("Provided file does not exist");
            }

            if (!FileExistsCaseSensitive(fileName))
            {
                throw new CommandException("Provided file case does not match real file name case");
            }

            var extension = ScriptModule.ScriptExtension;

            if (!fileName.EndsWith(extension))
            {
                throw new CommandException($"Provided file is not a compiled {extension} script");
            }

            var abiFile = fileName.Replace(extension, ".abi");

            if (!File.Exists(abiFile))
            {
                throw new CommandException($"No ABI file {abiFile} that matches provided script file");
            }

            var contractName = Path.GetFileNameWithoutExtension(fileName);

            var contractScript = File.ReadAllBytes(fileName);
            var abiBytes       = File.ReadAllBytes(abiFile);

            var abi = ContractInterface.FromBytes(abiBytes);

            var sb = new ScriptBuilder();

            int nexusVersion = 0;

            try
            {
                var nexusDetails = api.Execute("getNexus", new object[] { false });
                var root         = LunarLabs.Parser.JSON.JSONReader.ReadFromString(nexusDetails);

                var governance = root["governance"];

                var entry = governance.Children.Where(x => x.GetNode("name").Value == "nexus.protocol.version").FirstOrDefault();
                entry = entry.GetNodeByIndex(1);

                nexusVersion = Int32.Parse(entry.Value);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                nexusVersion = -1;
            }

            if (nexusVersion <= 1)
            {
                throw new CommandException("Failed to obtain nexus version via API");
            }


            bool isToken        = ValidationUtils.IsValidTicker(contractName);
            var  availableFlags = Enum.GetValues(typeof(TokenFlags)).Cast <TokenFlags>().ToArray();

            sb.AllowGas(Keys.Address, Address.Null, minFee, 9999);

            if (isUpgrade)
            {
                // check for modification in flags
                if (isToken)
                {
                    var symbol    = contractName;
                    var resultStr = api.Execute("getToken", new[] { symbol, "false" });

                    logger.Debug($"{resultStr}");

                    //2021.08.27 sfichera: Fixed api obj deserialization.
                    //dynamic apiResult = System.Text.Json.JsonSerializer.Deserialize<TokenResult>(resultStr);
                    dynamic apiResult = JsonConvert.DeserializeObject <TokenResult>(resultStr);

                    if (apiResult is TokenResult)
                    {
                        var oldToken   = (TokenResult)apiResult;
                        var oldFlags   = TokenFlags.None;
                        var splitFlags = oldToken.flags.Split(',');
                        foreach (var entry in splitFlags)
                        {
                            TokenFlags flag;

                            if (Enum.TryParse <TokenFlags>(entry, true, out flag))
                            {
                                oldFlags |= flag;
                            }
                        }

                        foreach (var flag in availableFlags)
                        {
                            var propName = "is" + flag;
                            if (abi.HasMethod(propName))
                            {
                                var isSet  = ExecuteScript(contractScript, abi, propName).AsBool();
                                var wasSet = oldFlags.HasFlag(flag);
                                if (isSet != wasSet)
                                {
                                    throw new CommandException($"Detected '{flag}' flag change: {wasSet} => {isSet}");
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new CommandException("could not find any deployed token contract for " + symbol);
                    }
                }

                sb.CallInterop("Runtime.UpgradeContract", Keys.Address, contractName, contractScript, abiBytes);
            }
            else
            if (isToken)
            {
                if (!abi.HasMethod("getName"))
                {
                    throw new CommandException("token contract is missing required 'name' property");
                }

                string symbol = null;


                if (nexusVersion < 6)
                {
                    symbol = contractName;
                }
                else
                {
                    if (abi.HasMethod("getSymbol"))
                    {
                        symbol = ExecuteScript(contractScript, abi, "getSymbol").AsString();
                    }

                    if (string.IsNullOrEmpty(symbol))
                    {
                        throw new CommandException("token contract 'symbol' property is either missing or returning an empty value");
                    }
                }

                var name = ExecuteScript(contractScript, abi, "getName").AsString();

                if (string.IsNullOrEmpty(name))
                {
                    throw new CommandException("token contract 'name' property is either missing or returning an empty value");
                }

                BigInteger maxSupply = abi.HasMethod("getMaxSupply") ? ExecuteScript(contractScript, abi, "getMaxSupply").AsNumber() : 0;
                BigInteger decimals  = abi.HasMethod("getDecimals") ? ExecuteScript(contractScript, abi, "getDecimals").AsNumber() : 0;

                TokenFlags flags = TokenFlags.None;

                foreach (var flag in availableFlags)
                {
                    var propName = "is" + flag;
                    if (abi.HasMethod(propName) && ExecuteScript(contractScript, abi, propName).AsBool())
                    {
                        flags |= flag;
                    }
                }

                if (nexusVersion < 6)
                {
                    sb.CallInterop("Nexus.CreateToken", Keys.Address, symbol, name, maxSupply, decimals, flags, contractScript, abiBytes);
                }
                else
                {
                    sb.CallInterop("Nexus.CreateToken", Keys.Address, contractScript, abiBytes);
                }

                contractName = symbol;
            }
            else
            {
                sb.CallInterop("Runtime.DeployContract", Keys.Address, contractName, contractScript, abiBytes);
            }

            sb.SpendGas(Keys.Address);
            var script = sb.EndScript();

            if (!isUpgrade)
            {
                var upgradeTrigger = AccountContract.GetTriggerForABI(AccountTrigger.OnUpgrade);
                if (abi.Implements(upgradeTrigger))
                {
                    logger.Message($"{contractName} implements proper triggers, and can be upgraded later.");
                }
                else
                {
                    logger.Warning($"{contractName} does not implements proper triggers, can't be upgraded later.");
                }
            }

            var hash = ExecuteTransaction(settings, api, script, ProofOfWork.Minimal, Keys);

            if (hash != Hash.Null)
            {
                var expectedEvent    = isUpgrade ? EventKind.ContractUpgrade : (isToken ? EventKind.TokenCreate : EventKind.ContractDeploy);
                var expectedEventStr = expectedEvent.ToString();

                var events = GetTransactionEvents(hash);
                if (events.Any(x => x.kind == expectedEventStr))
                {
                    var contractAddress = SmartContract.GetAddressForName(contractName);

                    string action = isUpgrade ? "Upgraded" : "Deployed";

                    logger.Message($"{action} {contractName} at {contractAddress}");
                }
                else
                {
                    throw new CommandException("Transaction was confirmed but deployment event is missing!");
                }
            }
        }
예제 #21
0
        private static ExecutionState Runtime_UpgradeContract(RuntimeVM vm)
        {
            var tx = vm.Transaction;

            Throw.IfNull(tx, nameof(tx));

            var pow = tx.Hash.GetDifficulty();

            vm.Expect(pow >= (int)ProofOfWork.Minimal, "expected proof of work");

            vm.ExpectStackSize(1);

            var from = vm.PopAddress();

            vm.Expect(from.IsUser, "address must be user");

            vm.Expect(vm.IsStakeMaster(from), "needs to be master");

            vm.Expect(vm.IsWitness(from), "invalid witness");

            var contractName = vm.PopString("contractName");

            var contractAddress = SmartContract.GetAddressForName(contractName);
            var deployed        = vm.Chain.IsContractDeployed(vm.Storage, contractAddress);

            vm.Expect(deployed, $"{contractName} does not exist");

            byte[]            script;
            ContractInterface abi;

            bool isNative = Nexus.IsNativeContract(contractName);

            vm.Expect(!isNative, "cannot upgrade native contract");

            bool isToken = ValidationUtils.IsValidTicker(contractName);

            script = vm.PopBytes("contractScript");

            var abiBytes = vm.PopBytes("contractABI");

            abi = ContractInterface.FromBytes(abiBytes);

            var fuelCost = vm.GetGovernanceValue(Nexus.FuelPerContractDeployTag);

            // governance value is in usd fiat, here convert from fiat to fuel amount
            fuelCost = vm.GetTokenQuote(DomainSettings.FiatTokenSymbol, DomainSettings.FuelTokenSymbol, fuelCost);

            // burn the "cost" tokens
            vm.BurnTokens(DomainSettings.FuelTokenSymbol, from, fuelCost);

            // ABI validation
            ValidateABI(vm, contractName, abi, isNative);

            SmartContract oldContract;

            if (isToken)
            {
                oldContract = vm.Nexus.GetTokenContract(vm.Storage, contractName);
            }
            else
            {
                oldContract = vm.Chain.GetContractByName(vm.Storage, contractName);
            }

            vm.Expect(oldContract != null, "could not fetch previous contract");
            vm.Expect(abi.Implements(oldContract.ABI), "new abi does not implement all methods of previous abi");
            vm.Expect(vm.InvokeTrigger(false, script, contractName, abi, AccountTrigger.OnUpgrade.ToString(), from) == TriggerResult.Success, "OnUpgrade trigger failed");

            if (isToken)
            {
                vm.Nexus.UpgradeTokenContract(vm.RootStorage, contractName, script, abi);
            }
            else
            {
                vm.Chain.UpgradeContract(vm.Storage, contractName, script, abi);
            }

            vm.Notify(EventKind.ContractUpgrade, from, contractName);

            return(ExecutionState.Running);
        }
예제 #22
0
        private static ExecutionState Runtime_DeployContract(RuntimeVM vm)
        {
            var tx = vm.Transaction;

            Throw.IfNull(tx, nameof(tx));

            var pow = tx.Hash.GetDifficulty();

            vm.Expect(pow >= (int)ProofOfWork.Minimal, "expected proof of work");

            vm.ExpectStackSize(1);

            var from = vm.PopAddress();

            vm.Expect(from.IsUser, "address must be user");

            if (vm.Nexus.HasGenesis)
            {
                //Runtime.Expect(org != DomainSettings.ValidatorsOrganizationName, "cannot deploy contract via this organization");
                vm.Expect(vm.IsStakeMaster(from), "needs to be master");
            }

            vm.Expect(vm.IsWitness(from), "invalid witness");

            var contractName = vm.PopString("contractName");

            var contractAddress = SmartContract.GetAddressForName(contractName);
            var deployed        = vm.Chain.IsContractDeployed(vm.Storage, contractAddress);

            // TODO
            if (vm.ProtocolVersion >= 2)
            {
                vm.Expect(!deployed, $"{contractName} is already deployed");
            }
            else
            if (deployed)
            {
                return(ExecutionState.Running);
            }

            byte[]            script;
            ContractInterface abi;

            bool isNative = Nexus.IsNativeContract(contractName);

            if (isNative)
            {
                if (contractName == "validator" && vm.GenesisAddress == Address.Null)
                {
                    vm.Nexus.Initialize(from);
                }

                script = new byte[] { (byte)Opcode.RET };

                var contractInstance = vm.Nexus.GetNativeContractByAddress(contractAddress);
                abi = contractInstance.ABI;
            }
            else
            {
                if (ValidationUtils.IsValidTicker(contractName))
                {
                    throw new VMException(vm, "use createToken instead for this kind of contract");
                }
                else
                {
                    vm.Expect(ValidationUtils.IsValidIdentifier(contractName), "invalid contract name");
                }

                var isReserved = ValidationUtils.IsReservedIdentifier(contractName);

                if (isReserved && vm.IsWitness(vm.GenesisAddress))
                {
                    isReserved = false;
                }

                vm.Expect(!isReserved, $"name '{contractName}' reserved by system");

                script = vm.PopBytes("contractScript");

                var abiBytes = vm.PopBytes("contractABI");
                abi = ContractInterface.FromBytes(abiBytes);

                var fuelCost = vm.GetGovernanceValue(Nexus.FuelPerContractDeployTag);
                // governance value is in usd fiat, here convert from fiat to fuel amount
                fuelCost = vm.GetTokenQuote(DomainSettings.FiatTokenSymbol, DomainSettings.FuelTokenSymbol, fuelCost);

                // burn the "cost" tokens
                vm.BurnTokens(DomainSettings.FuelTokenSymbol, from, fuelCost);
            }

            // ABI validation
            ValidateABI(vm, contractName, abi, isNative);

            var success = vm.Chain.DeployContractScript(vm.Storage, from, contractName, contractAddress, script, abi);

            vm.Expect(success, $"deployment of {contractName} failed");

            var constructor = abi.FindMethod(SmartContract.ConstructorName);

            if (constructor != null)
            {
                vm.CallContext(contractName, constructor, from);
            }

            vm.Notify(EventKind.ContractDeploy, from, contractName);

            return(ExecutionState.Running);
        }
예제 #23
0
 public TokenSeries(BigInteger mintCount, BigInteger maxSupply, TokenSeriesMode mode, byte[] script, ContractInterface ABI, byte[] ROM)
 {
     MintCount = mintCount;
     MaxSupply = maxSupply;
     Mode      = mode;
     Script    = script;
     this.ABI  = ABI;
     SetROM(ROM);
 }
예제 #24
0
        private static void DeployOrUpgrade(string[] args, NexusAPI api, BigInteger minFee, bool isUpgrade)
        {
            if (args.Length != 1)
            {
                throw new CommandException("Invalid number of arguments, expected file name");
            }

            DoChecks(api);

            var fileName = args[0];

            if (!File.Exists(fileName))
            {
                throw new CommandException("Provided file does not exist");
            }

            var extension = ScriptModule.ScriptExtension;

            if (!fileName.EndsWith(extension))
            {
                throw new CommandException($"Provided file is not a compiled {extension} script");
            }

            var abiFile = fileName.Replace(extension, ".abi");

            if (!File.Exists(abiFile))
            {
                throw new CommandException($"No ABI file {abiFile} that matches provided script file");
            }

            var contractName = Path.GetFileNameWithoutExtension(fileName);

            var contractScript = File.ReadAllBytes(fileName);
            var abiBytes       = File.ReadAllBytes(abiFile);

            var abi = ContractInterface.FromBytes(abiBytes);

            var sb = new ScriptBuilder();

            bool isToken        = ValidationUtils.IsValidTicker(contractName);
            var  availableFlags = Enum.GetValues(typeof(TokenFlags)).Cast <TokenFlags>().ToArray();

            sb.AllowGas(Keys.Address, Address.Null, minFee, 9999);

            if (isUpgrade)
            {
                // check for modification in flags
                if (isToken)
                {
                    var     symbol    = contractName;
                    var     resultStr = api.Execute("getToken", new[] { symbol, "false" });
                    dynamic apiResult = JsonConvert.DeserializeObject <TokenResult>(resultStr);

                    if (apiResult is TokenResult)
                    {
                        var oldToken = (TokenResult)apiResult;

                        var oldFlags   = TokenFlags.None;
                        var splitFlags = oldToken.flags.Split(',');
                        foreach (var entry in splitFlags)
                        {
                            TokenFlags flag;

                            if (Enum.TryParse <TokenFlags>(entry, true, out flag))
                            {
                                oldFlags |= flag;
                            }
                        }

                        foreach (var flag in availableFlags)
                        {
                            var propName = "is" + flag;
                            if (abi.HasMethod(propName))
                            {
                                var isSet  = ExecuteScript(contractScript, abi, propName).AsBool();
                                var wasSet = oldFlags.HasFlag(flag);
                                if (isSet != wasSet)
                                {
                                    throw new CommandException($"Detected '{flag}' flag change: {wasSet} => {isSet}");
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new CommandException("could not find any deployed token contract for " + symbol);
                    }
                }

                sb.CallInterop("Runtime.UpgradeContract", Keys.Address, contractName, contractScript, abiBytes);
            }
            else
            if (isToken)
            {
                if (!abi.HasMethod("getName"))
                {
                    throw new CommandException("token contract is missing required 'name' property");
                }

                var symbol = contractName;
                var name   = ExecuteScript(contractScript, abi, "getName").AsString();

                if (string.IsNullOrEmpty(name))
                {
                    throw new CommandException("token contract 'name' property is returning an empty value");
                }

                BigInteger maxSupply = abi.HasMethod("getMaxSupply") ? ExecuteScript(contractScript, abi, "getMaxSupply").AsNumber() : 0;
                BigInteger decimals  = abi.HasMethod("getDecimals") ? ExecuteScript(contractScript, abi, "getDecimals").AsNumber() : 0;

                TokenFlags flags = TokenFlags.None;

                foreach (var flag in availableFlags)
                {
                    var propName = "is" + flag;
                    if (abi.HasMethod(propName) && ExecuteScript(contractScript, abi, propName).AsBool())
                    {
                        flags |= flag;
                    }
                }

                sb.CallInterop("Nexus.CreateToken", Keys.Address, symbol, name, maxSupply, decimals, flags, contractScript, abiBytes);

                contractName = symbol;
            }
            else
            {
                sb.CallInterop("Runtime.DeployContract", Keys.Address, contractName, contractScript, abiBytes);
            }

            sb.SpendGas(Keys.Address);
            var script = sb.EndScript();

            if (!isUpgrade)
            {
                var upgradeTrigger = AccountContract.GetTriggerForABI(AccountTrigger.OnUpgrade);
                if (abi.Implements(upgradeTrigger))
                {
                    logger.Message($"{contractName} implements proper triggers, and can be upgraded later.");
                }
                else
                {
                    logger.Warning($"{contractName} does not implements proper triggers, can't be upgraded later.");
                }
            }

            var hash = ExecuteTransaction(api, script, ProofOfWork.Minimal, Keys);

            if (hash != Hash.Null)
            {
                var expectedEvent    = isUpgrade ? EventKind.ContractUpgrade : (isToken ? EventKind.TokenCreate : EventKind.ContractDeploy);
                var expectedEventStr = expectedEvent.ToString();

                var events = GetTransactionEvents(hash);
                if (events.Any(x => x.kind == expectedEventStr))
                {
                    var contractAddress = SmartContract.GetAddressForName(contractName);

                    string action = isUpgrade ? "Upgraded" : "Deployed";

                    logger.Message($"{action} {contractName} at {contractAddress}");
                }
                else
                {
                    throw new CommandException("Transaction was confirmed but deployment event is missing!");
                }
            }
        }
예제 #25
0
        public override ContractInterface GenerateCode(CodeGenerator output)
        {
            foreach (var evt in Events.Values)
            {
                evt.Validate();
            }

            if (this.Kind == ModuleKind.Token)
            {
                bool hasName = false;

                foreach (var method in this.Methods.Values)
                {
                    string name;

                    if (method.Name.StartsWith("get"))
                    {
                        name = method.Name.Substring(3);

                        switch (name)
                        {
                        case "Name":
                            hasName = true;
                            ExpectMethodType(method, VarKind.String);
                            break;

                        case "MaxSupply":
                            ExpectMethodType(method, VarKind.Number);
                            break;
                        }
                    }
                    else
                    {
                        if (method.Name.StartsWith("is") && method.Name.Length > 2 && char.IsUpper(method.Name[2]))
                        {
                            ExpectMethodType(method, VarKind.Bool);
                        }
                    }
                }

                if (!hasName)
                {
                    throw new CompilerException($"token {this.Name} is missing property 'name'");
                }
            }

            this.Scope.Enter(output);

            /*{
             *  var reg = Parser.Instance.AllocRegister(output, this, "methodName");
             *  output.AppendLine(this, $"POP {reg}");
             *  foreach (var entry in Methods.Values)
             *  {
             *      output.AppendLine(this, $"LOAD r0, \"{entry.Name}\"");
             *      output.AppendLine(this, $"EQUAL r0, {reg}");
             *      output.AppendLine(this, $"JMPIF r0, @{entry.GetEntryLabel()}");
             *  }
             *  Parser.Instance.DeallocRegister(reg);
             *  output.AppendLine(this, "THROW \"unknown method was called\"");
             * }*/

            foreach (var entry in Methods.Values)
            {
                entry.GenerateCode(output);
            }

            this.Scope.Leave(output);

            var methods = Methods.Values.Where(x => [email protected]).Select(x => x.GetABI());
            var events  = Events.Values.Select(x => x.GetABI());
            var abi     = new ContractInterface(methods, events);

            return(abi);
        }
예제 #26
0
 protected virtual void ProcessABI(ContractInterface abi, DebugInfo debugInfo)
 {
     // do nothing
 }
예제 #27
0
        public void UpgradeContract(StorageContext storage, string name, byte[] script, ContractInterface abi)
        {
            if (Nexus.IsNativeContract(name) || ValidationUtils.IsValidTicker(name))
            {
                throw new ChainException($"Cannot upgrade this type of contract: {name}");
            }

            if (!IsContractDeployed(storage, name))
            {
                throw new ChainException($"Cannot upgrade non-existing contract: {name}");
            }

            var address = SmartContract.GetAddressForName(name);

            var scriptKey = GetContractKey(address, "script");

            storage.Put(scriptKey, script);

            var abiKey   = GetContractKey(address, "abi");
            var abiBytes = abi.ToByteArray();

            storage.Put(abiBytes, abiBytes);
        }
예제 #28
0
        public Transaction GenerateToken(PhantasmaKeys owner, string symbol, string name, BigInteger totalSupply,
                                         int decimals, TokenFlags flags, byte[] tokenScript = null, Dictionary <string, int> labels = null, IEnumerable <ContractMethod> customMethods = null)
        {
            var version = Nexus.GetGovernanceValue(Nexus.RootStorage, Nexus.NexusProtocolVersionTag);

            if (labels == null)
            {
                labels = new Dictionary <string, int>();
            }

            if (tokenScript == null)
            {
                // small script that restricts minting of tokens to transactions where the owner is a witness
                var      addressStr = Base16.Encode(owner.Address.ToByteArray());
                string[] scriptString;

                if (version >= 4)
                {
                    scriptString = new string[] {
                        $"alias r3, $result",
                        $"alias r4, $owner",
                        $"@{AccountTrigger.OnMint}: nop",
                        $"load $owner 0x{addressStr}",
                        "push $owner",
                        "extcall \"Address()\"",
                        "extcall \"Runtime.IsWitness\"",
                        "pop $result",
                        $"jmpif $result, @end",
                        $"load r0 \"invalid witness\"",
                        $"throw r0",

                        $"@end: ret"
                    };
                }
                else
                {
                    scriptString = new string[] {
                        $"alias r1, $triggerMint",
                        $"alias r2, $currentTrigger",
                        $"alias r3, $result",
                        $"alias r4, $owner",

                        $@"load $triggerMint, ""{AccountTrigger.OnMint}""",
                        $"pop $currentTrigger",

                        $"equal $triggerMint, $currentTrigger, $result",
                        $"jmpif $result, @mintHandler",
                        $"jmp @end",

                        $"@mintHandler: nop",
                        $"load $owner 0x{addressStr}",
                        "push $owner",
                        "extcall \"Address()\"",
                        "extcall \"Runtime.IsWitness\"",
                        "pop $result",
                        $"jmpif $result, @end",
                        $"load r0 \"invalid witness\"",
                        $"throw r0",

                        $"@end: ret"
                    };
                }
                DebugInfo debugInfo;
                tokenScript = AssemblerUtils.BuildScript(scriptString, "GenerateToken", out debugInfo, out labels);
            }

            var sb = ScriptUtils.
                     BeginScript().
                     AllowGas(owner.Address, Address.Null, MinimumFee, 9999);

            if (version >= 4)
            {
                var triggerMap = new Dictionary <AccountTrigger, int>();

                var onMintLabel = AccountTrigger.OnMint.ToString();
                if (labels.ContainsKey(onMintLabel))
                {
                    triggerMap[AccountTrigger.OnMint] = labels[onMintLabel];
                }

                var methods = AccountContract.GetTriggersForABI(triggerMap);

                if (customMethods != null)
                {
                    methods = methods.Concat(customMethods);
                }

                var abi      = new ContractInterface(methods, Enumerable.Empty <ContractEvent>());
                var abiBytes = abi.ToByteArray();

                sb.CallInterop("Nexus.CreateToken", owner.Address, symbol, name, totalSupply, decimals, flags, tokenScript, abiBytes);
            }
            else
            {
                sb.CallInterop("Nexus.CreateToken", owner.Address, symbol, name, totalSupply, decimals, flags, tokenScript);
            }

            if (!flags.HasFlag(TokenFlags.Fungible))
            {
                ContractInterface nftABI;
                byte[]            nftScript;
                NFTUtils.GenerateNFTDummyScript(symbol, name, name, "http://simulator/nft/*", "http://simulator/img/*", out nftScript, out nftABI);
                sb.CallInterop("Nexus.CreateTokenSeries", owner.Address, symbol, new BigInteger(0), totalSupply, TokenSeriesMode.Unique, nftScript, nftABI.ToByteArray());
            }

            sb.SpendGas(owner.Address);

            var script = sb.EndScript();

            var tx = MakeTransaction(owner, ProofOfWork.Minimal, Nexus.RootChain, script);

            return(tx);
        }
예제 #29
0
        static byte[] GenCreateToken()
        {
            Console.Write("Token symbol? ");
            string symbol = Console.ReadLine();

            if (!ValidationUtils.IsValidTicker(symbol))
            {
                Console.Write("Invalid token symbol");
                return(null);
            }

            Console.Write("Token name? ");
            string name = Console.ReadLine();

            TokenFlags flags = TokenFlags.Transferable;

            var possibleValues = new[] { TokenFlags.Burnable, TokenFlags.Divisible, TokenFlags.Finite, TokenFlags.Fungible };

            foreach (var val in possibleValues)
            {
                if (FetchAnswer($"Is {symbol} {val}?"))
                {
                    flags |= val;
                }
            }

            int decimals;

            if (flags.HasFlag(TokenFlags.Divisible))
            {
                Console.Write($"How many decimals {symbol} has?: ");
                if (!int.TryParse(Console.ReadLine(), out decimals) || decimals < 0 || decimals > 18)
                {
                    Console.Write("Invalid decimals");
                    return(null);
                }
            }
            else
            {
                decimals = 0;
            }

            BigInteger maxSupply;

            if (flags.HasFlag(TokenFlags.Finite))
            {
                Console.Write($"What is the max supply of {symbol}?: ");

                decimal val;
                if (!decimal.TryParse(Console.ReadLine(), out val) || val <= 0)
                {
                    Console.Write("Invalid decimals");
                    return(null);
                }

                maxSupply = UnitConversion.ToBigInteger(val, decimals);
            }
            else
            {
                maxSupply = 0;
            }


            var labels     = new Dictionary <string, int>();
            var addressStr = Base16.Encode(signerKeys.Address.ToByteArray());

            string[] scriptString;

            scriptString = new string[] {
                $"alias r3, $result",
                $"alias r4, $owner",
                $"@{AccountTrigger.OnMint}: nop",
                $"load $owner 0x{addressStr}",
                "push $owner",
                "extcall \"Address()\"",
                "extcall \"Runtime.IsWitness\"",
                "pop $result",
                $"jmpif $result, @end",
                $"load r0 \"invalid witness\"",
                $"throw r0",

                $"@end: ret"
            };

            DebugInfo debugInfo;
            var       tokenScript = AssemblerUtils.BuildScript(scriptString, "GenerateToken", out debugInfo, out labels);

            var sb = ScriptUtils.
                     BeginScript().
                     AllowGas(signerKeys.Address, Address.Null, MinimumFee, 9999);

            var triggerMap = new Dictionary <AccountTrigger, int>();

            var onMintLabel = AccountTrigger.OnMint.ToString();

            if (labels.ContainsKey(onMintLabel))
            {
                triggerMap[AccountTrigger.OnMint] = labels[onMintLabel];
            }

            var methods = AccountContract.GetTriggersForABI(triggerMap);

            var abi      = new ContractInterface(methods, Enumerable.Empty <ContractEvent>());
            var abiBytes = abi.ToByteArray();

            sb.CallInterop("Nexus.CreateToken", signerKeys.Address, symbol, name, maxSupply, decimals, flags, tokenScript, abiBytes);

            if (!flags.HasFlag(TokenFlags.Fungible))
            {
                Console.Write("NFT deployment not supported yet");
                return(null);
            }

            sb.SpendGas(signerKeys.Address);

            var script = sb.EndScript();


            return(script);
        }