mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-29 12:41:44 +00:00
name reg
This commit is contained in:
@ -9,6 +9,7 @@ import (
|
|||||||
. "github.com/tendermint/tendermint/common"
|
. "github.com/tendermint/tendermint/common"
|
||||||
dbm "github.com/tendermint/tendermint/db"
|
dbm "github.com/tendermint/tendermint/db"
|
||||||
"github.com/tendermint/tendermint/merkle"
|
"github.com/tendermint/tendermint/merkle"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeStorage(db dbm.DB, root []byte) merkle.Tree {
|
func makeStorage(db dbm.DB, root []byte) merkle.Tree {
|
||||||
@ -27,6 +28,7 @@ type BlockCache struct {
|
|||||||
backend *State
|
backend *State
|
||||||
accounts map[string]accountInfo
|
accounts map[string]accountInfo
|
||||||
storages map[Tuple256]storageInfo
|
storages map[Tuple256]storageInfo
|
||||||
|
names map[string]nameInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockCache(backend *State) *BlockCache {
|
func NewBlockCache(backend *State) *BlockCache {
|
||||||
@ -35,6 +37,7 @@ func NewBlockCache(backend *State) *BlockCache {
|
|||||||
backend: backend,
|
backend: backend,
|
||||||
accounts: make(map[string]accountInfo),
|
accounts: make(map[string]accountInfo),
|
||||||
storages: make(map[Tuple256]storageInfo),
|
storages: make(map[Tuple256]storageInfo),
|
||||||
|
names: make(map[string]nameInfo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +126,44 @@ func (cache *BlockCache) SetStorage(addr Word256, key Word256, value Word256) {
|
|||||||
|
|
||||||
// BlockCache.storage
|
// BlockCache.storage
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
// BlockCache.names
|
||||||
|
|
||||||
|
func (cache *BlockCache) GetNameRegEntry(name []byte) *types.NameRegEntry {
|
||||||
|
entry, removed, _ := cache.names[string(name)].unpack()
|
||||||
|
if removed {
|
||||||
|
return nil
|
||||||
|
} else if entry != nil {
|
||||||
|
return entry
|
||||||
|
} else {
|
||||||
|
entry = cache.backend.GetNameRegEntry(name)
|
||||||
|
cache.names[string(name)] = nameInfo{entry, false, false}
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cache *BlockCache) UpdateNameRegEntry(entry *types.NameRegEntry) {
|
||||||
|
name := entry.Name
|
||||||
|
// SANITY CHECK
|
||||||
|
_, removed, _ := cache.names[string(name)].unpack()
|
||||||
|
if removed {
|
||||||
|
panic("UpdateNameRegEntry on a removed name")
|
||||||
|
}
|
||||||
|
// SANITY CHECK END
|
||||||
|
cache.names[string(name)] = nameInfo{entry, false, true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cache *BlockCache) RemoveNameRegEntry(name []byte) {
|
||||||
|
// SANITY CHECK
|
||||||
|
_, removed, _ := cache.names[string(name)].unpack()
|
||||||
|
if removed {
|
||||||
|
panic("RemoveNameRegEntry on a removed entry")
|
||||||
|
}
|
||||||
|
// SANITY CHECK END
|
||||||
|
cache.names[string(name)] = nameInfo{nil, true, false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockCache.names
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
// CONTRACT the updates are in deterministic order.
|
// CONTRACT the updates are in deterministic order.
|
||||||
func (cache *BlockCache) Sync() {
|
func (cache *BlockCache) Sync() {
|
||||||
@ -202,6 +243,33 @@ func (cache *BlockCache) Sync() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine order for names
|
||||||
|
// note names may be of any length less than some limit
|
||||||
|
// and are arbitrary byte arrays...
|
||||||
|
nameStrs := []string{}
|
||||||
|
for nameStr := range cache.names {
|
||||||
|
nameStrs = append(nameStrs, nameStr)
|
||||||
|
}
|
||||||
|
sort.Strings(nameStrs)
|
||||||
|
|
||||||
|
// Update or delete names.
|
||||||
|
for _, nameStr := range nameStrs {
|
||||||
|
entry, removed, dirty := cache.names[nameStr].unpack()
|
||||||
|
if removed {
|
||||||
|
removed := cache.backend.RemoveNameRegEntry(entry.Name)
|
||||||
|
if !removed {
|
||||||
|
panic(Fmt("Could not remove namereg entry to be removed: %X", entry.Name))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if entry == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dirty {
|
||||||
|
cache.backend.UpdateNameRegEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -225,3 +293,13 @@ type storageInfo struct {
|
|||||||
func (stjInfo storageInfo) unpack() (Word256, bool) {
|
func (stjInfo storageInfo) unpack() (Word256, bool) {
|
||||||
return stjInfo.value, stjInfo.dirty
|
return stjInfo.value, stjInfo.dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type nameInfo struct {
|
||||||
|
name *types.NameRegEntry
|
||||||
|
removed bool
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nInfo nameInfo) unpack() (*types.NameRegEntry, bool, bool) {
|
||||||
|
return nInfo.name, nInfo.removed, nInfo.dirty
|
||||||
|
}
|
||||||
|
@ -296,7 +296,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
|||||||
|
|
||||||
// TODO: do something with fees
|
// TODO: do something with fees
|
||||||
fees := uint64(0)
|
fees := uint64(0)
|
||||||
_s := blockCache.State() // hack to access validators and event switch.
|
_s := blockCache.State() // hack to access validators and block height
|
||||||
|
|
||||||
// Exec tx
|
// Exec tx
|
||||||
switch tx := tx_.(type) {
|
switch tx := tx_.(type) {
|
||||||
@ -478,6 +478,102 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case *types.NameTx:
|
||||||
|
var inAcc *account.Account
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
inAcc = blockCache.GetAccount(tx.Input.Address)
|
||||||
|
if inAcc == nil {
|
||||||
|
log.Debug(Fmt("Can't find in account %X", tx.Input.Address))
|
||||||
|
return types.ErrTxInvalidAddress
|
||||||
|
}
|
||||||
|
// pubKey should be present in either "inAcc" or "tx.Input"
|
||||||
|
if err := checkInputPubKey(inAcc, tx.Input); err != nil {
|
||||||
|
log.Debug(Fmt("Can't find pubkey for %X", tx.Input.Address))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signBytes := account.SignBytes(tx)
|
||||||
|
err := validateInput(inAcc, signBytes, tx.Input)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(Fmt("validateInput failed on %X:", tx.Input.Address))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// fee is in addition to the amount which is used to determine the TTL
|
||||||
|
if tx.Input.Amount < tx.Fee {
|
||||||
|
log.Debug(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
|
||||||
|
return types.ErrTxInsufficientFunds
|
||||||
|
}
|
||||||
|
|
||||||
|
value := tx.Input.Amount - tx.Fee
|
||||||
|
|
||||||
|
// let's say cost of a name for one block is len(data) + 32
|
||||||
|
// TODO: the casting is dangerous and things can overflow (below)!
|
||||||
|
// we should make LastBlockHeight a uint64
|
||||||
|
costPerBlock := uint(len(tx.Data) + 32)
|
||||||
|
expiresIn := uint(value / uint64(costPerBlock))
|
||||||
|
|
||||||
|
// check if the name exists
|
||||||
|
entry := blockCache.GetNameRegEntry(tx.Name)
|
||||||
|
|
||||||
|
if entry != nil {
|
||||||
|
var expired bool
|
||||||
|
// if the entry already exists, and hasn't expired, we must be owner
|
||||||
|
if entry.Expires > _s.LastBlockHeight {
|
||||||
|
// ensure we are owner
|
||||||
|
if bytes.Compare(entry.Owner, tx.Input.Address) != 0 {
|
||||||
|
log.Debug(Fmt("Sender %X is trying to update a name (%s) for which he is not owner", tx.Input.Address, tx.Name))
|
||||||
|
return types.ErrTxInvalidAddress // (?)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expired = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// no value and empty data means delete the entry
|
||||||
|
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)
|
||||||
|
blockCache.RemoveNameRegEntry(entry.Name)
|
||||||
|
} else {
|
||||||
|
// update the entry by bumping the expiry
|
||||||
|
// and changing the data
|
||||||
|
if expired {
|
||||||
|
entry.Expires = _s.LastBlockHeight + expiresIn
|
||||||
|
} else {
|
||||||
|
// since the size of the data may have changed
|
||||||
|
// we use the total amount of "credit"
|
||||||
|
credit := uint64((entry.Expires - _s.LastBlockHeight) * uint(len(entry.Data)))
|
||||||
|
credit += value
|
||||||
|
expiresIn = uint(credit) / costPerBlock
|
||||||
|
entry.Expires = _s.LastBlockHeight + expiresIn
|
||||||
|
}
|
||||||
|
entry.Data = tx.Data
|
||||||
|
blockCache.UpdateNameRegEntry(entry)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if expiresIn < 5 {
|
||||||
|
return fmt.Errorf("Names must be registered for at least 5 blocks")
|
||||||
|
}
|
||||||
|
// entry does not exist, so create it
|
||||||
|
entry = &types.NameRegEntry{
|
||||||
|
Name: tx.Name,
|
||||||
|
Owner: tx.Input.Address,
|
||||||
|
Data: tx.Data,
|
||||||
|
Expires: _s.LastBlockHeight + expiresIn,
|
||||||
|
}
|
||||||
|
blockCache.UpdateNameRegEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: something with the value sent?
|
||||||
|
|
||||||
|
// Good!
|
||||||
|
inAcc.Sequence += 1
|
||||||
|
inAcc.Balance -= value
|
||||||
|
blockCache.UpdateAccount(inAcc)
|
||||||
|
|
||||||
|
// TODO: maybe we want to take funds on error and allow txs in that don't do anythingi?
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
case *types.BondTx:
|
case *types.BondTx:
|
||||||
valInfo := blockCache.State().GetValidatorInfo(tx.PubKey.Address())
|
valInfo := blockCache.State().GetValidatorInfo(tx.PubKey.Address())
|
||||||
if valInfo != nil {
|
if valInfo != nil {
|
||||||
|
@ -100,9 +100,14 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make namereg tree
|
||||||
|
nameReg := merkle.NewIAVLTree(binary.BasicCodec, NameRegCodec, 0, db)
|
||||||
|
// TODO: add names to genesis.json
|
||||||
|
|
||||||
// IAVLTrees must be persisted before copy operations.
|
// IAVLTrees must be persisted before copy operations.
|
||||||
accounts.Save()
|
accounts.Save()
|
||||||
validatorInfos.Save()
|
validatorInfos.Save()
|
||||||
|
nameReg.Save()
|
||||||
|
|
||||||
return &State{
|
return &State{
|
||||||
DB: db,
|
DB: db,
|
||||||
@ -116,5 +121,6 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
|
|||||||
UnbondingValidators: NewValidatorSet(nil),
|
UnbondingValidators: NewValidatorSet(nil),
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
validatorInfos: validatorInfos,
|
validatorInfos: validatorInfos,
|
||||||
|
nameReg: nameReg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package state
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/account"
|
"github.com/tendermint/tendermint/account"
|
||||||
@ -36,6 +37,7 @@ type State struct {
|
|||||||
UnbondingValidators *ValidatorSet
|
UnbondingValidators *ValidatorSet
|
||||||
accounts merkle.Tree // Shouldn't be accessed directly.
|
accounts merkle.Tree // Shouldn't be accessed directly.
|
||||||
validatorInfos merkle.Tree // Shouldn't be accessed directly.
|
validatorInfos merkle.Tree // Shouldn't be accessed directly.
|
||||||
|
nameReg merkle.Tree // Shouldn't be accessed directly.
|
||||||
|
|
||||||
evc events.Fireable // typically an events.EventCache
|
evc events.Fireable // typically an events.EventCache
|
||||||
}
|
}
|
||||||
@ -61,6 +63,9 @@ func LoadState(db dbm.DB) *State {
|
|||||||
validatorInfosHash := binary.ReadByteSlice(r, n, err)
|
validatorInfosHash := binary.ReadByteSlice(r, n, err)
|
||||||
s.validatorInfos = merkle.NewIAVLTree(binary.BasicCodec, ValidatorInfoCodec, 0, db)
|
s.validatorInfos = merkle.NewIAVLTree(binary.BasicCodec, ValidatorInfoCodec, 0, db)
|
||||||
s.validatorInfos.Load(validatorInfosHash)
|
s.validatorInfos.Load(validatorInfosHash)
|
||||||
|
nameRegHash := binary.ReadByteSlice(r, n, err)
|
||||||
|
s.nameReg = merkle.NewIAVLTree(binary.BasicCodec, NameRegCodec, 0, db)
|
||||||
|
s.nameReg.Load(nameRegHash)
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
panic(*err)
|
panic(*err)
|
||||||
}
|
}
|
||||||
@ -72,6 +77,7 @@ func LoadState(db dbm.DB) *State {
|
|||||||
func (s *State) Save() {
|
func (s *State) Save() {
|
||||||
s.accounts.Save()
|
s.accounts.Save()
|
||||||
s.validatorInfos.Save()
|
s.validatorInfos.Save()
|
||||||
|
s.nameReg.Save()
|
||||||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||||
binary.WriteString(s.ChainID, buf, n, err)
|
binary.WriteString(s.ChainID, buf, n, err)
|
||||||
binary.WriteUvarint(s.LastBlockHeight, buf, n, err)
|
binary.WriteUvarint(s.LastBlockHeight, buf, n, err)
|
||||||
@ -83,6 +89,7 @@ func (s *State) Save() {
|
|||||||
binary.WriteBinary(s.UnbondingValidators, buf, n, err)
|
binary.WriteBinary(s.UnbondingValidators, buf, n, err)
|
||||||
binary.WriteByteSlice(s.accounts.Hash(), buf, n, err)
|
binary.WriteByteSlice(s.accounts.Hash(), buf, n, err)
|
||||||
binary.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
|
binary.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
|
||||||
|
binary.WriteByteSlice(s.nameReg.Hash(), buf, n, err)
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
panic(*err)
|
panic(*err)
|
||||||
}
|
}
|
||||||
@ -105,6 +112,7 @@ func (s *State) Copy() *State {
|
|||||||
UnbondingValidators: s.UnbondingValidators.Copy(), // copy the valSet lazily.
|
UnbondingValidators: s.UnbondingValidators.Copy(), // copy the valSet lazily.
|
||||||
accounts: s.accounts.Copy(),
|
accounts: s.accounts.Copy(),
|
||||||
validatorInfos: s.validatorInfos.Copy(),
|
validatorInfos: s.validatorInfos.Copy(),
|
||||||
|
nameReg: s.nameReg.Copy(),
|
||||||
evc: nil,
|
evc: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,6 +124,7 @@ func (s *State) Hash() []byte {
|
|||||||
s.UnbondingValidators,
|
s.UnbondingValidators,
|
||||||
s.accounts,
|
s.accounts,
|
||||||
s.validatorInfos,
|
s.validatorInfos,
|
||||||
|
s.nameReg,
|
||||||
}
|
}
|
||||||
return merkle.HashFromHashables(hashables)
|
return merkle.HashFromHashables(hashables)
|
||||||
}
|
}
|
||||||
@ -272,6 +281,46 @@ func (s *State) LoadStorage(hash []byte) (storage merkle.Tree) {
|
|||||||
|
|
||||||
// State.storage
|
// State.storage
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
// State.nameReg
|
||||||
|
|
||||||
|
func (s *State) GetNameRegEntry(name []byte) *types.NameRegEntry {
|
||||||
|
_, value := s.nameReg.Get(name)
|
||||||
|
if value == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
entry := value.(*types.NameRegEntry)
|
||||||
|
// XXX: do we need to copy?
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) UpdateNameRegEntry(entry *types.NameRegEntry) bool {
|
||||||
|
return s.nameReg.Set(entry.Name, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) RemoveNameRegEntry(name []byte) bool {
|
||||||
|
_, removed := s.nameReg.Remove(name)
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) GetNames() merkle.Tree {
|
||||||
|
return s.nameReg.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NameRegEncoder(o interface{}, w io.Writer, n *int64, err *error) {
|
||||||
|
binary.WriteBinary(o.(*types.NameRegEntry), w, n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NameRegDecoder(r io.Reader, n *int64, err *error) interface{} {
|
||||||
|
return binary.ReadBinary(&types.NameRegEntry{}, r, n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var NameRegCodec = binary.Codec{
|
||||||
|
Encode: NameRegEncoder,
|
||||||
|
Decode: NameRegDecoder,
|
||||||
|
}
|
||||||
|
|
||||||
|
// State.nameReg
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
// Implements events.Eventable. Typically uses events.EventCache
|
// Implements events.Eventable. Typically uses events.EventCache
|
||||||
func (s *State) SetFireable(evc events.Fireable) {
|
func (s *State) SetFireable(evc events.Fireable) {
|
||||||
|
@ -268,6 +268,91 @@ func TestTxs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CallTx.
|
||||||
|
{
|
||||||
|
state := state.Copy()
|
||||||
|
newAcc1 := state.GetAccount(acc1.Address)
|
||||||
|
newAcc1.Code = []byte{0x60}
|
||||||
|
state.UpdateAccount(newAcc1)
|
||||||
|
tx := &types.CallTx{
|
||||||
|
Input: &types.TxInput{
|
||||||
|
Address: acc0.Address,
|
||||||
|
Amount: 1,
|
||||||
|
Sequence: acc0.Sequence + 1,
|
||||||
|
PubKey: acc0PubKey,
|
||||||
|
},
|
||||||
|
Address: acc1.Address,
|
||||||
|
GasLimit: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Input.Signature = privAccounts[0].Sign(tx)
|
||||||
|
err := execTxWithState(state, tx, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got error in executing call transaction, %v", err)
|
||||||
|
}
|
||||||
|
newAcc0 := state.GetAccount(acc0.Address)
|
||||||
|
if acc0.Balance-1 != newAcc0.Balance {
|
||||||
|
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
|
||||||
|
acc0.Balance-1, newAcc0.Balance)
|
||||||
|
}
|
||||||
|
newAcc1 = state.GetAccount(acc1.Address)
|
||||||
|
if acc1.Balance+1 != newAcc1.Balance {
|
||||||
|
t.Errorf("Unexpected newAcc1 balance. Expected %v, got %v",
|
||||||
|
acc1.Balance+1, newAcc1.Balance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameTx.
|
||||||
|
{
|
||||||
|
entryName := []byte("satoshi")
|
||||||
|
entryData := []byte(`
|
||||||
|
A purely peer-to-peer version of electronic cash would allow online
|
||||||
|
payments to be sent directly from one party to another without going through a
|
||||||
|
financial institution. Digital signatures provide part of the solution, but the main
|
||||||
|
benefits are lost if a trusted third party is still required to prevent double-spending.
|
||||||
|
We propose a solution to the double-spending problem using a peer-to-peer network.
|
||||||
|
The network timestamps transactions by hashing them into an ongoing chain of
|
||||||
|
hash-based proof-of-work, forming a record that cannot be changed without redoing
|
||||||
|
the proof-of-work. The longest chain not only serves as proof of the sequence of
|
||||||
|
events witnessed, but proof that it came from the largest pool of CPU power. As
|
||||||
|
long as a majority of CPU power is controlled by nodes that are not cooperating to
|
||||||
|
attack the network, they'll generate the longest chain and outpace attackers. The
|
||||||
|
network itself requires minimal structure. Messages are broadcast on a best effort
|
||||||
|
basis, and nodes can leave and rejoin the network at will, accepting the longest
|
||||||
|
proof-of-work chain as proof of what happened while they were gone `)
|
||||||
|
entryAmount := uint64(10000)
|
||||||
|
|
||||||
|
state := state.Copy()
|
||||||
|
tx := &types.NameTx{
|
||||||
|
Input: &types.TxInput{
|
||||||
|
Address: acc0.Address,
|
||||||
|
Amount: entryAmount,
|
||||||
|
Sequence: acc0.Sequence + 1,
|
||||||
|
PubKey: acc0PubKey,
|
||||||
|
},
|
||||||
|
Name: entryName,
|
||||||
|
Data: entryData,
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Input.Signature = privAccounts[0].Sign(tx)
|
||||||
|
err := execTxWithState(state, tx, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got error in executing call transaction, %v", err)
|
||||||
|
}
|
||||||
|
newAcc0 := state.GetAccount(acc0.Address)
|
||||||
|
if acc0.Balance-entryAmount != newAcc0.Balance {
|
||||||
|
t.Errorf("Unexpected newAcc0 balance. Expected %v, got %v",
|
||||||
|
acc0.Balance-entryAmount, newAcc0.Balance)
|
||||||
|
}
|
||||||
|
entry := state.GetNameRegEntry(entryName)
|
||||||
|
if entry == nil {
|
||||||
|
t.Errorf("Expected an entry but got nil")
|
||||||
|
}
|
||||||
|
if bytes.Compare(entry.Data, entryData) != 0 {
|
||||||
|
t.Errorf("Wrong data stored")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BondTx.
|
// BondTx.
|
||||||
{
|
{
|
||||||
state := state.Copy()
|
state := state.Copy()
|
||||||
|
8
types/names.go
Normal file
8
types/names.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type NameRegEntry struct {
|
||||||
|
Name []byte // registered name for the entry
|
||||||
|
Owner []byte // address that created the entry
|
||||||
|
Data []byte // binary encoded byte array
|
||||||
|
Expires uint // block at which this entry expires
|
||||||
|
}
|
25
types/tx.go
25
types/tx.go
@ -35,6 +35,7 @@ Tx (Transaction) is an atomic operation on the ledger state.
|
|||||||
Account Txs:
|
Account Txs:
|
||||||
- SendTx Send coins to address
|
- SendTx Send coins to address
|
||||||
- CallTx Send a msg to a contract that runs in the vm
|
- CallTx Send a msg to a contract that runs in the vm
|
||||||
|
- NameTx Store some value under a name in the global namereg
|
||||||
|
|
||||||
Validation Txs:
|
Validation Txs:
|
||||||
- BondTx New validator posts a bond
|
- BondTx New validator posts a bond
|
||||||
@ -50,6 +51,7 @@ const (
|
|||||||
// Account transactions
|
// Account transactions
|
||||||
TxTypeSend = byte(0x01)
|
TxTypeSend = byte(0x01)
|
||||||
TxTypeCall = byte(0x02)
|
TxTypeCall = byte(0x02)
|
||||||
|
TxTypeName = byte(0x03)
|
||||||
|
|
||||||
// Validation transactions
|
// Validation transactions
|
||||||
TxTypeBond = byte(0x11)
|
TxTypeBond = byte(0x11)
|
||||||
@ -63,6 +65,7 @@ var _ = binary.RegisterInterface(
|
|||||||
struct{ Tx }{},
|
struct{ Tx }{},
|
||||||
binary.ConcreteType{&SendTx{}, TxTypeSend},
|
binary.ConcreteType{&SendTx{}, TxTypeSend},
|
||||||
binary.ConcreteType{&CallTx{}, TxTypeCall},
|
binary.ConcreteType{&CallTx{}, TxTypeCall},
|
||||||
|
binary.ConcreteType{&NameTx{}, TxTypeName},
|
||||||
binary.ConcreteType{&BondTx{}, TxTypeBond},
|
binary.ConcreteType{&BondTx{}, TxTypeBond},
|
||||||
binary.ConcreteType{&UnbondTx{}, TxTypeUnbond},
|
binary.ConcreteType{&UnbondTx{}, TxTypeUnbond},
|
||||||
binary.ConcreteType{&RebondTx{}, TxTypeRebond},
|
binary.ConcreteType{&RebondTx{}, TxTypeRebond},
|
||||||
@ -178,6 +181,28 @@ func (tx *CallTx) String() string {
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type NameTx struct {
|
||||||
|
Input *TxInput `json:"input"`
|
||||||
|
Name []byte `json:"name"`
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
Fee uint64 `json:"fee"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *NameTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
|
||||||
|
// We hex encode the network name so we don't deal with escaping issues.
|
||||||
|
binary.WriteTo([]byte(Fmt(`{"network":"%X"`, config.GetString("network"))), w, n, err)
|
||||||
|
binary.WriteTo([]byte(Fmt(`,"tx":[%v,{"name":"%s","data":"%s"`, TxTypeName, tx.Name, tx.Data)), w, n, err)
|
||||||
|
binary.WriteTo([]byte(Fmt(`,"fee":%v,"input":`, tx.Fee)), w, n, err)
|
||||||
|
tx.Input.WriteSignBytes(w, n, err)
|
||||||
|
binary.WriteTo([]byte(`}]}`), w, n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *NameTx) String() string {
|
||||||
|
return Fmt("NameTx{%v -> %s: %s}", tx.Input, tx.Name, tx.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
type BondTx struct {
|
type BondTx struct {
|
||||||
PubKey account.PubKeyEd25519 `json:"pub_key"`
|
PubKey account.PubKeyEd25519 `json:"pub_key"`
|
||||||
Signature account.SignatureEd25519 `json:"signature"`
|
Signature account.SignatureEd25519 `json:"signature"`
|
||||||
|
Reference in New Issue
Block a user