func makeTransactionForChainWithHoursFee(t *testing.T, bc *Blockchain, ux coin.UxOut, sec cipher.SecKey, hours, fee uint64) (coin.Transaction, cipher.SecKey) { chrs := ux.CoinHours(bc.Time()) if chrs < hours+fee { log.Panicf("CoinHours underflow. Have %d, need at least %d", chrs, hours+fee) } assert.Equal(t, cipher.AddressFromPubKey(cipher.PubKeyFromSecKey(sec)), ux.Body.Address) knownUx, exists := bc.GetUnspent().Get(ux.Hash()) assert.True(t, exists) assert.Equal(t, knownUx, ux) tx := coin.Transaction{} tx.PushInput(ux.Hash()) p, newSec := cipher.GenerateKeyPair() addr := cipher.AddressFromPubKey(p) tx.PushOutput(addr, 1e6, hours) coinsOut := ux.Body.Coins - 1e6 if coinsOut > 0 { tx.PushOutput(genAddress, coinsOut, chrs-hours-fee) } tx.SignInputs([]cipher.SecKey{sec}) assert.Equal(t, len(tx.Sigs), 1) assert.Nil(t, cipher.ChkSig(ux.Body.Address, cipher.AddSHA256(tx.HashInner(), tx.In[0]), tx.Sigs[0])) tx.UpdateHeader() assert.Nil(t, tx.Verify()) err := bc.VerifyTransaction(tx) assert.Nil(t, err) return tx, newSec }
func TestTransactionSignInputs(t *testing.T) { tx := &Transaction{} // Panics if txns already signed tx.Sigs = append(tx.Sigs, cipher.Sig{}) assert.Panics(t, func() { tx.SignInputs([]cipher.SecKey{}) }) // Panics if not enough keys tx = &Transaction{} ux, s := makeUxOutWithSecret(t) tx.PushInput(ux.Hash()) ux2, s2 := makeUxOutWithSecret(t) tx.PushInput(ux2.Hash()) tx.PushOutput(makeAddress(), 40, 80) assert.Equal(t, len(tx.Sigs), 0) assert.Panics(t, func() { tx.SignInputs([]cipher.SecKey{s}) }) assert.Equal(t, len(tx.Sigs), 0) // Valid signing h := tx.HashInner() assert.NotPanics(t, func() { tx.SignInputs([]cipher.SecKey{s, s2}) }) assert.Equal(t, len(tx.Sigs), 2) assert.Equal(t, tx.HashInner(), h) p := cipher.PubKeyFromSecKey(s) a := cipher.AddressFromPubKey(p) p = cipher.PubKeyFromSecKey(s2) a2 := cipher.AddressFromPubKey(p) assert.Nil(t, cipher.ChkSig(a, cipher.AddSHA256(h, tx.In[0]), tx.Sigs[0])) assert.Nil(t, cipher.ChkSig(a2, cipher.AddSHA256(h, tx.In[1]), tx.Sigs[1])) assert.NotNil(t, cipher.ChkSig(a, h, tx.Sigs[1])) assert.NotNil(t, cipher.ChkSig(a2, h, tx.Sigs[0])) }
// addBlockToBlockchain test helper function // Adds 2 blocks to the blockchain and return an unspent that has >0 coin hours func addBlockToBlockchain(t *testing.T, bc *Blockchain) (coin.Block, coin.UxOut) { // Split the genesis block into two transactions assert.Equal(t, len(bc.GetUnspent().Array()), 1) ux := bc.GetUnspent().Array()[0] assert.Equal(t, ux.Body.Address, genAddress) pub := cipher.PubKeyFromSecKey(genSecret) assert.Equal(t, genAddress, cipher.AddressFromPubKey(pub)) sig := cipher.SignHash(ux.Hash(), genSecret) assert.Nil(t, cipher.ChkSig(ux.Body.Address, ux.Hash(), sig)) tx, sec := makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 0, 0) b, err := bc.NewBlockFromTransactions(coin.Transactions{tx}, _incTime) assert.Nil(t, err) assertExecuteBlock(t, bc, b, tx) assert.Equal(t, len(bc.GetUnspent().Array()), 2) // Spend one of them // The other will have hours now ux = coin.UxOut{} for _, u := range bc.GetUnspent().Pool { if u.Body.Address != genAddress { ux = u break } } assert.NotEqual(t, ux.Body.Address, cipher.Address{}) assert.NotEqual(t, ux.Body.Address, genAddress) pub = cipher.PubKeyFromSecKey(sec) addr := cipher.AddressFromPubKey(pub) assert.Equal(t, ux.Body.Address, addr) tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, sec, 0, 0) b, err = bc.NewBlockFromTransactions(coin.Transactions{tx}, bc.Time()+_incTime) assert.Nil(t, err) assertExecuteBlock(t, bc, b, tx) assert.Equal(t, len(bc.GetUnspent().Array()), 2) // Check that the output in the 2nd block is owned by genesis, // and has coin hours for _, u := range bc.GetUnspent().Pool { if u.Body.Address == genAddress { ux = u break } } assert.Equal(t, ux.Body.Address, genAddress) assert.Equal(t, ux.Head.BkSeq, uint64(1)) assert.True(t, ux.CoinHours(bc.Time()) > 0) return b, ux }
//test signatures func TestCrypto2(t *testing.T) { a := "5a42c0643bdb465d90bf673b99c14f5fa02db71513249d904573d2b8b63d353d" b, err := hex.DecodeString(a) if err != nil { t.Fatal(err) } if len(b) != 32 { t.Fatal() } seckey := cipher.NewSecKey(b) pubkey := cipher.PubKeyFromSecKey(seckey) addr := cipher.AddressFromPubKey(pubkey) _ = addr test := []byte("test message") hash := cipher.SumSHA256(test) err = cipher.TestSecKeyHash(seckey, hash) if err != nil { t.Fatal() } }
func (wlt *Wallet) GenerateAddresses(num int) []cipher.Address { var seckeys []cipher.SecKey var sd []byte var err error if len(wlt.Entries) == 0 { sd, seckeys = cipher.GenerateDeterministicKeyPairsSeed([]byte(wlt.getLastSeed()), num) } else { sd, err = hex.DecodeString(wlt.getLastSeed()) if err != nil { log.Panicf("decode hex seed failed,%v", err) } sd, seckeys = cipher.GenerateDeterministicKeyPairsSeed(sd, num) } wlt.setLastSeed(hex.EncodeToString(sd)) addrs := make([]cipher.Address, len(seckeys)) for i, s := range seckeys { p := cipher.PubKeyFromSecKey(s) a := cipher.AddressFromPubKey(p) addrs[i] = a wlt.Entries = append(wlt.Entries, WalletEntry{ Address: a, Secret: s, Public: p, }) } return addrs }
func getKeyEntry(pub cipher.PubKey, sec cipher.SecKey) KeyEntry { var e KeyEntry //skycoin address if BitcoinAddress == false { e = KeyEntry{ Address: cipher.AddressFromPubKey(pub).String(), Public: pub.Hex(), Secret: sec.Hex(), } } //bitcoin address if BitcoinAddress == true { e = KeyEntry{ Address: cipher.BitcoinAddressFromPubkey(pub), Public: pub.Hex(), Secret: cipher.BitcoinWalletImportFormatFromSeckey(sec), } } //hide the secret key if HideSeckey == true { e.Secret = "" } return e }
func NewWalletEntryFromKeypair(pub cipher.PubKey, sec cipher.SecKey) WalletEntry { return WalletEntry{ Address: cipher.AddressFromPubKey(pub), Public: pub, Secret: sec, } }
func TestFullTransaction(t *testing.T) { p1, s1 := cipher.GenerateKeyPair() a1 := cipher.AddressFromPubKey(p1) bc := NewBlockchain() bc.CreateGenesisBlock(a1, _genTime, _genCoins) tx := Transaction{} ux := bc.Unspent.Array()[0] tx.PushInput(ux.Hash()) p2, s2 := cipher.GenerateKeyPair() a2 := cipher.AddressFromPubKey(p2) tx.PushOutput(a1, ux.Body.Coins-6e6, 100) tx.PushOutput(a2, 1e6, 100) tx.PushOutput(a2, 5e6, 100) tx.SignInputs([]cipher.SecKey{s1}) tx.UpdateHeader() assert.Nil(t, tx.Verify()) assert.Nil(t, bc.VerifyTransaction(tx)) b, err := bc.NewBlockFromTransactions(Transactions{tx}, bc.Time()+_incTime) assert.Nil(t, err) _, err = bc.ExecuteBlock(b) assert.Nil(t, err) txo := CreateUnspents(bc.Head().Head, tx) tx = Transaction{} assert.Equal(t, txo[0].Body.Address, a1) assert.Equal(t, txo[1].Body.Address, a2) assert.Equal(t, txo[2].Body.Address, a2) ux0, ok := bc.Unspent.Get(txo[0].Hash()) assert.True(t, ok) ux1, ok := bc.Unspent.Get(txo[1].Hash()) assert.True(t, ok) ux2, ok := bc.Unspent.Get(txo[2].Hash()) assert.True(t, ok) tx.PushInput(ux0.Hash()) tx.PushInput(ux1.Hash()) tx.PushInput(ux2.Hash()) tx.PushOutput(a2, 10e6, 200) tx.PushOutput(a1, ux.Body.Coins-10e6, 100) tx.SignInputs([]cipher.SecKey{s1, s2, s2}) tx.UpdateHeader() assert.Nil(t, tx.Verify()) assert.Nil(t, bc.VerifyTransaction(tx)) b, err = bc.NewBlockFromTransactions(Transactions{tx}, bc.Time()+_incTime) assert.Nil(t, err) _, err = bc.ExecuteBlock(b) assert.Nil(t, err) }
// Creates a ReadableWalletEntry given a pubkey hex string. The Secret field // is left empty. func ReadableWalletEntryFromPubkey(pub string) ReadableWalletEntry { pubkey := cipher.MustPubKeyFromHex(pub) addr := cipher.AddressFromPubKey(pubkey) return ReadableWalletEntry{ Address: addr.String(), Public: pub, } }
func makeUxBodyWithSecret(t *testing.T) (coin.UxBody, cipher.SecKey) { p, s := cipher.GenerateKeyPair() return coin.UxBody{ SrcTransaction: cipher.SumSHA256(randBytes(t, 128)), Address: cipher.AddressFromPubKey(p), Coins: 10e6, Hours: 100, }, s }
//Generate Blockchain configuration for client only Blockchain, not intended to be synced to network func NewLocalBlockchain() Blockchain { pubkey, seckey := cipher.GenerateKeyPair() //generate new/random pubkey/private key fmt.Printf("NewLocalBlockchain: genesis address seckey= %v \n", seckey.Hex()) VC := NewBlockchain() VC.SecKey = seckey VC.Genesis.GenesisAddress = cipher.AddressFromPubKey(pubkey) VC.Genesis.GenesisTime = uint64(time.Now().Unix()) VC.InjectGenesisBlock() return VC }
func TestAddress1(t *testing.T) { a := "02fa939957e9fc52140e180264e621c2576a1bfe781f88792fb315ca3d1786afb8" b, err := hex.DecodeString(a) if err != nil { t.Fatal(err) } addr := cipher.AddressFromPubKey(cipher.NewPubKey(b)) _ = addr ///func SignHash(hash cipher.SHA256, sec SecKey) (Sig, error) { }
func TestAddress2(t *testing.T) { a := "5a42c0643bdb465d90bf673b99c14f5fa02db71513249d904573d2b8b63d353d" b, err := hex.DecodeString(a) if err != nil { t.Fail() } if len(b) != 32 { t.Fail() } seckey := cipher.NewSecKey(b) pubkey := cipher.PubKeyFromSecKey(seckey) addr := cipher.AddressFromPubKey(pubkey) _ = addr ///func SignHash(hash cipher.SHA256, sec SecKey) (Sig, error) { }
func makeAddress() cipher.Address { p, _ := cipher.GenerateKeyPair() return cipher.AddressFromPubKey(p) }
func addPrivateKeyCMD() gcli.Command { name := "addPrivateKey" return gcli.Command{ Name: name, Usage: "Add a private key to specific wallet", ArgsUsage: "[private key]", Description: fmt.Sprintf(`Add a private key to specific wallet, the default wallet(%s/%s) will be used if the wallet file or path is not specified`, cfg.WalletDir, cfg.DefaultWalletName), Flags: []gcli.Flag{ gcli.StringFlag{ Name: "f", Usage: "[wallet file or path] private key will be added to this wallet", }, }, OnUsageError: onCommandUsageError(name), Action: func(c *gcli.Context) error { // get private key skStr := c.Args().First() if skStr == "" { gcli.ShowSubcommandHelp(c) return nil } // get wallet file path w := c.String("f") if w == "" { w = filepath.Join(cfg.WalletDir, cfg.DefaultWalletName) } if !strings.HasSuffix(w, walletExt) { return errWalletName } // only wallet file name, no path. if filepath.Base(w) == w { w = filepath.Join(cfg.WalletDir, w) } wlt, err := wallet.Load(w) if err != nil { errorWithHelp(c, err) return nil } sk, err := cipher.SecKeyFromHex(skStr) if err != nil { return fmt.Errorf("invalid private key: %s, must be an hex string of length 64", skStr) } pk := cipher.PubKeyFromSecKey(sk) addr := cipher.AddressFromPubKey(pk) entry := wallet.WalletEntry{ Address: addr, Public: pk, Secret: sk, } if err := wlt.AddEntry(entry); err != nil { return err } dir, err := filepath.Abs(filepath.Dir(w)) if err != nil { return err } if err := wlt.Save(dir); err != nil { return errors.New("save wallet failed") } fmt.Println("success") return nil }, } // Commands = append(Commands, cmd) }
func TestVerifyTransactionSpending(t *testing.T) { ft := FakeTree{} bc := NewBlockchain(&ft, nil) bc.CreateGenesisBlock(genAddress, _genCoins, _genTime) // Overspending hours tx := coin.Transaction{} uxs := bc.GetUnspent().Array() tx.PushInput(uxs[0].Hash()) tx.PushOutput(genAddress, 1e6, uxs[0].Body.Hours) tx.PushOutput(genAddress, uxs[0].Body.Coins-1e6, 1) uxIn, err := bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut := coin.CreateUnspents(bc.Head().Head, tx) assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut), "Insufficient coin hours") // add block to blockchain. _, ux := addBlockToBlockchain(t, bc) // addBlockToBlockchain(t, bc) // Valid tx, _ = makeTransactionForChainWithHoursFee(t, bc, ux, genSecret, 100, 50) uxIn, err = bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut = coin.CreateUnspents(bc.Head().Head, tx) assert.Nil(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut)) // Destroying coins tx = coin.Transaction{} tx.PushInput(ux.Hash()) tx.PushOutput(genAddress, 1e6, 100) tx.PushOutput(genAddress, 10e6, 100) uxIn, err = bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut = coin.CreateUnspents(bc.Head().Head, tx) err = coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut) assert.NotNil(t, err) assert.Equal(t, err.Error(), "Transactions may not create or destroy coins") assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut), "Transactions may not create or destroy coins") // Insufficient coins tx = coin.Transaction{} tx.PushInput(ux.Hash()) p, s := cipher.GenerateKeyPair() a := cipher.AddressFromPubKey(p) coins := ux.Body.Coins assert.True(t, coins > 1e6) tx.PushOutput(a, 1e6, 100) tx.PushOutput(genAddress, coins-1e6, 100) tx.SignInputs([]cipher.SecKey{genSecret}) tx.UpdateHeader() b, err := bc.NewBlockFromTransactions(coin.Transactions{tx}, bc.Time()+_incTime) assert.Nil(t, err) uxs, err = bc.ExecuteBlock(&b) assert.Nil(t, err) tx = coin.Transaction{} tx.PushInput(uxs[0].Hash()) tx.PushOutput(a, 10e6, 1) tx.SignInputs([]cipher.SecKey{s}) tx.UpdateHeader() uxIn, err = bc.GetUnspent().GetMultiple(tx.In) assert.Nil(t, err) uxOut = coin.CreateUnspents(bc.Head().Head, tx) assertError(t, coin.VerifyTransactionSpending(bc.Time(), uxIn, uxOut), "Insufficient coins") }
package coin import ( "encoding/hex" "math/rand" "testing" "time" "github.com/skycoin/skycoin/src/cipher" "github.com/stretchr/testify/assert" ) var ( genPublic, genSecret = cipher.GenerateKeyPair() genAddress = cipher.AddressFromPubKey(genPublic) testMaxSize = 1024 * 1024 _genTime uint64 = 1000 _incTime uint64 = 3600 * 1000 _genCoins uint64 = 1000e6 _genCoinHours uint64 = 1000 * 1000 ) func assertError(t *testing.T, err error, msg string) { assert.NotNil(t, err) assert.Equal(t, err.Error(), msg) } func tNow() uint64 { return uint64(time.Now().UTC().Unix()) }