Beispiel #1
0
        internal bool MintTokens(string symbol, StorageContext storage, BalanceSheet balances, SupplySheet supply, Address target, BigInteger amount)
        {
            if (!TokenExists(symbol))
            {
                return(false);
            }

            var tokenInfo = GetTokenInfo(symbol);

            if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible))
            {
                return(false);
            }

            if (amount <= 0)
            {
                return(false);
            }

            if (tokenInfo.IsCapped)
            {
                if (!supply.Mint(amount))
                {
                    return(false);
                }
            }

            if (!balances.Add(storage, target, amount))
            {
                return(false);
            }

            EditTokenSupply(symbol, amount);
            return(true);
        }
        public void Unstake(Address address)
        {
            Runtime.Expect(IsValidator(address), "validator failed");
            Runtime.Expect(IsWitness(address), "witness failed");

            var entry = _entryMap.Get <Address, ValidatorInfo>(address);

            var diff = Timestamp.Now - entry.timestamp;
            var days = diff / 86400; // convert seconds to days

            Runtime.Expect(days >= 30, "waiting period required");

            var stakeAmount = entry.stake;
            var token       = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol);
            var balances    = new BalanceSheet(token.Symbol);
            var balance     = balances.Get(this.Storage, Runtime.Chain.Address);

            Runtime.Expect(balance >= stakeAmount, "not enough balance");

            Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, stakeAmount), "balance subtract failed");
            Runtime.Expect(balances.Add(this.Storage, address, stakeAmount), "balance add failed");

            _entryMap.Remove(address);
            _entryList.Remove(address);

            Runtime.Notify(EventKind.TokenUnstake, address, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = stakeAmount
            });
        }
Beispiel #3
0
        internal bool MintTokens(string symbol, StorageContext storage, Chain chain, Address target, BigInteger amount)
        {
            if (!TokenExists(symbol))
            {
                return(false);
            }

            var tokenInfo = GetTokenInfo(symbol);

            if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible))
            {
                return(false);
            }

            if (amount <= 0)
            {
                return(false);
            }

            var supply = new SupplySheet(symbol, chain, this);

            if (!supply.Mint(storage, amount, tokenInfo.MaxSupply))
            {
                return(false);
            }

            var balances = new BalanceSheet(symbol);

            if (!balances.Add(storage, target, amount))
            {
                return(false);
            }

            return(true);
        }
        public void Stake(Address from, BigInteger stakeAmount)
        {
            Runtime.Expect(stakeAmount >= MinimumValidStake, "invalid amount");
            Runtime.Expect(IsWitness(from), "witness failed");

            var stakeBalances = new BalanceSheet(Nexus.StakingTokenSymbol);
            var balance       = stakeBalances.Get(this.Storage, from);

            var currentStake = _stakes.Get <Address, EnergyAction>(from);

            var newStake = stakeAmount + currentStake.totalAmount;

            Runtime.Expect(balance >= stakeAmount, "not enough balance");

            Runtime.Expect(stakeBalances.Subtract(this.Storage, from, stakeAmount), "balance subtract failed");
            Runtime.Expect(stakeBalances.Add(this.Storage, Runtime.Chain.Address, stakeAmount), "balance add failed");

            var entry = new EnergyAction()
            {
                unclaimedPartials = stakeAmount + GetLastAction(from).unclaimedPartials,
                totalAmount       = newStake,
                timestamp         = this.Runtime.Time,
            };

            _stakes.Set(from, entry);

            var logEntry = new VotingLogEntry()
            {
                timestamp = this.Runtime.Time,
                amount    = stakeAmount
            };

            var votingLogbook = _voteHistory.Get <Address, StorageList>(from);

            votingLogbook.Add(logEntry);

            var masterAccountThreshold = GetMasterThreshold();

            if (Runtime.Nexus.GenesisAddress != from && newStake >= masterAccountThreshold && !IsMaster(from))
            {
                var nextClaim = GetMasterClaimDate(2);

                _mastersList.Add(new EnergyMaster()
                {
                    address = from, claimDate = nextClaim
                });
                Runtime.Notify(EventKind.RolePromote, from, new RoleEventData()
                {
                    role = "master", date = nextClaim
                });
            }

            Runtime.Notify(EventKind.TokenStake, from, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = Nexus.StakingTokenSymbol, value = stakeAmount
            });
        }
Beispiel #5
0
        internal bool MintTokens(RuntimeVM runtimeVM, string symbol, Address target, BigInteger amount)
        {
            if (!TokenExists(symbol))
            {
                return(false);
            }

            var tokenInfo = GetTokenInfo(symbol);

            if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible))
            {
                return(false);
            }

            if (amount <= 0)
            {
                return(false);
            }

            var supply = new SupplySheet(symbol, runtimeVM.Chain, this);

            if (!supply.Mint(runtimeVM.ChangeSet, amount, tokenInfo.MaxSupply))
            {
                return(false);
            }

            var balances = new BalanceSheet(symbol);

            if (!balances.Add(runtimeVM.ChangeSet, target, amount))
            {
                return(false);
            }

            var tokenTriggerResult = SmartContract.InvokeTrigger(runtimeVM, tokenInfo.Script, TokenContract.TriggerMint, target, amount);

            if (!tokenTriggerResult)
            {
                return(false);
            }

            var accountScript        = this.LookUpAddressScript(target);
            var accountTriggerResult = SmartContract.InvokeTrigger(runtimeVM, accountScript, AccountContract.TriggerMint, target, amount);

            if (!accountTriggerResult)
            {
                return(false);
            }

            return(true);
        }
Beispiel #6
0
        internal StorageMap _allowanceTargets; //<Address, Address>

        public void AllowGas(Address from, Address to, BigInteger price, BigInteger limit)
        {
            if (Runtime.readOnlyMode)
            {
                return;
            }

            Runtime.Expect(IsWitness(from), "invalid witness");
            Runtime.Expect(from != to, "invalid gas target");
            Runtime.Expect(Runtime.Chain.Address != to, "invalid gas target");

            Runtime.Expect(price > 0, "price must be positive amount");
            Runtime.Expect(limit > 0, "limit must be positive amount");

            //var token = this.Runtime.Nexus.FuelToken;
            //Runtime.Expect(token != null, "invalid token");
            //Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "must be fungible token");

            var balances  = new BalanceSheet(Nexus.FuelTokenSymbol);
            var maxAmount = price * limit;

            var balance = balances.Get(this.Storage, from);

            Runtime.Expect(balance >= maxAmount, "not enough gas in address");

            Runtime.Expect(balances.Subtract(this.Storage, from, maxAmount), "gas escrow withdraw failed");
            Runtime.Expect(balances.Add(this.Storage, Runtime.Chain.Address, maxAmount), "gas escrow deposit failed");

            var allowance = _allowanceMap.ContainsKey(from) ? _allowanceMap.Get <Address, BigInteger>(from) : 0;

            Runtime.Expect(allowance == 0, "unexpected pending allowance");

            allowance += maxAmount;
            _allowanceMap.Set(from, allowance);
            _allowanceTargets.Set(from, to);

            Runtime.Notify(EventKind.GasEscrow, from, new GasEventData()
            {
                address = Runtime.Chain.Address, price = price, amount = limit
            });
        }
        public void TakePrivate(Address to, string symbol, uint queueID, RingSignature signature)
        {
            Runtime.Expect(Runtime.Nexus.TokenExists(symbol), "invalid token");
            var tokenInfo = this.Runtime.Nexus.GetTokenInfo(symbol);

            Runtime.Expect(tokenInfo.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible");

            var queue = FindQueue(symbol, queueID);

            Runtime.Expect(queue.ID > 0, "invalid queue");

            Runtime.Expect(queue.ID == queueID, "mismatching queue");
            Runtime.Expect(queue.addresses.Count() == queue.size, "queue not full yet");

            var addresses = queue.addresses.All <Address>();

            foreach (var address in addresses)
            {
                Runtime.Expect(address != to, "cant send to anyone already in the queue");
            }

            var msg = this.Runtime.Transaction.ToByteArray(false);

            Runtime.Expect(signature.Verify(msg, addresses), "ring signature failed");

            var signatures = queue.signatures.All <Signature>();

            foreach (RingSignature otherSignature in signatures)
            {
                Runtime.Expect(!signature.IsLinked(otherSignature), "ring signature already linked");
            }

            queue.signatures.Add(signature);

            // TODO this is wrong
            var balances = new BalanceSheet(symbol);

            balances.Add(this.Storage, to, TransferAmount);
        }
Beispiel #8
0
        internal bool TransferTokens(string symbol, StorageContext storage, Chain chain, Address source, Address destination, BigInteger amount)
        {
            if (!TokenExists(symbol))
            {
                return(false);
            }

            var tokenInfo = GetTokenInfo(symbol);

            if (!tokenInfo.Flags.HasFlag(TokenFlags.Transferable))
            {
                throw new Exception("Not transferable");
            }

            if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible))
            {
                throw new Exception("Should be fungible");
            }

            if (amount <= 0)
            {
                return(false);
            }

            var balances = new BalanceSheet(symbol);

            if (!balances.Subtract(storage, source, amount))
            {
                return(false);
            }

            if (!balances.Add(storage, destination, amount))
            {
                return(false);
            }

            return(true);
        }
        public void Stake(Address address)
        {
            Runtime.Expect(IsWitness(address), "witness failed");

            var count = _entryList.Count();
            var max   = GetMaxValidators();

            Runtime.Expect(count < max, "no open validators spots");

            var stakeAmount = GetRequiredStake();

            var token    = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol);
            var balances = new BalanceSheet(token.Symbol);
            var balance  = balances.Get(this.Storage, address);

            Runtime.Expect(balance >= stakeAmount, "not enough balance");

            Runtime.Expect(balances.Subtract(this.Storage, address, stakeAmount), "balance subtract failed");
            Runtime.Expect(balances.Add(this.Storage, Runtime.Chain.Address, stakeAmount), "balance add failed");

            _entryList.Add(address);

            var entry = new ValidatorInfo()
            {
                address   = address,
                stake     = stakeAmount,
                timestamp = Runtime.Time,
                slashes   = 0
            };

            _entryMap.Set(address, entry);

            Runtime.Notify(EventKind.TokenStake, address, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = stakeAmount
            });
        }
Beispiel #10
0
        public void SpendGas(Address from)
        {
            if (Runtime.readOnlyMode)
            {
                return;
            }

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

            Runtime.Expect(_allowanceMap.ContainsKey(from), "no gas allowance found");

            var availableAmount = _allowanceMap.Get <Address, BigInteger>(from);

            var spentGas       = Runtime.UsedGas;
            var requiredAmount = spentGas * Runtime.GasPrice;

            Runtime.Expect(availableAmount >= requiredAmount, "gas allowance is not enough");

            /*var token = this.Runtime.Nexus.FuelToken;
             * Runtime.Expect(token != null, "invalid token");
             * Runtime.Expect(token.Flags.HasFlag(TokenFlags.Fungible), "must be fungible token");
             */

            var balances = new BalanceSheet(Nexus.FuelTokenSymbol);

            var leftoverAmount = availableAmount - requiredAmount;

            var        targetAddress = _allowanceTargets.Get <Address, Address>(from);
            BigInteger targetGas;

            if (targetAddress != Address.Null)
            {
                targetGas = spentGas / 2; // 50% for dapps
            }
            else
            {
                targetGas = 0;
            }

            // return unused gas to transaction creator
            if (leftoverAmount > 0)
            {
                Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, leftoverAmount), "gas leftover deposit failed");
                Runtime.Expect(balances.Add(this.Storage, from, leftoverAmount), "gas leftover withdraw failed");
            }

            if (targetGas > 0)
            {
                var targetPayment = targetGas * Runtime.GasPrice;
                Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, targetPayment), "gas target withdraw failed");
                Runtime.Expect(balances.Add(this.Storage, targetAddress, targetPayment), "gas target deposit failed");
                spentGas -= targetGas;
            }

            _allowanceMap.Remove(from);
            _allowanceTargets.Remove(from);

            if (targetGas > 0)
            {
                Runtime.Notify(EventKind.GasPayment, targetAddress, new GasEventData()
                {
                    address = from, price = Runtime.GasPrice, amount = targetGas
                });
            }

            Runtime.Notify(EventKind.GasPayment, Runtime.Chain.Address, new GasEventData()
            {
                address = from, price = Runtime.GasPrice, amount = spentGas
            });
        }
Beispiel #11
0
        internal bool TransferTokens(RuntimeVM runtimeVM, string symbol, Address source, Address destination, BigInteger amount)
        {
            if (!TokenExists(symbol))
            {
                return(false);
            }

            var tokenInfo = GetTokenInfo(symbol);

            if (!tokenInfo.Flags.HasFlag(TokenFlags.Transferable))
            {
                throw new Exception("Not transferable");
            }

            if (!tokenInfo.Flags.HasFlag(TokenFlags.Fungible))
            {
                throw new Exception("Should be fungible");
            }

            if (amount <= 0)
            {
                return(false);
            }

            var balances = new BalanceSheet(symbol);

            if (!balances.Subtract(runtimeVM.ChangeSet, source, amount))
            {
                return(false);
            }

            if (!balances.Add(runtimeVM.ChangeSet, destination, amount))
            {
                return(false);
            }

            var tokenTriggerResult = SmartContract.InvokeTrigger(runtimeVM, tokenInfo.Script, TokenContract.TriggerSend, source, amount);

            if (!tokenTriggerResult)
            {
                return(false);
            }

            tokenTriggerResult = SmartContract.InvokeTrigger(runtimeVM, tokenInfo.Script, TokenContract.TriggerReceive, destination, amount);
            if (!tokenTriggerResult)
            {
                return(false);
            }

            var accountScript        = this.LookUpAddressScript(source);
            var accountTriggerResult = SmartContract.InvokeTrigger(runtimeVM, accountScript, AccountContract.TriggerSend, source, amount);

            if (!accountTriggerResult)
            {
                return(false);
            }

            accountScript        = this.LookUpAddressScript(destination);
            accountTriggerResult = SmartContract.InvokeTrigger(runtimeVM, accountScript, AccountContract.TriggerSend, destination, amount);
            if (!accountTriggerResult)
            {
                return(false);
            }

            return(true);
        }
Beispiel #12
0
        public void CloseBlock(Block block, StorageChangeSetContext storage)
        {
            var rootStorage = this.IsRoot ? storage : Nexus.RootStorage;

            if (block.Height > 1)
            {
                var prevBlock = GetBlockByHash(block.PreviousHash);

                if (prevBlock.Validator != block.Validator)
                {
                    block.Notify(new Event(EventKind.ValidatorSwitch, block.Validator, "block", Serialization.Serialize(prevBlock)));
                }
            }

            var balance        = new BalanceSheet(DomainSettings.FuelTokenSymbol);
            var blockAddress   = Address.FromHash("block");
            var totalAvailable = balance.Get(storage, blockAddress);

            var targets = new List <Address>();

            if (Nexus.HasGenesis)
            {
                var validators = Nexus.GetValidators();

                var totalValidators = Nexus.GetPrimaryValidatorCount();

                for (int i = 0; i < totalValidators; i++)
                {
                    var validator = validators[i];
                    if (validator.type != ValidatorType.Primary)
                    {
                        continue;
                    }

                    targets.Add(validator.address);
                }
            }
            else
            if (totalAvailable > 0)
            {
                targets.Add(Nexus.GetGenesisAddress(rootStorage));
            }

            if (targets.Count > 0)
            {
                if (!balance.Subtract(storage, blockAddress, totalAvailable))
                {
                    throw new BlockGenerationException("could not subtract balance from block address");
                }

                var amountPerValidator = totalAvailable / targets.Count;
                var leftOvers          = totalAvailable - (amountPerValidator * targets.Count);

                foreach (var address in targets)
                {
                    BigInteger amount = amountPerValidator;

                    if (address == block.Validator)
                    {
                        amount += leftOvers;
                    }

                    // TODO this should use triggers when available...
                    if (!balance.Add(storage, address, amount))
                    {
                        throw new BlockGenerationException($"could not add balance to {address}");
                    }

                    var eventData = Serialization.Serialize(new TokenEventData(DomainSettings.FuelTokenSymbol, amount, this.Name));
                    block.Notify(new Event(EventKind.TokenClaim, address, "block", eventData));
                }
            }
        }
Beispiel #13
0
        public BigInteger Unstake(Address from, BigInteger unstakeAmount)
        {
            Runtime.Expect(IsWitness(from), "witness failed");
            Runtime.Expect(unstakeAmount >= MinimumValidStake, "invalid amount");

            if (!_stakes.ContainsKey <Address>(from))
            {
                return(0);
            }

            var stake = _stakes.Get <Address, EnergyAction>(from);

            if (stake.timestamp.Value == 0) // failsafe, should never happen
            {
                return(0);
            }

            Runtime.Expect(Runtime.Time >= stake.timestamp, "Negative time diff");

            var diff = Runtime.Time - stake.timestamp;
            var days = diff / 86400; // convert seconds to days

            Runtime.Expect(days >= 1, "waiting period required");

            var token    = Runtime.Nexus.GetTokenInfo(Nexus.StakingTokenSymbol);
            var balances = new BalanceSheet(token.Symbol);
            var balance  = balances.Get(this.Storage, Runtime.Chain.Address);

            Runtime.Expect(balance >= unstakeAmount, "not enough balance");

            var availableStake = stake.totalAmount;

            availableStake -= GetStorageStake(from);
            Runtime.Expect(availableStake >= unstakeAmount, "tried to unstake more than what was staked");

            //if this is a partial unstake
            if (availableStake - unstakeAmount > 0)
            {
                Runtime.Expect(availableStake - unstakeAmount >= MinimumValidStake, "leftover stake would be below minimum staking amount");
            }

            Runtime.Expect(balances.Subtract(this.Storage, Runtime.Chain.Address, unstakeAmount), "balance subtract failed");
            Runtime.Expect(balances.Add(this.Storage, from, unstakeAmount), "balance add failed");

            stake.totalAmount -= unstakeAmount;

            var unclaimedPartials = GetLastAction(from).unclaimedPartials;

            if (stake.totalAmount == 0 && unclaimedPartials == 0)
            {
                _stakes.Remove(from);
                _voteHistory.Remove(from);
            }
            else
            {
                var entry = new EnergyAction()
                {
                    unclaimedPartials = unclaimedPartials,
                    totalAmount       = stake.totalAmount,
                    timestamp         = this.Runtime.Time,
                };

                _stakes.Set(from, entry);

                RemoveVotingPower(from, unstakeAmount);
            }

            if (stake.totalAmount < MasterAccountThreshold)
            {
                var count = _mastersList.Count();
                var index = -1;
                for (int i = 0; i < count; i++)
                {
                    var master = _mastersList.Get <EnergyMaster>(i);
                    if (master.address == from)
                    {
                        index = i;
                        break;
                    }
                }

                if (index >= 0)
                {
                    var penalizationDate = GetMasterClaimDateFromReference(1, _mastersList.Get <EnergyMaster>(index).claimDate);
                    _mastersList.RemoveAt <EnergyMaster>(index);

                    Runtime.Notify(EventKind.RoleDemote, from, new RoleEventData()
                    {
                        role = "master", date = penalizationDate
                    });
                }
            }

            Runtime.Notify(EventKind.TokenUnstake, from, new TokenEventData()
            {
                chainAddress = Runtime.Chain.Address, symbol = token.Symbol, value = unstakeAmount
            });

            return(unstakeAmount);
        }