m.Handle("/delete-access-token", jsonHandler(a.deleteAccessToken))
m.Handle("/check-access-token", jsonHandler(a.checkAccessToken))
+ m.Handle("/create-contract", jsonHandler(a.createContract))
+ m.Handle("/update-contract-alias", jsonHandler(a.updateContractAlias))
+ m.Handle("/get-contract", jsonHandler(a.getContract))
+ m.Handle("/list-contracts", jsonHandler(a.listContracts))
+
m.Handle("/create-transaction-feed", jsonHandler(a.createTxFeed))
m.Handle("/get-transaction-feed", jsonHandler(a.getTxFeed))
m.Handle("/update-transaction-feed", jsonHandler(a.updateTxFeed))
--- /dev/null
+package api
+
+import (
+ "context"
+ "strings"
+
+ "github.com/bytom/bytom/contract"
+ "github.com/bytom/bytom/crypto/sha3pool"
+ chainjson "github.com/bytom/bytom/encoding/json"
+ "github.com/bytom/bytom/errors"
+ "github.com/bytom/bytom/protocol/vm/vmutil"
+)
+
+// pre-define errors for supporting bytom errorFormatter
+var (
+ ErrNullContract = errors.New("contract is empty")
+ ErrNullContractID = errors.New("contract id is empty")
+ ErrNullContractAlias = errors.New("contract alias is empty")
+)
+
+// POST /create-asset
+func (a *API) createContract(_ context.Context, ins struct {
+ Alias string `json:"alias"`
+ Contract chainjson.HexBytes `json:"contract"`
+}) Response {
+ ins.Alias = strings.TrimSpace(ins.Alias)
+ if ins.Alias == "" {
+ return NewErrorResponse(ErrNullContractAlias)
+ }
+
+ if ins.Contract == nil {
+ return NewErrorResponse(ErrNullContract)
+ }
+
+ var hash [32]byte
+ sha3pool.Sum256(hash[:], ins.Contract)
+
+ registerProgram, err := vmutil.RegisterProgram(ins.Contract)
+ if err != nil {
+ return NewErrorResponse(err)
+ }
+
+ callProgram, err := vmutil.CallContractProgram(hash[:])
+ if err != nil {
+ return NewErrorResponse(err)
+ }
+
+ c := &contract.Contract{
+ Hash: hash[:],
+ Alias: ins.Alias,
+ Contract: ins.Contract,
+ CallProgram: callProgram,
+ RegisterProgram: registerProgram,
+ }
+ if err := a.wallet.ContractReg.SaveContract(c); err != nil {
+ return NewErrorResponse(err)
+ }
+
+ return NewSuccessResponse(c)
+}
+
+// POST /update-contract-alias
+func (a *API) updateContractAlias(_ context.Context, ins struct {
+ ID chainjson.HexBytes `json:"id"`
+ Alias string `json:"alias"`
+}) Response {
+ if ins.ID == nil {
+ return NewErrorResponse(ErrNullContractID)
+ }
+
+ ins.Alias = strings.TrimSpace(ins.Alias)
+ if ins.Alias == "" {
+ return NewErrorResponse(ErrNullContractAlias)
+ }
+
+ if err := a.wallet.ContractReg.UpdateContract(ins.ID, ins.Alias); err != nil {
+ return NewErrorResponse(err)
+ }
+
+ return NewSuccessResponse(nil)
+}
+
+// POST /get-contract
+func (a *API) getContract(_ context.Context, ins struct {
+ ID chainjson.HexBytes `json:"id"`
+}) Response {
+ if ins.ID == nil {
+ return NewErrorResponse(ErrNullContractID)
+ }
+
+ c, err := a.wallet.ContractReg.GetContract(ins.ID)
+ if err != nil {
+ return NewErrorResponse(err)
+ }
+
+ return NewSuccessResponse(c)
+}
+
+// POST /list-contracts
+func (a *API) listContracts(_ context.Context) Response {
+ cs, err := a.wallet.ContractReg.ListContracts()
+ if err != nil {
+ return NewErrorResponse(err)
+ }
+
+ return NewSuccessResponse(cs)
+}
"github.com/bytom/bytom/blockchain/rpc"
"github.com/bytom/bytom/blockchain/signers"
"github.com/bytom/bytom/blockchain/txbuilder"
+ "github.com/bytom/bytom/contract"
"github.com/bytom/bytom/errors"
"github.com/bytom/bytom/net/http/httperror"
"github.com/bytom/bytom/net/http/httpjson"
signers.ErrDupeXPub: {400, "BTM203", "Root XPubs cannot contain the same key more than once"},
// Contract error namespace (3xx)
- ErrCompileContract: {400, "BTM300", "Compile contract failed"},
- ErrInstContract: {400, "BTM301", "Instantiate contract failed"},
+ ErrCompileContract: {400, "BTM300", "Compile contract failed"},
+ ErrInstContract: {400, "BTM301", "Instantiate contract failed"},
+ contract.ErrContractDuplicated: {400, "BTM302", "Contract is duplicated"},
+ contract.ErrContractNotFound: {400, "BTM303", "Contract not found"},
// Transaction error namespace (7xx)
// Build transaction error namespace (70x ~ 72x)
--- /dev/null
+package contract
+
+import (
+ "encoding/json"
+ "sync"
+
+ dbm "github.com/bytom/bytom/database/leveldb"
+ chainjson "github.com/bytom/bytom/encoding/json"
+ "github.com/bytom/bytom/errors"
+)
+
+var (
+ userContractPrefix = []byte("UC:")
+)
+
+// pre-define errors for supporting bytom errorFormatter
+var (
+ ErrContractDuplicated = errors.New("contract is duplicated")
+ ErrContractNotFound = errors.New("contract not found")
+)
+
+// userContractKey return user contract key
+func userContractKey(hash chainjson.HexBytes) []byte {
+ return append(userContractPrefix, hash[:]...)
+}
+
+// Registry tracks and stores all user contract.
+type Registry struct {
+ db dbm.DB
+ contractMu sync.Mutex
+}
+
+//NewRegistry create new registry
+func NewRegistry(db dbm.DB) *Registry {
+ return &Registry{
+ db: db,
+ }
+}
+
+//Contract describe user contract
+type Contract struct {
+ Hash chainjson.HexBytes `json:"id"`
+ Alias string `json:"alias"`
+ Contract chainjson.HexBytes `json:"contract"`
+ CallProgram chainjson.HexBytes `json:"call_program"`
+ RegisterProgram chainjson.HexBytes `json:"register_program"`
+}
+
+// SaveContract save user contract
+func (reg *Registry) SaveContract(contract *Contract) error {
+ reg.contractMu.Lock()
+ defer reg.contractMu.Unlock()
+
+ contractKey := userContractKey(contract.Hash)
+ if existContract := reg.db.Get(contractKey); existContract != nil {
+ return ErrContractDuplicated
+ }
+
+ rawContract, err := json.Marshal(contract)
+ if err != nil {
+ return err
+ }
+
+ reg.db.Set(contractKey, rawContract)
+ return nil
+}
+
+//UpdateContract updates user contract alias
+func (reg *Registry) UpdateContract(hash chainjson.HexBytes, alias string) error {
+ reg.contractMu.Lock()
+ defer reg.contractMu.Unlock()
+
+ contract, err := reg.GetContract(hash)
+ if err != nil {
+ return err
+ }
+
+ contract.Alias = alias
+ rawContract, err := json.Marshal(contract)
+ if err != nil {
+ return err
+ }
+
+ reg.db.Set(userContractKey(hash), rawContract)
+ return nil
+}
+
+// GetContract get user contract
+func (reg *Registry) GetContract(hash chainjson.HexBytes) (*Contract, error) {
+ contract := &Contract{}
+ if rawContract := reg.db.Get(userContractKey(hash)); rawContract != nil {
+ return contract, json.Unmarshal(rawContract, contract)
+ }
+ return nil, ErrContractNotFound
+}
+
+// ListContracts returns user contracts
+func (reg *Registry) ListContracts() ([]*Contract, error) {
+ contracts := []*Contract{}
+ contractIter := reg.db.IteratorPrefix(userContractPrefix)
+ defer contractIter.Release()
+
+ for contractIter.Next() {
+ contract := &Contract{}
+ if err := json.Unmarshal(contractIter.Value(), contract); err != nil {
+ return nil, err
+ }
+
+ contracts = append(contracts, contract)
+ }
+ return contracts, nil
+}
"github.com/bytom/bytom/blockchain/txfeed"
cfg "github.com/bytom/bytom/config"
"github.com/bytom/bytom/consensus"
+ "github.com/bytom/bytom/contract"
"github.com/bytom/bytom/database"
dbm "github.com/bytom/bytom/database/leveldb"
"github.com/bytom/bytom/env"
walletDB := dbm.NewDB("wallet", config.DBBackend, config.DBDir())
accounts = account.NewManager(walletDB, chain)
assets = asset.NewRegistry(walletDB, chain)
- wallet, err = w.NewWallet(walletDB, accounts, assets, hsm, chain, dispatcher, config.Wallet.TxIndex)
+ contracts := contract.NewRegistry(walletDB)
+ wallet, err = w.NewWallet(walletDB, accounts, assets, contracts, hsm, chain, dispatcher, config.Wallet.TxIndex)
if err != nil {
log.WithFields(log.Fields{"module": logModule, "error": err}).Error("init NewWallet")
}
// CallContractProgram generates the script for control contract output
// follow BCRP(bytom contract register protocol)
-func CallContractProgram(contractID []byte) ([]byte, error) {
+func CallContractProgram(hash []byte) ([]byte, error) {
builder := NewBuilder()
builder.AddOp(vm.OP_1)
builder.AddOp(vm.OP_PUSHDATA1)
- builder.AddData(contractID)
+ builder.AddData(hash)
return builder.Build()
}
"github.com/bytom/bytom/asset"
"github.com/bytom/bytom/blockchain/pseudohsm"
"github.com/bytom/bytom/blockchain/signers"
+ "github.com/bytom/bytom/contract"
"github.com/bytom/bytom/crypto/ed25519/chainkd"
dbm "github.com/bytom/bytom/database/leveldb"
"github.com/bytom/bytom/event"
walletDB := dbm.NewDB("wallet", "leveldb", path.Join(dirPath, "wallet_db"))
accountManager := account.NewManager(walletDB, chain)
assets := asset.NewRegistry(walletDB, chain)
+ contracts := contract.NewRegistry(walletDB)
dispatcher := event.NewDispatcher()
- wallet, err := w.NewWallet(walletDB, accountManager, assets, hsm, chain, dispatcher, false)
+ wallet, err := w.NewWallet(walletDB, accountManager, assets, contracts, hsm, chain, dispatcher, false)
if err != nil {
return err
}
"github.com/bytom/bytom/account"
"github.com/bytom/bytom/asset"
"github.com/bytom/bytom/blockchain/pseudohsm"
+ "github.com/bytom/bytom/contract"
dbm "github.com/bytom/bytom/database/leveldb"
"github.com/bytom/bytom/errors"
"github.com/bytom/bytom/event"
TxIndexFlag bool
AccountMgr *account.Manager
AssetReg *asset.Registry
+ ContractReg *contract.Registry
Hsm *pseudohsm.HSM
chain *protocol.Chain
RecoveryMgr *recoveryManager
}
//NewWallet return a new wallet instance
-func NewWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, hsm *pseudohsm.HSM, chain *protocol.Chain, dispatcher *event.Dispatcher, txIndexFlag bool) (*Wallet, error) {
+func NewWallet(walletDB dbm.DB, account *account.Manager, asset *asset.Registry, contract *contract.Registry, hsm *pseudohsm.HSM, chain *protocol.Chain, dispatcher *event.Dispatcher, txIndexFlag bool) (*Wallet, error) {
w := &Wallet{
DB: walletDB,
AccountMgr: account,
AssetReg: asset,
+ ContractReg: contract,
chain: chain,
Hsm: hsm,
RecoveryMgr: newRecoveryManager(walletDB, account),
"github.com/bytom/bytom/blockchain/txbuilder"
"github.com/bytom/bytom/config"
"github.com/bytom/bytom/consensus"
+ "github.com/bytom/bytom/contract"
"github.com/bytom/bytom/crypto/ed25519/chainkd"
"github.com/bytom/bytom/database"
dbm "github.com/bytom/bytom/database/leveldb"
controlProg.KeyIndex = 1
reg := asset.NewRegistry(testDB, chain)
+ contractReg := contract.NewRegistry(testDB)
asset, err := reg.Define([]chainkd.XPub{xpub1.XPub}, 1, nil, 0, "TESTASSET", nil)
if err != nil {
t.Fatal(err)
tx := types.NewTx(*txData)
//block := mockSingleBlock(tx)
- w, err := NewWallet(testDB, accountManager, reg, hsm, chain, dispatcher, false)
+ w, err := NewWallet(testDB, accountManager, reg, contractReg, hsm, chain, dispatcher, false)
go w.memPoolTxQueryLoop()
w.eventDispatcher.Post(protocol.TxMsgEvent{TxMsg: &protocol.TxPoolMsg{TxDesc: &protocol.TxDesc{Tx: tx}, MsgType: protocol.MsgNewTx}})
time.Sleep(time.Millisecond * 10)