tutus-chain/pkg/core/native/contract.go

338 lines
11 KiB
Go

package native
import (
"math/big"
"strings"
"github.com/tutus-one/tutus-chain/pkg/config"
"github.com/tutus-one/tutus-chain/pkg/core/dao"
"github.com/tutus-one/tutus-chain/pkg/core/interop"
"github.com/tutus-one/tutus-chain/pkg/core/interop/interopnames"
"github.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
"github.com/tutus-one/tutus-chain/pkg/core/native/noderoles"
"github.com/tutus-one/tutus-chain/pkg/core/state"
"github.com/tutus-one/tutus-chain/pkg/core/transaction"
"github.com/tutus-one/tutus-chain/pkg/crypto/keys"
"github.com/tutus-one/tutus-chain/pkg/io"
"github.com/tutus-one/tutus-chain/pkg/util"
"github.com/tutus-one/tutus-chain/pkg/vm/emit"
)
// Native contract interfaces sufficient for Blockchain functioning and
// cross-native interaction.
type (
// IManagement is an interface required from native ContractManagement
// contract for interaction with Blockchain and other native contracts.
IManagement interface {
interop.Contract
GetNEP11Contracts(d *dao.Simple) []util.Uint160
GetNEP17Contracts(d *dao.Simple) []util.Uint160
}
// INEO is an interface required from native NeoToken contract for
// interaction with Blockchain and other native contracts.
INEO interface {
interop.Contract
GetCommitteeAddress(d *dao.Simple) util.Uint160
GetNextBlockValidatorsInternal(d *dao.Simple) keys.PublicKeys
BalanceOf(d *dao.Simple, acc util.Uint160) (*big.Int, uint32)
CalculateBonus(ic *interop.Context, acc util.Uint160, endHeight uint32) (*big.Int, error)
GetCommitteeMembers(d *dao.Simple) keys.PublicKeys
ComputeNextBlockValidators(d *dao.Simple) keys.PublicKeys
GetCandidates(d *dao.Simple) ([]state.Validator, error)
// Methods required for proper cross-native communication.
CheckCommittee(ic *interop.Context) bool
RevokeVotes(ic *interop.Context, h util.Uint160) error
}
// IGAS is an interface required from native GasToken contract for
// interaction with Blockchain and other native contracts.
IGAS interface {
interop.Contract
BalanceOf(d *dao.Simple, acc util.Uint160) *big.Int
// Methods required for proper cross-native communication.
Burn(ic *interop.Context, h util.Uint160, amount *big.Int)
Mint(ic *interop.Context, h util.Uint160, amount *big.Int, callOnPayment bool)
}
// IPolicy is an interface required from native PolicyContract contract for
// interaction with Blockchain and other native contracts.
IPolicy interface {
interop.Contract
// GetStoragePriceInternal returns the current storage price in picoGAS units.
GetStoragePriceInternal(d *dao.Simple) int64
GetMaxVerificationGas(d *dao.Simple) int64
// GetExecFeeFactorInternal returns the current execution fee factor in picoGAS units.
GetExecFeeFactorInternal(d *dao.Simple) int64
GetMaxTraceableBlocksInternal(d *dao.Simple) uint32
GetMillisecondsPerBlockInternal(d *dao.Simple) uint32
GetMaxValidUntilBlockIncrementFromCache(d *dao.Simple) uint32
GetAttributeFeeInternal(d *dao.Simple, attrType transaction.AttrType) int64
CheckPolicy(d *dao.Simple, tx *transaction.Transaction) error
GetFeePerByteInternal(d *dao.Simple) int64
// Methods required for proper cross-native communication.
BlockAccountInternal(ic *interop.Context, hash util.Uint160) bool
GetMaxValidUntilBlockIncrementInternal(ic *interop.Context) uint32
CleanWhitelist(ic *interop.Context, cs *state.Contract) error
interop.PolicyChecker
}
// IOracle is an interface required from native OracleContract contract for
// interaction with Blockchain and other native contracts.
IOracle interface {
interop.Contract
GetOracleResponseScript() []byte
GetRequests(d *dao.Simple) (map[uint64]*state.OracleRequest, error)
GetScriptHash(d *dao.Simple) (util.Uint160, error)
GetRequestInternal(d *dao.Simple, id uint64) (*state.OracleRequest, error)
SetService(o OracleService)
}
// IDesignate is an interface required from native RoleManagement contract
// for interaction with Blockchain and other native contracts.
IDesignate interface {
interop.Contract
GetDesignatedByRole(d *dao.Simple, r noderoles.Role, index uint32) (keys.PublicKeys, uint32, error)
GetLastDesignatedHash(d *dao.Simple, r noderoles.Role) (util.Uint160, error)
SetOracleService(o OracleService)
SetNotaryService(n NotaryService)
SetStateRootService(s StateRootService)
}
// INotary is an interface required from native Notary contract for
// interaction with Blockchain and other native contracts.
INotary interface {
interop.Contract
BalanceOf(dao *dao.Simple, acc util.Uint160) *big.Int
ExpirationOf(dao *dao.Simple, acc util.Uint160) uint32
GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32
}
// IPersonToken is an interface required from native PersonToken contract
// for interaction with Blockchain and other native contracts.
IPersonToken interface {
interop.Contract
GetTokenByOwner(d *dao.Simple, owner util.Uint160) (*state.PersonToken, error)
GetTokenByIDPublic(d *dao.Simple, tokenID uint64) (*state.PersonToken, error)
TokenExists(d *dao.Simple, owner util.Uint160) bool
GetAttribute(d *dao.Simple, tokenID uint64, key string) (*state.Attribute, error)
}
// IRoleRegistry is an interface required from native RoleRegistry contract
// for interaction with Blockchain and other native contracts.
// RoleRegistry provides democratic governance for Tutus, replacing NEO.CheckCommittee().
IRoleRegistry interface {
interop.Contract
// CheckCommittee returns true if caller has COMMITTEE role.
// This replaces NEO.CheckCommittee() for Tutus democratic governance.
CheckCommittee(ic *interop.Context) bool
// HasRoleInternal checks if address has role (includes hierarchy).
HasRoleInternal(d *dao.Simple, address util.Uint160, roleID uint64, blockHeight uint32) bool
// HasPermissionInternal checks if address has permission via roles.
HasPermissionInternal(d *dao.Simple, address util.Uint160, resource, action string, scope state.Scope, blockHeight uint32) bool
}
)
// Contracts is a convenient wrapper around an arbitrary set of native contracts
// providing common helper contract accessors.
type Contracts struct {
List []interop.Contract
// persistScript is a vm script which executes "onPersist" method of every native contract.
persistScript []byte
// postPersistScript is a vm script which executes "postPersist" method of every native contract.
postPersistScript []byte
}
// NewContracts initializes a wrapper around the provided set of native
// contracts.
func NewContracts(natives []interop.Contract) *Contracts {
return &Contracts{
List: natives,
}
}
// ByHash returns a native contract with the specified hash.
func (cs *Contracts) ByHash(h util.Uint160) interop.Contract {
for _, ctr := range cs.List {
if ctr.Metadata().Hash.Equals(h) {
return ctr
}
}
return nil
}
// ByName returns a native contract with the specified name.
func (cs *Contracts) ByName(name string) interop.Contract {
name = strings.ToLower(name)
for _, ctr := range cs.List {
if strings.ToLower(ctr.Metadata().Name) == name {
return ctr
}
}
return nil
}
// GetPersistScript returns a VM script calling "onPersist" syscall for native contracts.
func (cs *Contracts) GetPersistScript() []byte {
if cs.persistScript != nil {
return cs.persistScript
}
w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, interopnames.SystemContractNativeOnPersist)
cs.persistScript = w.Bytes()
return cs.persistScript
}
// GetPostPersistScript returns a VM script calling "postPersist" syscall for native contracts.
func (cs *Contracts) GetPostPersistScript() []byte {
if cs.postPersistScript != nil {
return cs.postPersistScript
}
w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, interopnames.SystemContractNativePostPersist)
cs.postPersistScript = w.Bytes()
return cs.postPersistScript
}
// Management returns native IManagement implementation. It panics if there's no
// contract with proper name in cs.
func (cs *Contracts) Management() IManagement {
return cs.ByName(nativenames.Management).(IManagement)
}
// NEO returns native INEO contract implementation. It panics if there's no
// contract with proper name in cs.
func (cs *Contracts) NEO() INEO {
return cs.ByName(nativenames.Neo).(INEO)
}
// GAS returns native IGAS contract implementation. It panics if there's no
// contract with proper name in cs.
func (cs *Contracts) GAS() IGAS {
return cs.ByName(nativenames.Gas).(IGAS)
}
// Designate returns native IDesignate contract implementation. It panics if
// there's no contract with proper name in cs.
func (cs *Contracts) Designate() IDesignate {
return cs.ByName(nativenames.Designation).(IDesignate)
}
// Policy returns native IPolicy contract implementation. It panics if there's
// no contract with proper name in cs.
func (cs *Contracts) Policy() IPolicy {
return cs.ByName(nativenames.Policy).(IPolicy)
}
// Oracle returns native IOracle contract implementation. It returns nil if
// there's no contract with proper name in cs.
func (cs *Contracts) Oracle() IOracle {
res := cs.ByName(nativenames.Oracle)
// Oracle contract is optional.
if res != nil {
return res.(IOracle)
}
return nil
}
// Notary returns native INotary contract implementation. It returns nil if
// there's no contract with proper name in cs.
func (cs *Contracts) Notary() INotary {
res := cs.ByName(nativenames.Notary)
// Notary contract is optional.
if res != nil {
return res.(INotary)
}
return nil
}
// PersonToken returns native IPersonToken contract implementation. It panics if
// there's no contract with proper name in cs.
func (cs *Contracts) PersonToken() IPersonToken {
return cs.ByName(nativenames.PersonToken).(IPersonToken)
}
// RoleRegistry returns native IRoleRegistry contract implementation. It panics if
// there's no contract with proper name in cs.
func (cs *Contracts) RoleRegistry() IRoleRegistry {
return cs.ByName(nativenames.RoleRegistry).(IRoleRegistry)
}
// NewDefaultContracts returns a new set of default native contracts.
func NewDefaultContracts(cfg config.ProtocolConfiguration) []interop.Contract {
mgmt := NewManagement()
s := newStd()
c := newCrypto()
ledger := NewLedger()
gas := newGAS(int64(cfg.InitialGASSupply))
neo := newNEO(cfg)
policy := newPolicy()
neo.GAS = gas
neo.Policy = policy
gas.NEO = neo
gas.Policy = policy
mgmt.NEO = neo
mgmt.Policy = policy
policy.NEO = neo
ledger.Policy = policy
desig := NewDesignate(cfg.Genesis.Roles)
desig.NEO = neo
oracle := newOracle()
oracle.GAS = gas
oracle.NEO = neo
oracle.Desig = desig
notary := newNotary()
notary.GAS = gas
notary.NEO = neo
notary.Desig = desig
notary.Policy = policy
treasury := newTreasury()
treasury.NEO = neo
personToken := newPersonToken()
personToken.NEO = neo
// Parse TutusCommittee addresses from config
var tutusCommittee []util.Uint160
for _, addrStr := range cfg.TutusCommittee {
addr, err := util.Uint160DecodeStringLE(addrStr)
if err != nil {
// Try parsing as hex (BE format)
addr, err = util.Uint160DecodeStringBE(addrStr)
if err != nil {
continue // Skip invalid addresses
}
}
tutusCommittee = append(tutusCommittee, addr)
}
roleRegistry := newRoleRegistry(tutusCommittee)
roleRegistry.NEO = neo
// Set RoleRegistry on PersonToken for cross-contract integration
personToken.RoleRegistry = roleRegistry
return []interop.Contract{
mgmt,
s,
c,
ledger,
neo,
gas,
policy,
desig,
oracle,
notary,
treasury,
personToken,
roleRegistry,
}
}