func TestBlockchainTail_03(t *testing.T) { bq := BlockchainTail{} bq.Init() h1 := cipher.SumSHA256(secp256k1.RandByte(888)) b1 := BlockBase{Hash: h1, Seqno: 1} // OK to leave '.sig' empty r1 := bq.try_append_to_BlockchainTail(&b1) if r1 != 0 { t.Log("BlockchainTail::try_append_to_BlockchainTail(): initial insert failed.") t.Fail() } if bq.GetNextSeqNo() != b1.Seqno+1 { t.Log("BlockchainTail::GetNextSeqNo() failed.") t.Fail() } r1dup := bq.try_append_to_BlockchainTail(&b1) if r1dup != 1 { t.Log("BlockchainTail::try_append_to_BlockchainTail(): duplicate hash not detected.") t.Fail() } h2 := cipher.SumSHA256(secp256k1.RandByte(888)) b2 := BlockBase{Hash: h2, Seqno: 2} // OK to leave '.sig' empty r2 := bq.try_append_to_BlockchainTail(&b2) if r2 != 0 { t.Log("BlockchainTail::try_append_to_BlockchainTail(): next insert failed.") t.Fail() } if bq.GetNextSeqNo() != b2.Seqno+1 { t.Log("BlockchainTail::GetNextSeqNo() failed.") t.Fail() } h3 := cipher.SumSHA256(secp256k1.RandByte(888)) b3 := BlockBase{Hash: h3, Seqno: 0} // OK to leave '.sig' empty r3 := bq.try_append_to_BlockchainTail(&b3) if r3 != 2 { t.Log("BlockchainTail::try_append_to_BlockchainTail(): low seqno not detected. ret=", r3) t.Fail() } b3.Seqno = 4 r4 := bq.try_append_to_BlockchainTail(&b3) if r4 != 3 { t.Log("BlockchainTail::try_append_to_BlockchainTail(): high seqno not detected.") t.Fail() } }
//applies block against the current head func (bc *BlockChain) ApplyBlock(block Block) error { //do time check //do prevhash check //check body hash //check BkSeq if block.Head.BkSeq != bc.Head().Head.BkSeq+1 { return errors.New("block sequence is out of order") } if block.Head.PrevHash != bc.Head().Head.Hash() { return errors.New("block PrevHash does not match current head") } if block.Head.Time < bc.Head().Head.Time { return errors.New("block time invalid") } if block.Head.BodyHash != cipher.SumSHA256(block.Body) { return errors.New("block body hash is wrong") } if err := bc.VerifyBlockSignature(block); err != nil { return errors.New("block signature check failed") } //block is valid, apply bc.Blocks = append(bc.Blocks, block) return nil }
//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 TestBlockchainTail_02(t *testing.T) { bq := BlockchainTail{} bq.Init() // Use more than configured length to ensure some elements are // removed: n := Cfg_blockchain_tail_length * 2 for i := 0; i < n; i++ { x := secp256k1.RandByte(888) // Random data. h := cipher.SumSHA256(x) // Its hash. b := BlockBase{Hash: h, Seqno: uint64(i)} // OK to leave '.sig' empty bq.append_nocheck(&b) } if len(bq.blockPtr_slice) != Cfg_blockchain_tail_length { t.Log("BlockchainTail::append_nocheck() incorrect append or remove.") t.Fail() } if !bq.is_consistent() { t.Log("BlockchainTail::is_consistent()") t.Fail() } }
func TestBlockStat_01(t *testing.T) { bs := BlockStat{} bs.Init() _, seckey := cipher.GenerateKeyPair() hash := cipher.SumSHA256(secp256k1.RandByte(888)) sig := cipher.SignHash(hash, seckey) var r int = -1 r = bs.try_add_hash_and_sig(hash, cipher.Sig{}) if r != 4 { t.Log("BlockStat::try_add_hash_and_sig() failed to detect invalid signature.") t.Fail() } r = bs.try_add_hash_and_sig(cipher.SHA256{}, sig) if r != 4 { t.Log("BlockStat::try_add_hash_and_sig() failed to detect invalid hash and signature.") t.Fail() } r = bs.try_add_hash_and_sig(cipher.SHA256{}, cipher.Sig{}) if r != 4 { t.Log("BlockStat::try_add_hash_and_sig() failed to detect invalid hash and signature.") t.Fail() } //signer_pubkey, err := cipher.PubKeyFromSig(cipher.Sig{}, cipher.SHA256{}) //if err != nil { //fmt.Printf("Got pubkey='%s' from all-zero sig and all-zero hash.\n", signer_pubkey.Hex()) //} bs.frozen = true r2 := bs.try_add_hash_and_sig(hash, sig) if r2 != 3 { t.Log("BlockStat::try_add_hash_and_sig() failed to detect frozen.") t.Fail() } bs.frozen = false r3 := bs.try_add_hash_and_sig(hash, sig) if r3 != 0 { t.Log("BlockStat::try_add_hash_and_sig() failed to add.") t.Fail() } sig2 := cipher.SignHash(hash, seckey) // Redo signing. r4 := bs.try_add_hash_and_sig(hash, sig2) if r4 != 1 { t.Log("BlockStat::try_add_hash_and_sig() failed to detect duplicate (hash,pubkey).") t.Fail() } r5 := bs.try_add_hash_and_sig(hash, sig) if r5 != 1 { t.Log("BlockStat::try_add_hash_and_sig() failed to detect duplicate (hash,sig).") t.Fail() } }
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 }
func NewEmptySimpleWallet() Wallet { idHash := cipher.SumSHA256(secp256k1.RandByte(256)) id := WalletID(hex.EncodeToString(idHash[:16])) return &SimpleWallet{ Filename: NewWalletFilename(id), Entries: WalletEntries{}, ID: id, } }
//creates new block func (bc *BlockChain) NewBlock(seckey cipher.SecKey, blockTime uint64, data []byte) Block { var b Block b.Head.Time = blockTime b.Head.BkSeq = bc.Head().Head.BkSeq + 1 b.Head.PrevHash = bc.Head().Head.Hash() b.Head.BodyHash = cipher.SumSHA256(data) b.Body = data bc.SignBlock(seckey, &b) return b }
func createUnconfirmedTxn() visor.UnconfirmedTxn { now := util.Now() return visor.UnconfirmedTxn{ Txn: coin.Transaction{ Head: coin.TransactionHeader{ Hash: cipher.SumSHA256([]byte("cascas")), }, }, Received: now, Checked: now, Announced: util.ZeroTime(), } }
func TestBlockStat_02(t *testing.T) { bs := BlockStat{} bs.Init() hash1 := cipher.SumSHA256(secp256k1.RandByte(888)) n1 := 3 for i := 0; i < n1; i++ { _, seckey := cipher.GenerateKeyPair() sig := cipher.SignHash(hash1, seckey) bs.try_add_hash_and_sig(hash1, sig) } hash2 := cipher.SumSHA256(secp256k1.RandByte(888)) n2 := 2 for i := 0; i < n2; i++ { _, seckey := cipher.GenerateKeyPair() sig := cipher.SignHash(hash2, seckey) bs.try_add_hash_and_sig(hash2, sig) } hash3 := cipher.SumSHA256(secp256k1.RandByte(888)) n3 := 1 for i := 0; i < n3; i++ { _, seckey := cipher.GenerateKeyPair() sig := cipher.SignHash(hash3, seckey) bs.try_add_hash_and_sig(hash3, sig) } best_hash, _, _ := bs.GetBestHashPubkeySig() if best_hash != hash1 { t.Log("BlockStat::try_add_hash_and_sig() or BlockStat::GetBestHashPubkeySig() issue.") t.Fail() } }
//Generate Deterministic Wallet //generates a random seed if seed is "" func NewWallet(seed string) Wallet { //if seed is blank, generate a new seed if seed == "" { seed_raw := cipher.SumSHA256(secp256k1.RandByte(64)) seed = hex.EncodeToString(seed_raw[:]) } pub, sec := cipher.GenerateDeterministicKeyPair([]byte(seed[:])) return Wallet{ Meta: map[string]string{ "filename": NewWalletFilename(), "seed": seed, "type": "deterministic", "coin": "sky"}, Entry: NewWalletEntryFromKeypair(pub, sec), } }
func TestGetTxnsMessageProcess(t *testing.T) { v, _ := setupVisor() d, _ := newVisorDaemon(v) defer shutdown(d) gc := setupExistingPool(d.Pool) p := d.Pool go p.Pool.ConnectionWriteLoop(gc) tx := createUnconfirmedTxn() tx.Txn.Head.Hash = cipher.SumSHA256([]byte("asdadwadwada")) txns := []cipher.SHA256{tx.Txn.Hash()} m := NewGetTxnsMessage(txns) m.c = messageContext(addr) go p.Pool.ConnectionWriteLoop(m.c.Conn) defer m.c.Conn.Close() // We don't have any to reply with assert.NotPanics(t, func() { m.Process(d) }) assert.True(t, m.c.Conn.LastSent.IsZero()) // Disabled, nothing should happen d.Visor.Visor.Unconfirmed.Txns[tx.Txn.Hash()] = tx d.Visor.Config.Disabled = true assert.NotPanics(t, func() { m.Process(d) }) assert.True(t, m.c.Conn.LastSent.IsZero()) // We have some to reply with d.Visor.Config.Disabled = false assert.NotPanics(t, func() { m.Process(d) }) wait() assert.Equal(t, len(p.Pool.SendResults), 1) if len(p.Pool.SendResults) == 0 { t.Fatal("SendResults empty, would block") } sr := <-p.Pool.SendResults assert.Equal(t, sr.Connection, m.c.Conn) assert.Nil(t, sr.Error) _, ok := sr.Message.(*GiveTxnsMessage) assert.True(t, ok) assert.False(t, m.c.Conn.LastSent.IsZero()) // Should not be broadcast to others assert.True(t, gc.LastSent.IsZero()) }
// NewWallet generates Deterministic Wallet // generates a random seed if seed is "" func NewWallet(wltName string, opts ...Option) Wallet { seedRaw := cipher.SumSHA256(secp256k1.RandByte(64)) seed := hex.EncodeToString(seedRaw[:]) w := Wallet{ Meta: map[string]string{ "filename": wltName, "version": version, "label": "", "seed": seed, "lastSeed": seed, "tm": fmt.Sprintf("%v", time.Now().Unix()), "type": "deterministic", "coin": "sky"}, } for _, opt := range opts { opt(&w) } return w }
func main() { registerFlags() parseFlags() w := Wallet{ Meta: make(map[string]string), //map[string]string Entries: make([]KeyEntry, genCount), } if BitcoinAddress == false { w.Meta = map[string]string{"coin": "skycoin"} } else { w.Meta = map[string]string{"coin": "bitcoin"} } if seed == "" { //generate a new seed, as hex string seed = cipher.SumSHA256(cipher.RandByte(1024)).Hex() } w.Meta["seed"] = seed seckeys := cipher.GenerateDeterministicKeyPairs([]byte(seed), genCount) for i, sec := range seckeys { pub := cipher.PubKeyFromSecKey(sec) w.Entries[i] = getKeyEntry(pub, sec) } output, err := json.MarshalIndent(w, "", " ") if err != nil { fmt.Printf("Error formating wallet to JSON. Error : %s\n", err.Error()) return } fmt.Printf("%s\n", string(output)) }
// Hashes only the Transaction Inputs & Outputs // This is what is signed // Client hashes the inner hash with hash of output being spent and signs it with private key func (self *Transaction) HashInner() cipher.SHA256 { b1 := encoder.Serialize(self.In) b2 := encoder.Serialize(self.Out) b3 := append(b1, b2...) return cipher.SumSHA256(b3) }
// Returns the encoded size and the hash of it (avoids duplicate encoding) func (self *Transaction) SizeHash() (int, cipher.SHA256) { b := self.Serialize() return len(b), cipher.SumSHA256(b) }
// Hashes an entire Transaction struct, including the TransactionHeader func (self *Transaction) Hash() cipher.SHA256 { b := self.Serialize() return cipher.SumSHA256(b) }
func randSHA256() cipher.SHA256 { b := make([]byte, 128) rand.Read(b) return cipher.SumSHA256(b) }
func (self *DeterministicWalletSeed) toWalletID() WalletID { // Uses the first 16 bytes of SHA256(seed) as id shaid := cipher.SumSHA256(self[:]) return WalletID(hex.EncodeToString(shaid[:16])) }
func randSHA256(t *testing.T) cipher.SHA256 { return cipher.SumSHA256(randBytes(t, 128)) }
func TestVisorResendTransaction(t *testing.T) { defer cleanupVisor() defer gnet.EraseMessages() p, gc := setupPool() go p.Pool.ConnectionWriteLoop(gc) vc, mv := setupVisor() v := NewVisor(vc) assert.Equal(t, len(v.Visor.Unconfirmed.Txns), 0) // Nothing should happen if txn unknown v.ResendTransaction(cipher.SumSHA256([]byte("garbage")), p) wait() assert.Equal(t, len(p.Pool.SendResults), 0) assert.Equal(t, len(p.Pool.SendResults), 0) assert.Equal(t, len(v.Visor.Unconfirmed.Txns), 0) assert.True(t, gc.LastSent.IsZero()) // give the visor some coins, and make a spend to add a txn assert.Nil(t, transferCoins(mv, v.Visor)) tx, err := v.Spend(v.Visor.Wallets[0].GetID(), wallet.Balance{10e6, 0}, 0, mv.Wallets[0].GetAddresses()[0], p) assert.Nil(t, err) wait() assert.Equal(t, len(p.Pool.SendResults), 1) if len(p.Pool.SendResults) == 0 { t.Fatal("SendResults empty, would block") } <-p.Pool.SendResults assert.Equal(t, len(v.Visor.Unconfirmed.Txns), 1) h := tx.Hash() ut := v.Visor.Unconfirmed.Txns[h] ut.Announced = util.ZeroTime() v.Visor.Unconfirmed.Txns[h] = ut assert.True(t, v.Visor.Unconfirmed.Txns[h].Announced.IsZero()) // Reset the sent timer since we made a successful spend gc.LastSent = util.ZeroTime() // Nothing should send if disabled v.Config.Disabled = true v.ResendTransaction(h, p) wait() assert.Equal(t, len(p.Pool.SendResults), 0) ann := v.Visor.Unconfirmed.Txns[h].Announced assert.True(t, ann.IsZero()) assert.True(t, gc.LastSent.IsZero()) // Should have resent v.Config.Disabled = false gc.Conn = NewDummyConn(addr) v.ResendTransaction(h, p) wait() assert.Equal(t, len(p.Pool.SendResults), 1) if len(p.Pool.SendResults) == 0 { t.Fatal("SendResults empty, would block") } sr := <-p.Pool.SendResults assert.Nil(t, sr.Error) assert.Equal(t, sr.Connection, gc) _, ok := sr.Message.(*GiveTxnsMessage) assert.True(t, ok) ann = v.Visor.Unconfirmed.Txns[h].Announced // Announced state should not be updated until we process it assert.True(t, ann.IsZero()) assert.False(t, gc.LastSent.IsZero()) }
// Returns hash of UxBody + UxHead func (self *UxOut) SnapshotHash() cipher.SHA256 { b1 := encoder.Serialize(self.Body) //body b2 := encoder.Serialize(self.Head) //time, bkseq b3 := append(b1, b2...) return cipher.SumSHA256(b3) }
func (self *BlockHeader) Hash() cipher.SHA256 { b1 := encoder.Serialize(*self) return cipher.SumSHA256(b1) }
//////////////////////////////////////////////////////////////////////////////// // // main // //////////////////////////////////////////////////////////////////////////////// func main() { cmd_line_args_process() // PERFORMANCE: cipher.DebugLevel1 = false cipher.DebugLevel2 = false var X []*MinimalConnectionManager var hack_global_seqno uint64 = 0 seed := "hdhdhdkjashfy7273" _, SecKeyArray := cipher.GenerateDeterministicKeyPairsSeed([]byte(seed), Cfg_simu_num_node) for i := 0; i < Cfg_simu_num_node; i++ { cm := MinimalConnectionManager{} // Reason for mutual registration: (1) when conn man receives // messages, it needs to notify the node; (2) when node has // processed a mesage, it might need to use conn man to send // some data out. nodePtr := consensus.NewConsensusParticipantPtr(&cm) s := SecKeyArray[i] nodePtr.SetPubkeySeckey(cipher.PubKeyFromSecKey(s), s) cm.theNodePtr = nodePtr X = append(X, &cm) } if false { fmt.Printf("Got %d nodes\n", len(X)) } if Cfg_simu_topology_is_random { fmt.Printf("CONFIG Topology: connecting %d nodes randomly with approx"+ " %d nearest-neighbors in and approx %d nearest-neighbors out.\n", Cfg_simu_num_node, Cfg_simu_fanout_per_node, Cfg_simu_fanout_per_node) for i, _ := range X { cm := X[i] for g := 0; g < Cfg_simu_fanout_per_node; g++ { j := mathrand.Intn(Cfg_simu_num_node) if i != j { cm.RegisterPublisher(X[j]) } } } } else { fmt.Printf("CONFIG Topology: connecting %d nodes via one (thick)"+ " circle with approx %d nearest-neighbors in and approx %d "+ "nearest-neighbors out.\n", Cfg_simu_num_node, Cfg_simu_fanout_per_node, Cfg_simu_fanout_per_node) n := len(X) for i := 0; i < n; i++ { cm := X[i] c_left := int(Cfg_simu_fanout_per_node / 2) c_right := Cfg_simu_fanout_per_node - c_left for c := 0; c < c_left; c++ { j := (i - 1 - c + n) % n cm.RegisterPublisher(X[j]) } for c := 0; c < c_right; c++ { j := (i + 1 + c) % n cm.RegisterPublisher(X[j]) } } } // Connect. PROD: This should request connections. The // connections can be accepted, rejected or never answered. Such // replies are asynchronous. SIMU: we connect synchronously. for i, _ := range X { X[i].RequestConnectionToAllMyPublisher() } global_seqno2h := make(map[uint64]cipher.SHA256) global_seqno2h_alt := make(map[uint64]cipher.SHA256) iter := 0 block_round := 0 done_processing_messages := false for ; iter < Cfg_simu_num_iter; iter++ { if true { if block_round < Cfg_simu_num_block_round { // NOTE: Propagating blocks from here is a // simplification/HACK: it implies that we have // knowledge of when messaging due to previous // activity (blocks and connections) has // stopped. Again, we make blocks from here for // debugging and testing only. //x := secp256k1.RandByte(888) // Random data in SIMU. x := make([]byte, 888) mathrand.Read(x) h := cipher.SumSHA256(x) // Its hash. //x_alt := secp256k1.RandByte(888) // Random data in SIMU. x_alt := make([]byte, 888) mathrand.Read(x) h_alt := cipher.SumSHA256(x_alt) // Its hash. global_seqno2h[hack_global_seqno] = h global_seqno2h_alt[hack_global_seqno] = h_alt indices := get_random_index_subset(Cfg_simu_num_node, Cfg_simu_num_blockmaker) if Cfg_debug_show_block_maker { fmt.Printf("block_round=%d, Random indices of block-"+ "makers: %v\n", block_round, indices) } n_forkers := int(Cfg_simu_prob_malicious * float64(len(indices))) for i := 0; i < len(indices); i++ { // TODO: Have many nodes send same block, and a few nodes // send a different block. Research the conditions under // which the block published by the majority would // dominate the other one. index := indices[i] nodePtr := X[index].GetNode() malicious := (i < n_forkers) duplicate := (mathrand.Float64() < Cfg_simu_prob_duplicate) ph := &h if malicious { ph = &h_alt } rep := 1 if duplicate { rep = 2 } // // WARNING: In a reslistic simulation, one would // need to remove the assumption of knowing global // properties such as 'hack_global_seqno' // if malicious { fmt.Printf(">>>>>> NODE (index,pubkey)=(%d,%s) is"+ " publishing ALTERNATIVE block\n", index, nodePtr.Pubkey.Hex()[:8]) } for j := 0; j < rep; j++ { // Signing same hash multipe times produces different // signatures (for a good reason). We do it // here to test if malicious re-publishing is // detected properly. propagate_hash_from_node(*ph, nodePtr, true, hack_global_seqno) } } hack_global_seqno += 1 block_round += 1 } else { done_processing_messages = true break // <<<<<<<< } } } zzz := "done" if !done_processing_messages { zzz = "***NOT done***" } fmt.Printf("Done (i) making Blocks, %s (ii) processing responses."+ " See stats on the next few lines. Used iterations=%d, unused"+ " iterations=%d. Exiting the event loop now.\n", zzz, iter, Cfg_simu_num_iter-iter) print_stat(X, iter) if Cfg_debug_node_final_state { for i, _ := range X { fmt.Printf("FILE_FinalState.txt|NODE i=%d ", i) X[i].GetNode().Print() fmt.Printf("\n") } } if Cfg_debug_node_summary { Simulate_compare_node_StateQueue(X, global_seqno2h, global_seqno2h_alt) } }
//////////////////////////////////////////////////////////////////////////////// // // main // //////////////////////////////////////////////////////////////////////////////// func main() { var X []*MinimalConnectionManager // Create nodes for i := 0; i < Cfg_simu_num_node; i++ { cm := MinimalConnectionManager{} // Reason for mutual registration: (1) when conn man receives // messages, it needs to notify the node; (2) when node has // processed a mesage, it might need to use conn man to send // some data out. nodePtr := consensus.NewConsensusParticipantPtr(&cm) cm.theNodePtr = nodePtr X = append(X, &cm) } // Contemplate connecting nodes into a thick circle: n := len(X) for i := 0; i < n; i++ { cm := X[i] c_left := int(Cfg_simu_fanout_per_node / 2) c_right := Cfg_simu_fanout_per_node - c_left for c := 0; c < c_left; c++ { j := (i - 1 - c + n) % n cm.RegisterPublisher(X[j]) } for c := 0; c < c_right; c++ { j := (i + 1 + c) % n cm.RegisterPublisher(X[j]) } } // // Request connections // for i := 0; i < n; i++ { X[i].RequestConnectionToAllMyPublisher() } { // // Choose a node to be a block-maker // index := mathrand.Intn(Cfg_simu_num_node) nodePtr := X[index].GetNode() // // Make a block (actually, only a header) // x := secp256k1.RandByte(888) // Random data. h := cipher.SumSHA256(x) // Its hash. b := consensus.BlockBase{} b.Init( nodePtr.SignatureOf(h), h, 0) // // Send it to subscribers. The subscribers are also publishers; // they send (forward, to be exact) the header to thire respective // listeners etc. // nodePtr.OnBlockHeaderArrived(&b) } // // Print the state of each node for a review or debugging. // for i, _ := range X { fmt.Printf("FILE_FinalState.txt|NODE i=%d ", i) X[i].GetNode().Print() fmt.Printf("\n") } }
func (self *UxBody) Hash() cipher.SHA256 { return cipher.SumSHA256(encoder.Serialize(self)) }
func NewDeterministicWalletSeed() DeterministicWalletSeed { seed := cipher.SumSHA256(secp256k1.RandByte(DeterministicSeedLength)) return DeterministicWalletSeed(seed) }
func PubKeyHash(pubkey cipher.PubKey) cipher.SHA256 { return cipher.SumSHA256(pubkey[:]) }
// Hash return hash of block header func (bh BlockHeader) Hash() cipher.SHA256 { b1 := encoder.Serialize(bh) return cipher.SumSHA256(b1) }