Beispiel #1
0
        /*
         *
         * Next Private Key = hash( current public key + seed + currentIndex )
         */
        private Wallet next(byte[] seed)
        {
            byte[] currentIndexByte = BitConverter.GetBytes(currentIndex);

            byte[] beforehash = new byte[keyPair.PublicKey.Length + seed.Length + currentIndexByte.Length];

            System.Buffer.BlockCopy(keyPair.PublicKey, 0, beforehash, 0, keyPair.PublicKey.Length);
            System.Buffer.BlockCopy(seed, 0, beforehash, keyPair.PublicKey.Length, seed.Length);
            System.Buffer.BlockCopy(currentIndexByte, 0, beforehash, keyPair.PublicKey.Length + seed.Length, currentIndexByte.Length);

            byte[] nextPrivKey = Hash.ComputeDoubleSHA256(beforehash);

            byte[] nextPubKeyByte;

            EccService.GenerateKeyFromPrivateKey(nextPrivKey, out nextPubKeyByte);

            KeyPair nextKeyPair = new KeyPair
            {
                PrivateKey = nextPrivKey,
                PublicKey  = nextPubKeyByte,
                Address    = BlockchainUtil.ToAddress(nextPubKeyByte),
            };

            return(new Wallet(nextKeyPair, currentIndex + 1));
        }
Beispiel #2
0
        public static KeyPair LoadFrom(string path)
        {
            var keyContent = File.ReadAllText(path);
            var keyPair    = JsonConvert.DeserializeObject <KeyPair>(keyContent);

            if (!EccService.TestKey(keyPair.PrivateKey, keyPair.PublicKey))
            {
                throw new InvalidDataException("Collapsed keypair.");
            }

            return(keyPair);
        }
Beispiel #3
0
        public static void Exec(string[] args)
        {
            byte[] publicKey;
            byte[] privateKey;
            EccService.GenerateKey(out privateKey, out publicKey);

            var json = JsonConvert.SerializeObject(
                new KeyPair
            {
                PrivateKey = privateKey,
                PublicKey  = publicKey,
                Address    = BlockchainUtil.ToAddress(publicKey),
            },
                Formatting.Indented);

            Console.WriteLine(json);
        }
Beispiel #4
0
        public void Run(Transaction tx,
                        DateTime blockTime, ulong coinbase = 0,
                        List <TransactionOutput> spentTxo  = null)
        {
            logger.LogDebug($@"Attempt to run TX:{
                tx.Id.ToString().Substring(0, 7)}");

            // Transaction header validity check.
            if (tx.Timestamp > blockTime ||
                !(coinbase == 0 ^ tx.InEntries.Count == 0))
            {
                throw new ArgumentException();
            }

            // In-Entry validity check.
            ulong inSum    = coinbase;
            var   redeemed = new List <TransactionOutput>();
            var   signHash = BlockchainUtil.GetTransactionSignHash(tx.Original);

            foreach (var inEntry in tx.InEntries)
            {
                // Signature check.
                var verified = EccService.Verify(
                    signHash, inEntry.Signature, inEntry.PublicKey);

                // UTXO check. The transaction output must not be spent by
                // previous transactions.
                var txo = new TransactionOutput
                {
                    TransactionId = inEntry.TransactionId,
                    OutIndex      = inEntry.OutEntryIndex,
                };
                var unspent =
                    !(spentTxo?.Contains(txo) ?? false) &&
                    Utxos.TryGetValue(txo, out txo);

                // Recipient address check.
                var addr       = BlockchainUtil.ToAddress(inEntry.PublicKey);
                var redeemable = txo.Recipient.Equals(
                    ByteString.CopyFrom(addr));

                // Sum all the reedemable.
                inSum = checked (inSum + txo.Amount);

                if (!verified || !unspent || !redeemable)
                {
                    throw new ArgumentException();
                }
                redeemed.Add(txo);
            }

            // Out-entry validity check.
            ulong  outSum    = 0;
            ushort outIndex  = 0;
            var    generated = new List <TransactionOutput>();

            foreach (var outEntry in tx.OutEntries)
            {
                if (outEntry.RecipientHash.IsNull() || outEntry.Amount <= 0)
                {
                    throw new ArgumentException();
                }

                // Sum all the transferred.
                outSum = checked (outSum + outEntry.Amount);

                // Create new UTXO entry.
                generated.Add(new TransactionOutput
                {
                    TransactionId = tx.Id,
                    OutIndex      = outIndex++,
                    Recipient     = outEntry.RecipientHash,
                    Amount        = outEntry.Amount,
                });
            }

            // Output exceeds input or coinbase.
            if (outSum > inSum)
            {
                throw new ArgumentException();
            }

            tx.ExecInfo = new TransactionExecInformation
            {
                Coinbase         = coinbase != 0,
                RedeemedOutputs  = redeemed,
                GeneratedOutputs = generated,
                TransactionFee   = inSum - outSum,
            };
        }
Beispiel #5
0
        public Transaction SendTo(
            HashSet <TransactionOutput> utxos,
            ByteString recipient, ulong amount)
        {
            // TODO: You should consider transaction fee.

            // Extract my spendable UTXOs.
            ulong sum       = 0;
            var   inEntries = new List <InEntry>();

            foreach (var utxo in utxos)
            {
                if (!utxo.Recipient.Equals(Address))
                {
                    continue;
                }
                inEntries.Add(new InEntry
                {
                    TransactionId = utxo.TransactionId,
                    OutEntryIndex = utxo.OutIndex,
                });

                sum += utxo.Amount;
                if (sum >= amount)
                {
                    goto CreateOutEntries;
                }
            }

            throw new ArgumentException(
                      "Insufficient fund.", nameof(amount));

CreateOutEntries:
            // Create list of out entries.  It should contain fund transfer and
            // change if necessary.  Also the sum of outputs must be less than
            // that of inputs.  The difference will be collected as transaction
            // fee.
            var outEntries = new List <OutEntry>
            {
                new OutEntry
                {
                    RecipientHash = recipient,
                    Amount        = amount,
                },
            };

            var change = sum - amount;

            if (change != 0)
            {
                outEntries.Add(new OutEntry
                {
                    RecipientHash = Address,
                    Amount        = change,
                });
            }

            // Construct to-be-signed transaction.
            var transaction = new Transaction
            {
                Timestamp  = DateTime.UtcNow,
                InEntries  = inEntries,
                OutEntries = outEntries,
            };

            // Take a transaction signing hash and sign against it.  Since
            // wallet contains a single key pair, single signing is sufficient.
            var signHash = BlockchainUtil.GetTransactionSignHash(
                Serialize(transaction));
            var signature = EccService.Sign(
                signHash, keyPair.PrivateKey, keyPair.PublicKey);

            foreach (var inEntry in inEntries)
            {
                inEntry.PublicKey = keyPair.PublicKey;
                inEntry.Signature = signature;
            }

            var bytes = Serialize(transaction);

            return(BlockchainUtil.DeserializeTransaction(bytes));
        }