From 444c8c58ec5f300edfbdfb8e63706bf1366f2ee5 Mon Sep 17 00:00:00 2001 From: zhengq1 Date: Mon, 10 Jul 2017 15:21:09 +0800 Subject: [PATCH] Add StateUpdate payload support store a value use Namespace and key as index. Signed-off-by: zhengq1 --- account/client.go | 17 +++ common/config/config.go | 1 + core/ledger/blockchain.go | 4 +- core/ledger/ledger.go | 5 + core/ledger/ledgerStore.go | 6 +- core/store/ChainStore/ChainStore.go | 128 ++++++++++++++++++++++- core/store/ChainStore/DataEntryPrefix.go | 6 ++ core/transaction/payload/StateUpdate.go | 68 ++++++++++++ core/transaction/transaction.go | 15 +++ core/validation/txValidator.go | 11 ++ main.go | 3 +- 11 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 core/transaction/payload/StateUpdate.go diff --git a/account/client.go b/account/client.go index 28affc1a..ba90ca5f 100644 --- a/account/client.go +++ b/account/client.go @@ -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 +} diff --git a/common/config/config.go b/common/config/config.go index c3ed1c56..c1a7eb7b 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -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"` diff --git a/core/ledger/blockchain.go b/core/ledger/blockchain.go index 3a276398..03e1afb7 100644 --- a/core/ledger/blockchain.go +++ b/core/ledger/blockchain.go @@ -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.") @@ -32,7 +32,7 @@ func NewBlockchainWithGenesisBlock( defaultBookKeeper []*crypto.PubKey ) (*Block 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.") } diff --git a/core/ledger/ledger.go b/core/ledger/ledger.go index b9397653..69c38db2 100644 --- a/core/ledger/ledger.go +++ b/core/ledger/ledger.go @@ -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 { @@ -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) { diff --git a/core/ledger/ledgerStore.go b/core/ledger/ledgerStore.go index e4457632..d861a522 100644 --- a/core/ledger/ledgerStore.go +++ b/core/ledger/ledgerStore.go @@ -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) @@ -45,4 +45,8 @@ type ILedgerStore interface { GetUnspentFromProgramHash(programHash Uint160, assetid Uint256) ([]*tx.UTXOUnspent, error) IsTxHashDuplicate(txhash Uint256) bool + + GetStateUpdater() ([]*crypto.PubKey, error) + GetState(namespace []byte, key []byte) ([]byte, error) + IsStateUpdaterVaild(Tx *tx.Transaction) bool } diff --git a/core/store/ChainStore/ChainStore.go b/core/store/ChainStore/ChainStore.go index 67344bdb..8bdeac8f 100644 --- a/core/store/ChainStore/ChainStore.go +++ b/core/store/ChainStore/ChainStore.go @@ -76,7 +76,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 @@ -230,6 +230,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) @@ -652,6 +670,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) @@ -738,6 +785,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 || @@ -766,6 +814,43 @@ func (bd *ChainStore) persist(b *Block) error { } } + if b.Transactions[i].TxType == tx.StateUpdate { + su := b.Transactions[i].Payload.(*payload.StateUpdate) + + // stateKey + stateKey := bytes.NewBuffer(nil) + stateKey.WriteByte(byte(ST_STATES)) + serialization.WriteVarBytes(stateKey, su.Namespace) + serialization.WriteVarBytes(stateKey, su.Key) + + // verify tx signer public is in StateUpdater list. + log.Tracef("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.Tracef("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 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.Tracef("[persist] StateUpdate modify, key: %x, value:%x", stateKey, su.Value) + bd.st.BatchPut(stateKey.Bytes(), su.Value) + } + for index := 0; index < len(b.Transactions[i].Outputs); index++ { output := b.Transactions[i].Outputs[index] programHash := output.ProgramHash @@ -1279,6 +1364,47 @@ 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.Tracef("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 + stateKey := bytes.NewBuffer(nil) + stateKey.WriteByte(byte(ST_STATES)) + serialization.WriteVarBytes(stateKey, namespace) + serialization.WriteVarBytes(stateKey, key) + + stateValue, err := bd.st.Get(stateKey.Bytes()) + if err != nil { + return nil, err + } + + return stateValue, nil +} + func (bd *ChainStore) GetUnspentFromProgramHash(programHash Uint160, assetid Uint256) ([]*tx.UTXOUnspent, error) { prefix := []byte{byte(IX_Unspent_UTXO)} diff --git a/core/store/ChainStore/DataEntryPrefix.go b/core/store/ChainStore/DataEntryPrefix.go index 0edf30ea..c77f42bc 100644 --- a/core/store/ChainStore/DataEntryPrefix.go +++ b/core/store/ChainStore/DataEntryPrefix.go @@ -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 ) diff --git a/core/transaction/payload/StateUpdate.go b/core/transaction/payload/StateUpdate.go new file mode 100644 index 00000000..29257f53 --- /dev/null +++ b/core/transaction/payload/StateUpdate.go @@ -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 +} diff --git a/core/transaction/transaction.go b/core/transaction/transaction.go index b14adf80..4346ff94 100644 --- a/core/transaction/transaction.go +++ b/core/transaction/transaction.go @@ -28,6 +28,7 @@ const ( RegisterAsset TransactionType = 0x40 TransferAsset TransactionType = 0x80 Record TransactionType = 0x81 + StateUpdate TransactionType = 0x90 DeployCode TransactionType = 0xd0 DataFile TransactionType = 0x12 ) @@ -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.") } @@ -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 diff --git a/core/validation/txValidator.go b/core/validation/txValidator.go index 02e8a4b0..c81ad154 100644 --- a/core/validation/txValidator.go +++ b/core/validation/txValidator.go @@ -125,6 +125,12 @@ func VerifyTransactionWithLedger(Tx *tx.Transaction, ledger *ledger.Ledger) erro if exist := ledger.Store.IsTxHashDuplicate(Tx.Hash()); exist { return errors.New("[VerifyTransactionWithLedger] duplicate transaction check faild.") } + if Tx.TxType == tx.StateUpdate { + if !IsStateUpdaterVaild(Tx, ledger) { + return errors.New("[IsStateUpdaterVaild] faild.") + } + } + return nil } @@ -162,6 +168,10 @@ func IsDoubleSpend(tx *tx.Transaction, ledger *ledger.Ledger) bool { return ledger.IsDoubleSpend(tx) } +func IsStateUpdaterVaild(tx *tx.Transaction, ledger *ledger.Ledger) bool { + return ledger.IsStateUpdaterVaild(tx) +} + func CheckAssetPrecision(Tx *tx.Transaction) error { if len(Tx.Outputs) == 0 { return nil @@ -250,6 +260,7 @@ func CheckTransactionPayload(Tx *tx.Transaction) error { case *payload.Record: case *payload.DeployCode: case *payload.DataFile: + case *payload.StateUpdate: default: return errors.New("[txValidator],invalidate transaction payload type.") } diff --git a/main.go b/main.go index 8d36afed..d385ef05 100644 --- a/main.go +++ b/main.go @@ -61,9 +61,10 @@ func main() { } log.Debug("The Node's PublicKey ", acct.PublicKey) ledger.StandbyBookKeepers = account.GetBookKeepers() + ledger.StateUpdater = account.GetStateUpdater() log.Info("3. BlockChain init") - blockChain, err = ledger.NewBlockchainWithGenesisBlock(ledger.StandbyBookKeepers) + blockChain, err = ledger.NewBlockchainWithGenesisBlock(ledger.StandbyBookKeepers, ledger.StateUpdater) if err != nil { log.Error(err, " BlockChain generate failed") goto ERROR