Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add StateUpdate payload #345

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions account/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,3 +563,20 @@ func GetBookKeepers() []*crypto.PubKey {

return pubKeys
}

func GetStateUpdater() []*crypto.PubKey {
var pubKeys = []*crypto.PubKey{}
for _, key := range config.Parameters.StateUpdater {
pubKey := []byte(key)
pubKey, err := hex.DecodeString(key)
// TODO Convert the key string to byte
k, err := crypto.DecodePoint(pubKey)
if err != nil {
log.Error("Incorrectly book keepers key")
return nil
}
pubKeys = append(pubKeys, k)
}

return pubKeys
}
1 change: 1 addition & 0 deletions common/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Configuration struct {
Version int `json:"Version"`
SeedList []string `json:"SeedList"`
BookKeepers []string `json:"BookKeepers"` // The default book keepers' publickey
StateUpdater []string `json:"StateUpdater"`
HttpRestPort int `json:"HttpRestPort"`
RestCertPath string `json:"RestCertPath"`
RestKeyPath string `json:"RestKeyPath"`
Expand Down
4 changes: 2 additions & 2 deletions core/ledger/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewBlockchain(height uint32) *Blockchain {
}
}

func NewBlockchainWithGenesisBlock(defaultBookKeeper []*crypto.PubKey) (*Blockchain, error) {
func NewBlockchainWithGenesisBlock(defaultBookKeeper []*crypto.PubKey, defaultStateUpdater []*crypto.PubKey) (*Blockchain, error) {
genesisBlock, err := GenesisBlockInit()
if err != nil {
return nil, NewDetailErr(err, ErrNoCode, "[Blockchain], NewBlockchainWithGenesisBlock failed.")
Expand All @@ -32,7 +32,7 @@ func NewBlockchainWithGenesisBlock(defaultBookKeeper []*crypto.PubKey) (*Blockch
hashx := genesisBlock.Hash()
genesisBlock.hash = &hashx

height, err := DefaultLedger.Store.InitLedgerStoreWithGenesisBlock(genesisBlock, defaultBookKeeper)
height, err := DefaultLedger.Store.InitLedgerStoreWithGenesisBlock(genesisBlock, defaultBookKeeper, defaultStateUpdater)
if err != nil {
return nil, NewDetailErr(err, ErrNoCode, "[Blockchain], InitLevelDBStoreWithGenesisBlock failed.")
}
Expand Down
5 changes: 5 additions & 0 deletions core/ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

var DefaultLedger *Ledger
var StandbyBookKeepers []*crypto.PubKey
var StateUpdater []*crypto.PubKey

// Ledger - the struct for onchainDNA ledger
type Ledger struct {
Expand All @@ -26,6 +27,10 @@ func (l *Ledger) IsDoubleSpend(Tx *tx.Transaction) bool {
return DefaultLedger.Store.IsDoubleSpend(Tx)
}

func (l *Ledger) IsStateUpdaterVaild(Tx *tx.Transaction) bool {
return DefaultLedger.Store.IsStateUpdaterVaild(Tx)
}

//Get the DefaultLedger.
//Note: the later version will support the mutiLedger.So this func mybe expired later.
func GetDefaultLedger() (*Ledger, error) {
Expand Down
6 changes: 5 additions & 1 deletion core/ledger/ledgerStore.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type ILedgerStore interface {
GetHeaderHashByHeight(height uint32) Uint256

GetBookKeeperList() ([]*crypto.PubKey, []*crypto.PubKey, error)
InitLedgerStoreWithGenesisBlock(genesisblock *Block, defaultBookKeeper []*crypto.PubKey) (uint32, error)
InitLedgerStoreWithGenesisBlock(genesisblock *Block, defaultBookKeeper []*crypto.PubKey, defaultStateUpdater []*crypto.PubKey) (uint32, error)

GetQuantityIssued(assetid Uint256) (Fixed64, error)

Expand All @@ -48,4 +48,8 @@ type ILedgerStore interface {

IsTxHashDuplicate(txhash Uint256) bool
IsBlockInStore(hash Uint256) bool

GetStateUpdater() ([]*crypto.PubKey, error)
GetState(namespace []byte, key []byte) ([]byte, error)
IsStateUpdaterVaild(Tx *tx.Transaction) bool
}
131 changes: 130 additions & 1 deletion core/store/ChainStore/ChainStore.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func NewChainStore(file string) (*ChainStore, error) {
}, nil
}

func (bd *ChainStore) InitLedgerStoreWithGenesisBlock(genesisBlock *Block, defaultBookKeeper []*crypto.PubKey) (uint32, error) {
func (bd *ChainStore) InitLedgerStoreWithGenesisBlock(genesisBlock *Block, defaultBookKeeper []*crypto.PubKey, defaultStateUpdater []*crypto.PubKey) (uint32, error) {

hash := genesisBlock.Hash()
bd.headerIndex[0] = hash
Expand Down Expand Up @@ -238,6 +238,24 @@ func (bd *ChainStore) InitLedgerStoreWithGenesisBlock(genesisBlock *Block, defau
bd.st.Put(bkListKey.Bytes(), bkListValue.Bytes())
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// process defaultStateUpdater
///////////////////////////////////////////////////
// StateUpdater key
suKey := bytes.NewBuffer(nil)
suKey.WriteByte(byte(CA_StateUpdater))

// StateUpdater value
suValue := bytes.NewBuffer(nil)
serialization.WriteVarUint(suValue, uint64(len(defaultStateUpdater)))
for k := 0; k < len(defaultStateUpdater); k++ {
defaultStateUpdater[k].Serialize(suValue)
}

// StateUpdater put value
bd.st.Put(suKey.Bytes(), suValue.Bytes())
///////////////////////////////////////////////////

// persist genesis block
bd.persist(genesisBlock)

Expand Down Expand Up @@ -660,6 +678,35 @@ func (self *ChainStore) GetBookKeeperList() ([]*crypto.PubKey, []*crypto.PubKey,
return currBookKeeper, nextBookKeeper, nil
}

func (self *ChainStore) GetStateUpdater() ([]*crypto.PubKey, error) {
prefix := []byte{byte(CA_StateUpdater)}
suValue, err_get := self.st.Get(prefix)
if err_get != nil {
return nil, err_get
}

r := bytes.NewReader(suValue)

// read length
count, err := serialization.ReadVarUint(r, 0)
if err != nil {
return nil, err
}

var stateUpdater = make([]*crypto.PubKey, count)
for i := uint64(0); i < count; i++ {
bk := new(crypto.PubKey)
err := bk.DeSerialize(r)
if err != nil {
return nil, err
}

stateUpdater[i] = bk
}

return stateUpdater, nil
}

func (bd *ChainStore) persist(b *Block) error {
utxoUnspents := make(map[Uint160]map[Uint256][]*tx.UTXOUnspent)
unspents := make(map[Uint256][]uint16)
Expand Down Expand Up @@ -746,6 +793,7 @@ func (bd *ChainStore) persist(b *Block) error {
b.Transactions[i].TxType == tx.IssueAsset ||
b.Transactions[i].TxType == tx.TransferAsset ||
b.Transactions[i].TxType == tx.Record ||
b.Transactions[i].TxType == tx.StateUpdate ||
b.Transactions[i].TxType == tx.BookKeeper ||
b.Transactions[i].TxType == tx.PrivacyPayload ||
b.Transactions[i].TxType == tx.BookKeeping ||
Expand Down Expand Up @@ -774,6 +822,48 @@ func (bd *ChainStore) persist(b *Block) error {
}
}

if b.Transactions[i].TxType == tx.StateUpdate {
su := b.Transactions[i].Payload.(*payload.StateUpdate)

// stateKey
statePrefix := []byte{byte(ST_STATES)}
stateKey := append(statePrefix, su.Namespace...)
stateKey = append(stateKey, su.Key...)
//stateValueOld, err_get := bd.st.Get(stateKey)

// stateValue
//stateValue := bytes.NewBuffer(nil)
//serialization.WriteVarBytes(stateValue, su.Value)

// verify tx signer public is in StateUpdater list.
//publicKey := b.Transactions[i].Programs[0].Parameter[1:34]
log.Trace(fmt.Sprintf("StateUpdate tx publickey: %x", su.Updater))

stateUpdater, err := bd.GetStateUpdater()
if err != nil {
return err
}

findflag := false
for k := 0; k < len(stateUpdater); k++ {
log.Trace(fmt.Sprintf("StateUpdate updaterPublickey %d: %x %x", k, stateUpdater[k].X, stateUpdater[k].Y))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use Tracef, Debugf, Warnf... method instead. see #332 for more details.


if su.Updater.X.Cmp(stateUpdater[k].X) == 0 && su.Updater.Y.Cmp(stateUpdater[k].Y) == 0 {
findflag = true
break
}
}

if !findflag {
return errors.New(fmt.Sprintf("[persist] stateUpdater publickey not found in store, reject. tx publickey: %x", su.Updater))
}

// if not found in store, put value to the key.
// if found in store, rewrite value.
log.Trace(fmt.Sprintf("[persist] StateUpdate modify, key: %x, value:%x", stateKey, su.Value))
bd.st.BatchPut(stateKey, su.Value)
}

for index := 0; index < len(b.Transactions[i].Outputs); index++ {
output := b.Transactions[i].Outputs[index]
programHash := output.ProgramHash
Expand Down Expand Up @@ -1291,6 +1381,45 @@ func (bd *ChainStore) GetAccount(programHash Uint160) (*account.AccountState, er
return accountState, nil
}

func (bd *ChainStore) IsStateUpdaterVaild(Tx *tx.Transaction) bool {
su := Tx.Payload.(*payload.StateUpdate)

stateUpdater, err := bd.GetStateUpdater()
if err != nil {
return false
}

findflag := false
for k := 0; k < len(stateUpdater); k++ {
log.Trace(fmt.Sprintf("StateUpdate updaterPublickey %d: %x %x", k, stateUpdater[k].X, stateUpdater[k].Y))

if su.Updater.X.Cmp(stateUpdater[k].X) == 0 && su.Updater.Y.Cmp(stateUpdater[k].Y) == 0 {
findflag = true
break
}
}

if !findflag {
return false
}

return true
}

func (bd *ChainStore) GetState(namespace []byte, key []byte) ([]byte, error) {

// stateKey
statePrefix := []byte{byte(ST_STATES)}
stateKey := append(statePrefix, namespace...)
stateKey = append(stateKey, key...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this desgin may have problem since we can not figure out namespace and key from the stateKey.
case: namespace: dna, key: node3, and namespace:dnanode, key:3 will share the same underlying data。
recommendation: Use len(namespace) + namespace + key schema to construct the stateKey

stateValue, err := bd.st.Get(stateKey)
if err != nil {
return nil, err
}

return stateValue, nil
}

func (bd *ChainStore) IsBlockInStore(hash Uint256) bool {

var b *Block = new(Block)
Expand Down
6 changes: 6 additions & 0 deletions core/store/ChainStore/DataEntryPrefix.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ const (
ST_QuantityIssued DataEntryPrefix = 0xc1
ST_ACCOUNT DataEntryPrefix = 0xc2

// STATES
ST_STATES DataEntryPrefix = 0xd0

//SYSTEM
SYS_CurrentBlock DataEntryPrefix = 0x40
SYS_CurrentHeader DataEntryPrefix = 0x41
SYS_CurrentBookKeeper DataEntryPrefix = 0x42

// CA
CA_StateUpdater DataEntryPrefix = 0x50

//CONFIG
CFG_Version DataEntryPrefix = 0xf0
)
68 changes: 68 additions & 0 deletions core/transaction/payload/StateUpdate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package payload

import (
"DNA/common/serialization"
"DNA/crypto"
. "DNA/errors"
"errors"
"io"
)

type StateUpdate struct {
Namespace []byte
Key []byte
Value []byte
Updater *crypto.PubKey
}

func (su *StateUpdate) Data() []byte {
return []byte{0}
}

func (su *StateUpdate) Serialize(w io.Writer) error {
err := serialization.WriteVarBytes(w, su.Namespace)
if err != nil {
return NewDetailErr(err, ErrNoCode, "[StateUpdate], Namespace serialize failed.")
}

err = serialization.WriteVarBytes(w, su.Key)
if err != nil {
return NewDetailErr(err, ErrNoCode, "[StateUpdate], key serialize failed.")
}

err = serialization.WriteVarBytes(w, su.Value)
if err != nil {
return NewDetailErr(err, ErrNoCode, "[StateUpdate], value serialize failed.")
}

su.Updater.Serialize(w)

return nil
}

func (su *StateUpdate) Deserialize(r io.Reader) error {
var err error

su.Namespace, err = serialization.ReadVarBytes(r)
if err != nil {
return NewDetailErr(errors.New("[StateUpdate], Namespace deserialize failed."), ErrNoCode, "")
}

su.Key, err = serialization.ReadVarBytes(r)
if err != nil {
return NewDetailErr(errors.New("[StateUpdate], key deserialize failed."), ErrNoCode, "")
}

su.Value, err = serialization.ReadVarBytes(r)
if err != nil {
return NewDetailErr(errors.New("[StateUpdate], value deserialize failed."), ErrNoCode, "")
}

su.Updater = new(crypto.PubKey)
err = su.Updater.DeSerialize(r)
if err != nil {
return NewDetailErr(err, ErrNoCode, "[StateUpdate], updater Deserialize failed.")
}

return nil
}
15 changes: 15 additions & 0 deletions core/transaction/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
RegisterAsset TransactionType = 0x40
TransferAsset TransactionType = 0x80
Record TransactionType = 0x81
StateUpdate TransactionType = 0x90
DeployCode TransactionType = 0xd0
DataFile TransactionType = 0x12
)
Expand Down Expand Up @@ -200,6 +201,8 @@ func (tx *Transaction) DeserializeUnsignedWithoutType(r io.Reader) error {
tx.Payload = new(payload.PrivacyPayload)
case DataFile:
tx.Payload = new(payload.DataFile)
case StateUpdate:
tx.Payload = new(payload.StateUpdate)
default:
return errors.New("[Transaction],invalide transaction type.")
}
Expand Down Expand Up @@ -340,6 +343,18 @@ func (tx *Transaction) GetProgramHashes() ([]Uint160, error) {
return nil, NewDetailErr(err, ErrNoCode, "[Transaction], GetProgramHashes ToCodeHash failed.")
}
hashs = append(hashs, astHash)
case StateUpdate:
updater := tx.Payload.(*payload.StateUpdate).Updater
signatureRedeemScript, err := contract.CreateSignatureRedeemScript(updater)
if err != nil {
return nil, NewDetailErr(err, ErrNoCode, "[Transaction], StateUpdate GetProgramHashes CreateSignatureRedeemScript failed.")
}

astHash, err := ToCodeHash(signatureRedeemScript)
if err != nil {
return nil, NewDetailErr(err, ErrNoCode, "[Transaction], StateUpdate GetProgramHashes ToCodeHash failed.")
}
hashs = append(hashs, astHash)
default:
}
//remove dupilicated hashes
Expand Down
Loading