namereg cleanup, tests

This commit is contained in:
Ethan Buchman
2015-05-24 14:41:42 -04:00
parent 02aedaaefb
commit 77ff09e173
5 changed files with 242 additions and 41 deletions

View File

@ -505,18 +505,20 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
}
// validate the input strings
if !tx.Validate() {
log.Debug(Fmt("Invalid characters present in NameTx name (%s) or data (%s)", tx.Name, tx.Data))
if err := tx.ValidateStrings(); err != nil {
log.Debug(err.Error())
return types.ErrTxInvalidString
}
value := tx.Input.Amount - tx.Fee
// let's say cost of a name for one block is len(data) + 32
costPerBlock := uint64(len(tx.Data) + 32)
costPerBlock := types.NameCostPerBlock * types.NameCostPerByte * tx.BaseEntryCost()
expiresIn := value / uint64(costPerBlock)
lastBlockHeight := uint64(_s.LastBlockHeight)
log.Debug("New NameTx", "value", value, "costPerBlock", costPerBlock, "expiresIn", expiresIn, "lastBlock", lastBlockHeight)
// check if the name exists
entry := blockCache.GetNameRegEntry(tx.Name)
@ -537,26 +539,36 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
if value == 0 && len(tx.Data) == 0 {
// maybe we reward you for telling us we can delete this crap
// (owners if not expired, anyone if expired)
log.Debug("Removing namereg entry", "name", entry.Name)
blockCache.RemoveNameRegEntry(entry.Name)
} else {
// update the entry by bumping the expiry
// and changing the data
if expired {
if expiresIn < types.MinNameRegistrationPeriod {
return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)
}
entry.Expires = lastBlockHeight + expiresIn
entry.Owner = tx.Input.Address
log.Debug("An old namereg entry has expired and been reclaimed", "name", entry.Name, "expiresIn", expiresIn, "owner", entry.Owner)
} else {
// since the size of the data may have changed
// we use the total amount of "credit"
credit := (entry.Expires - lastBlockHeight) * uint64(len(entry.Data))
credit += value
oldCredit := (entry.Expires - lastBlockHeight) * types.BaseEntryCost(entry.Name, entry.Data)
credit := oldCredit + value
expiresIn = credit / costPerBlock
if expiresIn < types.MinNameRegistrationPeriod {
return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)
}
entry.Expires = lastBlockHeight + expiresIn
log.Debug("Updated namereg entry", "name", entry.Name, "expiresIn", expiresIn, "oldCredit", oldCredit, "value", value, "credit", credit)
}
entry.Data = tx.Data
blockCache.UpdateNameRegEntry(entry)
}
} else {
if expiresIn < 5 {
return fmt.Errorf("Names must be registered for at least 5 blocks")
if expiresIn < types.MinNameRegistrationPeriod {
return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)
}
// entry does not exist, so create it
entry = &types.NameRegEntry{
@ -565,6 +577,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
Data: tx.Data,
Expires: lastBlockHeight + expiresIn,
}
log.Debug("Creating namereg entry", "name", entry.Name, "expiresIn", expiresIn)
blockCache.UpdateNameRegEntry(entry)
}

View File

@ -21,6 +21,15 @@ func execTxWithState(state *State, tx types.Tx, runCall bool) error {
}
}
func execTxWithStateNewBlock(state *State, tx types.Tx, runCall bool) error {
if err := execTxWithState(state, tx, runCall); err != nil {
return err
}
state.LastBlockHeight += 1
return nil
}
func TestCopyState(t *testing.T) {
// Generate a random state
s0, privAccounts, _ := RandGenesisState(10, true, 1000, 5, true, 1000)
@ -166,26 +175,6 @@ func TestTxSequence(t *testing.T) {
acc0PubKey := privAccounts[0].PubKey
acc1 := state.GetAccount(privAccounts[1].PubKey.Address())
// Try executing a SendTx with various sequence numbers.
makeSendTx := func(sequence uint) *types.SendTx {
return &types.SendTx{
Inputs: []*types.TxInput{
&types.TxInput{
Address: acc0.Address,
Amount: 1,
Sequence: sequence,
PubKey: acc0PubKey,
},
},
Outputs: []*types.TxOutput{
&types.TxOutput{
Address: acc1.Address,
Amount: 1,
},
},
}
}
// Test a variety of sequence numbers for the tx.
// The tx should only pass when i == 1.
for i := -1; i < 3; i++ {
@ -220,6 +209,130 @@ func TestTxSequence(t *testing.T) {
}
}
func TestNameTxs(t *testing.T) {
state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000)
types.MinNameRegistrationPeriod = 5
startingBlock := uint64(state.LastBlockHeight)
// try some bad names. these should all fail
names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}), "baffledbythespectacleinallofthisyouseeehesaidwithouteyes", "no spaces please"}
data := "something about all this just doesn't feel right."
fee := uint64(1000)
numDesiredBlocks := uint64(5)
for _, name := range names {
amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[0])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatalf("Expected invalid name error from %s", name)
}
}
// try some bad data. these should all fail
name := "hold_it_chum"
datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"}
for _, data := range datas {
amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[0])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatalf("Expected invalid data error from %s", data)
}
}
validateEntry := func(t *testing.T, entry *types.NameRegEntry, name, data string, addr []byte, expires uint64) {
if entry == nil {
t.Fatalf("Could not find name %s", name)
}
if bytes.Compare(entry.Owner, addr) != 0 {
t.Fatalf("Wrong owner. Got %X expected %X", entry.Owner, addr)
}
if data != entry.Data {
t.Fatalf("Wrong data. Got %s expected %s", entry.Data, data)
}
if name != entry.Name {
t.Fatalf("Wrong name. Got %s expected %s", entry.Name, name)
}
if expires != entry.Expires {
t.Fatalf("Wrong expiry. Got %d, expected %d", entry.Expires, expires)
}
}
// try a good one, check data, owner, expiry
name = "looking_good/karaoke_bar"
data = "on this side of neptune there are 1234567890 people: first is OMNIVORE. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')"
amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data)
tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[0])
if err := execTxWithState(state, tx, true); err != nil {
t.Fatal(err)
}
entry := state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks)
// fail to update it as non-owner, in same block
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[1])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatal("Expected error")
}
// update it as owner, just to increase expiry, in same block
// NOTE: we have to resend the data or it will clear it (is this what we want?)
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[0])
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*2)
// update it as owner, just to increase expiry, in next block
tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[0])
if err := execTxWithStateNewBlock(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*3)
// fail to update it as non-owner
state.LastBlockHeight = uint(entry.Expires - 1)
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[1])
if err := execTxWithState(state, tx, true); err == nil {
t.Fatal("Expected error")
}
// once expires, non-owner succeeds
state.LastBlockHeight = uint(entry.Expires)
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[1])
if err := execTxWithState(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[1].Address, uint64(state.LastBlockHeight)+numDesiredBlocks)
// update it as new owner, with new data (longer), but keep the expiry!
data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that."
oldCredit := amt - fee
numDesiredBlocks = 10
amt = fee + (numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) - oldCredit)
tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee)
tx.Sign(privAccounts[1])
if err := execTxWithState(state, tx, true); err != nil {
t.Fatal(err)
}
entry = state.GetNameRegEntry(name)
validateEntry(t, entry, name, data, privAccounts[1].Address, uint64(state.LastBlockHeight)+numDesiredBlocks)
}
// TODO: test overflows.
// TODO: test for unbonding validators.
func TestTxs(t *testing.T) {
@ -268,7 +381,7 @@ func TestTxs(t *testing.T) {
}
}
// CallTx.
// CallTx. Just runs through it and checks the transfer. See vm, rpc tests for more
{
state := state.Copy()
newAcc1 := state.GetAccount(acc1.Address)