func TestSetAnnounced(t *testing.T) { ut := NewUnconfirmedTxnPool() assert.Equal(t, len(ut.Txns), 0) // Unknown should be safe and a noop assert.NotPanics(t, func() { ut.SetAnnounced(cipher.SHA256{}, util.Now()) }) assert.Equal(t, len(ut.Txns), 0) utx := createUnconfirmedTxn() assert.True(t, utx.Announced.IsZero()) ut.Txns[utx.Hash()] = utx now := util.Now() ut.SetAnnounced(utx.Hash(), now) assert.Equal(t, ut.Txns[utx.Hash()].Announced, now) }
// Adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (self *UnconfirmedTxnPool) RecordTxn(bc *coin.Blockchain, t coin.Transaction, addrs map[cipher.Address]byte, maxSize int, burnFactor uint64) (error, bool) { if err := VerifyTransaction(bc, &t, maxSize, burnFactor); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn h := t.Hash() ut, ok := self.Txns[h] if ok { now := util.Now() ut.Received = now ut.Checked = now self.Txns[h] = ut return nil, true } // Add txn to index self.Txns[h] = self.createUnconfirmedTxn(&bc.Unspent, t, addrs) // Add predicted unspents self.Unspent[h] = coin.CreateUnspents(bc.Head().Head, t) return nil, false }
// InjectTxn adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (utp *UnconfirmedTxnPool) InjectTxn(bc *Blockchain, t coin.Transaction) (error, bool) { if err := t.Verify(); err != nil { return err, false } if err := VerifyTransactionFee(bc, &t); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn h := t.Hash() ut, ok := utp.Txns[h] if ok { now := util.Now() ut.Received = now ut.Checked = now utp.Txns[h] = ut return nil, true } // Add txn to index unspent := bc.GetUnspent() utp.Txns[h] = utp.createUnconfirmedTxn(unspent, t) // Add predicted unspents utp.Unspent[h] = coin.CreateUnspents(bc.Head().Head, t) return nil, false }
// Adds a coin.Transaction to the pool, or updates an existing one's timestamps // Returns an error if txn is invalid, and whether the transaction already // existed in the pool. func (self *UnconfirmedTxnPool) RecordTxn(bc *coin.Blockchain, t coin.Transaction, addrs map[coin.Address]byte, maxSize int, burnFactor uint64) (error, bool) { if err := VerifyTransaction(bc, &t, maxSize, burnFactor); err != nil { return err, false } if err := bc.VerifyTransaction(t); err != nil { return err, false } // Update if we already have this txn ut, ok := self.Txns[t.Hash()] if ok { now := util.Now() ut.Received = now ut.Checked = now self.Txns[ut.Txn.Hash()] = ut return nil, true } // Add txn to index self.Txns[t.Hash()] = self.createUnconfirmedTxn(&bc.Unspent, t, addrs) // Add predicted unspents uxs := coin.CreateExpectedUnspents(t) for i, _ := range uxs { self.Unspent.Add(uxs[i]) } return nil, false }
// Creates an unconfirmed transaction func (self *UnconfirmedTxnPool) createUnconfirmedTxn(bcUnsp *coin.UnspentPool, t coin.Transaction, addrs map[coin.Address]byte) UnconfirmedTxn { now := util.Now() ut := UnconfirmedTxn{ Txn: t, Received: now, Checked: now, Announced: util.ZeroTime(), IsOurReceive: false, IsOurSpend: false, } // Check if this unspent is related to us if addrs != nil { // Check if this is one of our receiving txns for i, _ := range t.Out { if _, ok := addrs[t.Out[i].Address]; ok { ut.IsOurReceive = true break } } // Check if this is one of our spending txns for i, _ := range t.In { if ux, ok := bcUnsp.Get(t.In[i]); ok { if _, ok := addrs[ux.Body.Address]; ok { ut.IsOurSpend = true break } } } } return ut }
func main() { flag.Parse() if len(*host) == 0 { log.Fatalf("Missing required -host parameter") } var err error var notBefore time.Time if len(*validFrom) == 0 { notBefore = util.Now() } else { notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom) if err != nil { fmt.Fprintf(os.Stderr, "Failed to parse creation date: %v\n", err) os.Exit(1) } } err = util.GenerateCert(*certFile, *keyFile, *host, *organization, *rsaBits, *isCA, notBefore, *validFor) if err == nil { fmt.Printf("Created %s and %s\n", *certFile, *keyFile) } else { fmt.Fprintln(os.Stderr, "Failed to create cert and key") fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
// Send a ping if our last message sent was over pingRate ago func (self *Pool) sendPings() { now := util.Now() for _, c := range self.Pool.Pool { if c.LastSent.Add(self.Config.PingRate).Before(now) { self.Pool.SendMessage(c, &PingMessage{}) } } }
// Removes connections that have not sent a message in too long func (self *Pool) clearStaleConnections() { now := util.Now() for _, c := range self.Pool.Pool { if c.LastReceived.Add(self.Config.IdleLimit).Before(now) { self.Pool.Disconnect(c, DisconnectIdle) } } }
func createUnconfirmedTxn() UnconfirmedTxn { ut := UnconfirmedTxn{} ut.Txn = coin.Transaction{} ut.Txn.Head.Hash = randSHA256() ut.Received = util.Now() ut.Checked = ut.Received ut.Announced = util.ZeroTime() return ut }
func TestGenerateCert(t *testing.T) { defer os.Remove("certtest.pem") defer os.Remove("keytest.pem") err := GenerateCert("certtest.pem", "keytest.pem", "127.0.0.1", "org", 2048, false, util.Now(), time.Hour*24) assert.Nil(t, err) _, err = tls.LoadX509KeyPair("certtest.pem", "keytest.pem") assert.Nil(t, err) }
// Creates an unconfirmed transaction func (utp *UnconfirmedTxnPool) createUnconfirmedTxn(bcUnsp *coin.UnspentPool, t coin.Transaction) UnconfirmedTxn { now := util.Now() return UnconfirmedTxn{ Txn: t, Received: now, Checked: now, Announced: util.ZeroTime(), } }
// Creates an unconfirmed transaction func (self *UnconfirmedTxnPool) createUnconfirmedTxn(bcUnsp *coin.UnspentPool, t coin.Transaction, addrs map[coin.Address]byte) UnconfirmedTxn { now := util.Now() return UnconfirmedTxn{ Txn: t, Received: now, Checked: now, Announced: util.ZeroTime(), } }
// Returns transactions in which we are a party and have not been announced // in ago duration func (self *UnconfirmedTxnPool) GetOldOwnedTransactions(ago time.Duration) []UnconfirmedTxn { txns := make([]UnconfirmedTxn, 0) now := util.Now() for _, tx := range self.Txns { // TODO -- don't record IsOurSpend/IsOurReceive and do lookup each time? // Slower but more correct if (tx.IsOurSpend || tx.IsOurReceive) && now.Sub(tx.Announced) > ago { txns = append(txns, tx) } } return txns }
func TestVisorSetAnnounced(t *testing.T) { defer cleanupVisor() vc := newMasterVisorConfig(t) v := NewVisor(vc) now := util.Now() utx := addUnconfirmedTxn(v) assert.True(t, utx.Announced.IsZero()) assert.True(t, v.Unconfirmed.Txns[utx.Hash()].Announced.IsZero()) v.SetAnnounced(utx.Hash(), now) assert.False(t, v.Unconfirmed.Txns[utx.Hash()].Announced.IsZero()) assert.Equal(t, v.Unconfirmed.Txns[utx.Hash()].Announced, now) }
func createUnconfirmedTxn() visor.UnconfirmedTxn { now := util.Now() return visor.UnconfirmedTxn{ Txn: coin.Transaction{ Head: coin.TransactionHeader{ Hash: coin.SumSHA256([]byte("cascas")), }, }, Received: now, Checked: now, Announced: util.ZeroTime(), } }
// Checks all unconfirmed txns against the blockchain. maxAge is how long // we'll hold a txn regardless of whether it has been invalidated. // checkPeriod is how often we check the txn against the blockchain. func (self *UnconfirmedTxnPool) Refresh(bc *coin.Blockchain, checkPeriod, maxAge time.Duration) { now := util.Now() toRemove := make([]coin.SHA256, 0) for k, t := range self.Txns { if now.Sub(t.Received) >= maxAge { toRemove = append(toRemove, k) } else if now.Sub(t.Checked) >= checkPeriod { if bc.VerifyTransaction(t.Txn) == nil { t.Checked = now self.Txns[k] = t } else { toRemove = append(toRemove, k) } } } self.removeTxns(bc, toRemove) }
// Checks that certFile and keyFile exist and are files, and if not, // returns a slice of errors indicating status. // If neither certFile nor keyFile exist, they are automatically created // for host func CreateCertIfNotExists(host, certFile, keyFile string) []error { // check that cert/key both exist, or dont exist, errs := certKeyXor(certFile, keyFile) // Automatically create a new cert if neither files exist if !exist && len(errs) == 0 { logger.Info("Creating certificate %s", certFile) logger.Info("Creating key %s", keyFile) err := util.GenerateCert(certFile, keyFile, host, "Skycoind", 2048, false, util.Now(), 365*24*time.Hour) if err == nil { logger.Info("Created certificate %s for host %s", certFile, host) logger.Info("Created key %s for host %s", keyFile, host) } else { errs = append(errs, err) } } return errs }
// Refresh checks all unconfirmed txns against the blockchain. maxAge is how long // we'll hold a txn regardless of whether it has been invalidated. // checkPeriod is how often we check the txn against the blockchain. func (utp *UnconfirmedTxnPool) Refresh(bc *Blockchain, checkPeriod, maxAge time.Duration) { now := util.Now() var toRemove []cipher.SHA256 for k, t := range utp.Txns { if now.Sub(t.Received) >= maxAge { toRemove = append(toRemove, k) } else if now.Sub(t.Checked) >= checkPeriod { if bc.VerifyTransaction(t.Txn) == nil { t.Checked = now utp.Txns[k] = t } else { toRemove = append(toRemove, k) } } } utp.removeTxns(bc, toRemove) }
// Called when a ConnectEvent is processed off the onConnectEvent channel func (self *Daemon) onConnect(e ConnectEvent) { a := e.Addr if e.Solicited { logger.Info("Connected to %s as we requested", a) } else { logger.Info("Received unsolicited connection to %s", a) } delete(self.pendingConnections, a) c := self.Pool.Pool.Addresses[a] if c == nil { logger.Warning("While processing an onConnect event, no pool " + "connection was found") return } blacklisted := self.Peers.Peers.IsBlacklisted(a) if blacklisted { logger.Info("%s is blacklisted, disconnecting", a) self.Pool.Pool.Disconnect(c, DisconnectIsBlacklisted) return } if self.ipCountMaxed(a) { logger.Info("Max connections for %s reached, disconnecting", a) self.Pool.Pool.Disconnect(c, DisconnectIPLimitReached) return } self.recordIPCount(a) if e.Solicited { self.OutgoingConnections[a] = c } self.ExpectingIntroductions[a] = util.Now() logger.Debug("Sending introduction message to %s", a) m := NewIntroductionMessage(self.Messages.Mirror, self.Config.Version, self.Pool.Pool.Config.Port) self.Pool.Pool.SendMessage(c, m) }
// Removes unsolicited connections who haven't sent a version func (self *Daemon) cullInvalidConnections() { // This method only handles the erroneous people from the DHT, but not // malicious nodes now := util.Now() for a, t := range self.ExpectingIntroductions { // Forget about anyone that already disconnected if self.Pool.Pool.Addresses[a] == nil { delete(self.ExpectingIntroductions, a) continue } // Remove anyone that fails to send a version within introductionWait time if t.Add(self.Config.IntroductionWait).Before(now) { logger.Info("Removing %s for not sending a version", a) delete(self.ExpectingIntroductions, a) self.Pool.Pool.Disconnect(self.Pool.Pool.Addresses[a], DisconnectIntroductionTimeout) self.Peers.RemovePeer(a) } } }
func TestRecordMessageEventNeedsIntroduction(t *testing.T) { // Needs Introduction but didn't get it first d := newDefaultDaemon() m := &PingMessage{} m.c = messageContext(addr) d.Pool.Pool.Addresses[addr] = m.c.Conn d.Pool.Pool.Pool[m.c.Conn.Id] = m.c.Conn assert.Equal(t, len(d.messageEvents), 0) d.ExpectingIntroductions[addr] = util.Now() d.processMessageEvent(MessageEvent{m, m.c}) assert.Equal(t, len(d.Pool.Pool.DisconnectQueue), 1) if len(d.Pool.Pool.DisconnectQueue) == 0 { t.Fatal("DisconnectQueue empty, would block") } de := <-d.Pool.Pool.DisconnectQueue assert.Equal(t, de.ConnId, m.c.Conn.Id) assert.Equal(t, de.Reason, DisconnectNoIntroduction) delete(d.ExpectingIntroductions, addr) shutdown(d) }
func TestRecordMessageEventIsIntroduction(t *testing.T) { // Needs Introduction and thats what it has received d := newDefaultDaemon() d.ExpectingIntroductions[addr] = util.Now() assert.Equal(t, len(d.messageEvents), 0) m := NewIntroductionMessage(d.Messages.Mirror, d.Config.Version, d.Pool.Pool.Config.Port) m.c = messageContext(addr) err := d.recordMessageEvent(m, m.c) assert.Nil(t, err) assert.Equal(t, len(d.messageEvents), 1) if len(d.messageEvents) == 0 { t.Fatal("d.messageEvents empty, would block") } me := <-d.messageEvents _, ok := me.Message.(*IntroductionMessage) assert.Equal(t, len(d.Pool.Pool.DisconnectQueue), 0) assert.True(t, ok) delete(d.ExpectingIntroductions, addr) shutdown(d) }
// Called when a ConnectEvent is processed off the onConnectEvent channel func (self *Daemon) onConnect(c *gnet.Connection, solicited bool) { a := c.Addr() if solicited { logger.Info("Connected to %s as we requested", a) } else { logger.Info("Received unsolicited connection to %s", a) } serviceConList := self.pendingConnections[a] //list of services to connect to delete(self.pendingConnections, a) blacklisted := self.Peers.Peers.IsBlacklisted(a) if blacklisted { logger.Info("%s is blacklisted, disconnecting", a) self.Pool.Disconnect(c, DisconnectIsBlacklisted) return } if self.Pool.Addresses[a] != nil { logger.Info("Already connected to %s, disconnecting", a) self.Pool.Disconnect(c, DisconnectConnectedTwice) } if solicited { self.OutgoingConnections[a] = c } self.ExpectingIntroductions[a] = util.Now() logger.Debug("Sending introduction message to %s", a) m := NewIntroductionMessage(MirrorConstant, self.Config.Version, self.Pool.Config.Port) self.Service.Send(c, m) //send connection message to each service in list for _, service := range serviceConList { self.ConnectToService(c, service) } }
func TestGetOldOwnedTransactions(t *testing.T) { mv := setupMasterVisor() up := mv.Unconfirmed // Setup txns notOursNew, err := makeValidTxn(mv) assert.Nil(t, err) notOursOld, err := makeValidTxn(mv) assert.Nil(t, err) ourSpendNew, err := makeValidTxn(mv) assert.Nil(t, err) ourSpendOld, err := makeValidTxn(mv) assert.Nil(t, err) ourReceiveNew, err := makeValidTxn(mv) assert.Nil(t, err) ourReceiveOld, err := makeValidTxn(mv) assert.Nil(t, err) ourBothNew, err := makeValidTxn(mv) assert.Nil(t, err) ourBothOld, err := makeValidTxn(mv) assert.Nil(t, err) // Add a transaction that is not ours, both new and old err, known := up.RecordTxn(mv.blockchain, notOursNew, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(notOursNew.Hash(), util.Now()) err, known = up.RecordTxn(mv.blockchain, notOursOld, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) // Add a transaction that is our spend, both new and old addrs := make(map[cipher.Address]byte, 1) ux, ok := mv.blockchain.Unspent.Get(ourSpendNew.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourSpendNew, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(ourSpendNew.Hash(), util.Now()) addrs = make(map[cipher.Address]byte, 1) ux, ok = mv.blockchain.Unspent.Get(ourSpendNew.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourSpendOld, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) // Add a transaction that is our receive, both new and old addrs = make(map[cipher.Address]byte, 1) addrs[ourReceiveNew.Out[1].Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourReceiveNew, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(ourReceiveNew.Hash(), util.Now()) addrs = make(map[cipher.Address]byte, 1) addrs[ourReceiveOld.Out[1].Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourReceiveOld, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) // Add a transaction that is both our spend and receive, both new and old addrs = make(map[cipher.Address]byte, 2) ux, ok = mv.blockchain.Unspent.Get(ourBothNew.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) addrs[ourBothNew.Out[1].Address] = byte(1) assert.Equal(t, len(addrs), 2) err, known = up.RecordTxn(mv.blockchain, ourBothNew, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(ourBothNew.Hash(), util.Now()) addrs = make(map[cipher.Address]byte, 1) ux, ok = mv.blockchain.Unspent.Get(ourBothOld.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) addrs[ourBothOld.Out[1].Address] = byte(1) assert.Equal(t, len(addrs), 2) err, known = up.RecordTxn(mv.blockchain, ourBothOld, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) }
func testRefresh(t *testing.T, mv *Visor, refresh func(checkPeriod, maxAge time.Duration)) { up := mv.Unconfirmed // Add a transaction that is invalid, but will not be checked yet // Add a transaction that is invalid, and will be checked and removed invalidTxUnchecked, err := makeValidTxn(mv) assert.Nil(t, err) invalidTxChecked, err := makeValidTxn(mv) assert.Nil(t, err) assert.Nil(t, invalidTxUnchecked.Verify()) assert.Nil(t, invalidTxChecked.Verify()) // Invalidate it by spending the output that this txn references invalidator, err := makeValidTxn(mv) assert.Nil(t, err) assert.Nil(t, up.InjectTxn(mv.blockchain, invalidator, nil, false)) assert.Equal(t, len(up.Txns), 1) _, err = mv.CreateAndExecuteBlock() assert.Nil(t, err) assert.Equal(t, len(up.Txns), 0) assert.NotNil(t, mv.blockchain.VerifyTransaction(invalidTxUnchecked)) assert.NotNil(t, mv.blockchain.VerifyTransaction(invalidTxChecked)) invalidUtxUnchecked := UnconfirmedTxn{ Txn: invalidTxUnchecked, Received: util.Now(), Checked: util.Now(), Announced: util.ZeroTime(), } invalidUtxChecked := invalidUtxUnchecked invalidUtxChecked.Txn = invalidTxChecked invalidUtxUnchecked.Checked = util.Now().Add(time.Hour) invalidUtxChecked.Checked = util.Now().Add(-time.Hour) up.Txns[invalidUtxUnchecked.Hash()] = invalidUtxUnchecked up.Txns[invalidUtxChecked.Hash()] = invalidUtxChecked assert.Equal(t, len(up.Txns), 2) bh := coin.BlockHeader{} for _, ux := range mv.blockchain.TxUxOut(invalidTxUnchecked, bh) { up.Unspent.Add(ux) } for _, ux := range mv.blockchain.TxUxOut(invalidTxChecked, bh) { up.Unspent.Add(ux) } // Add a transaction that is valid, and will not be checked yet validTxUnchecked, err := makeValidTxn(mv) assert.Nil(t, err) assert.Nil(t, up.InjectTxn(mv.blockchain, validTxUnchecked, nil, false)) assert.Equal(t, len(up.Txns), 3) validUtxUnchecked := up.Txns[validTxUnchecked.Hash()] validUtxUnchecked.Checked = util.Now().Add(time.Hour) up.Txns[validUtxUnchecked.Hash()] = validUtxUnchecked // Add a transaction that is valid, and will be checked validTxChecked, err := makeValidTxn(mv) assert.Nil(t, err) assert.Nil(t, up.InjectTxn(mv.blockchain, validTxChecked, nil, false)) assert.Equal(t, len(up.Txns), 4) validUtxChecked := up.Txns[validTxChecked.Hash()] validUtxChecked.Checked = util.Now().Add(-time.Hour) up.Txns[validUtxChecked.Hash()] = validUtxChecked // Add a transaction that is expired validTxExpired, err := makeValidTxn(mv) assert.Nil(t, err) assert.Nil(t, up.InjectTxn(mv.blockchain, validTxExpired, nil, false)) assert.Equal(t, len(up.Txns), 5) validUtxExpired := up.Txns[validTxExpired.Hash()] validUtxExpired.Received = util.Now().Add(-time.Hour) up.Txns[validTxExpired.Hash()] = validUtxExpired // Pre-sanity check assert.Equal(t, len(up.Unspent.Arr), 2*5) assert.Equal(t, len(up.Txns), 5) // Refresh checkPeriod := time.Second * 2 maxAge := time.Second * 4 refresh(checkPeriod, maxAge) // All utxns that are unchecked should be exactly the same assert.Equal(t, up.Txns[validUtxUnchecked.Hash()], validUtxUnchecked) assert.Equal(t, up.Txns[invalidUtxUnchecked.Hash()], invalidUtxUnchecked) // The valid one that is checked should have its checked status updated validUtxCheckedUpdated := up.Txns[validUtxChecked.Hash()] assert.True(t, validUtxCheckedUpdated.Checked.After(validUtxChecked.Checked)) validUtxChecked.Checked = validUtxCheckedUpdated.Checked assert.Equal(t, validUtxChecked, validUtxCheckedUpdated) // The invalid checked one and the expired one should be removed _, ok := up.Txns[invalidUtxChecked.Hash()] assert.False(t, ok) _, ok = up.Txns[validUtxExpired.Hash()] assert.False(t, ok) // Also, the unspents should have 2 * nRemaining assert.Equal(t, len(up.Unspent.Arr), 2*3) assert.Equal(t, len(up.Txns), 3) }
func NewMessages(c MessagesConfig) *Messages { return &Messages{ Config: c, Mirror: rand.New(rand.NewSource(util.Now().UnixNano())).Uint32(), } }
// Sets all txns as announced func (self *Visor) SetTxnsAnnounced(txns []coin.SHA256) { now := util.Now() for _, h := range txns { self.Visor.Unconfirmed.SetAnnounced(h, now) } }
func TestGetOldOwnedTransactions(t *testing.T) { mv := setupMasterVisor() up := mv.Unconfirmed // Setup txns notOursNew, err := makeValidTxn(mv) assert.Nil(t, err) notOursOld, err := makeValidTxn(mv) assert.Nil(t, err) ourSpendNew, err := makeValidTxn(mv) assert.Nil(t, err) ourSpendOld, err := makeValidTxn(mv) assert.Nil(t, err) ourReceiveNew, err := makeValidTxn(mv) assert.Nil(t, err) ourReceiveOld, err := makeValidTxn(mv) assert.Nil(t, err) ourBothNew, err := makeValidTxn(mv) assert.Nil(t, err) ourBothOld, err := makeValidTxn(mv) assert.Nil(t, err) // Add a transaction that is not ours, both new and old err, known := up.RecordTxn(mv.blockchain, notOursNew, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(notOursNew.Hash(), util.Now()) err, known = up.RecordTxn(mv.blockchain, notOursOld, nil, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) // Add a transaction that is our spend, both new and old addrs := make(map[coin.Address]byte, 1) ux, ok := mv.blockchain.Unspent.Get(ourSpendNew.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourSpendNew, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(ourSpendNew.Hash(), util.Now()) addrs = make(map[coin.Address]byte, 1) ux, ok = mv.blockchain.Unspent.Get(ourSpendNew.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourSpendOld, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) // Add a transaction that is our receive, both new and old addrs = make(map[coin.Address]byte, 1) addrs[ourReceiveNew.Out[1].Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourReceiveNew, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(ourReceiveNew.Hash(), util.Now()) addrs = make(map[coin.Address]byte, 1) addrs[ourReceiveOld.Out[1].Address] = byte(1) err, known = up.RecordTxn(mv.blockchain, ourReceiveOld, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) // Add a transaction that is both our spend and receive, both new and old addrs = make(map[coin.Address]byte, 2) ux, ok = mv.blockchain.Unspent.Get(ourBothNew.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) addrs[ourBothNew.Out[1].Address] = byte(1) assert.Equal(t, len(addrs), 2) err, known = up.RecordTxn(mv.blockchain, ourBothNew, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) up.SetAnnounced(ourBothNew.Hash(), util.Now()) addrs = make(map[coin.Address]byte, 1) ux, ok = mv.blockchain.Unspent.Get(ourBothOld.In[0]) assert.True(t, ok) addrs[ux.Body.Address] = byte(1) addrs[ourBothOld.Out[1].Address] = byte(1) assert.Equal(t, len(addrs), 2) err, known = up.RecordTxn(mv.blockchain, ourBothOld, addrs, testBlockSize, 0) assert.Nil(t, err) assert.False(t, known) // Get the old owned txns utxns := up.GetOldOwnedTransactions(time.Hour) // Check that the 3 txns are ones we are interested in and old enough assert.Equal(t, len(utxns), 3) mapTxns := make(map[coin.SHA256]bool) txns := make(coin.Transactions, len(utxns)) for i, utx := range utxns { txns[i] = utx.Txn assert.True(t, utx.IsOurSpend || utx.IsOurReceive) assert.True(t, utx.Announced.IsZero()) mapTxns[utx.Hash()] = true } assert.Equal(t, len(mapTxns), 3) txns = coin.SortTransactions(txns, getFee) expectTxns := coin.Transactions{ourSpendOld, ourReceiveOld, ourBothOld} expectTxns = coin.SortTransactions(expectTxns, getFee) assert.Equal(t, txns, expectTxns) }