package native import ( "errors" "strings" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" ) // reservedContractID represents the upper bound of the reserved IDs for native contracts. const reservedContractID = -100 // Contracts is a set of registered native contracts. type Contracts struct { NEO *NEO GAS *GAS Policy *Policy Oracle *Oracle Designate *Designate Notary *Notary Contracts []interop.Contract // persistScript is vm script which executes "onPersist" method of every native contract. persistScript []byte // postPersistScript is vm script which executes "postPersist" method of every native contract. postPersistScript []byte } // ByHash returns native contract with the specified hash. func (cs *Contracts) ByHash(h util.Uint160) interop.Contract { for _, ctr := range cs.Contracts { if ctr.Metadata().Hash.Equals(h) { return ctr } } return nil } // ByName returns native contract with the specified name. func (cs *Contracts) ByName(name string) interop.Contract { name = strings.ToLower(name) for _, ctr := range cs.Contracts { if strings.ToLower(ctr.Metadata().Name) == name { return ctr } } return nil } // NewContracts returns new set of native contracts with new GAS, NEO, Policy, Oracle, // Designate and (optional) Notary contracts. func NewContracts(p2pSigExtensionsEnabled bool) *Contracts { cs := new(Contracts) gas := newGAS() neo := newNEO() neo.GAS = gas gas.NEO = neo cs.GAS = gas cs.Contracts = append(cs.Contracts, gas) cs.NEO = neo cs.Contracts = append(cs.Contracts, neo) policy := newPolicy() cs.Policy = policy cs.Contracts = append(cs.Contracts, policy) oracle := newOracle() oracle.GAS = gas oracle.NEO = neo cs.Oracle = oracle cs.Contracts = append(cs.Contracts, oracle) desig := newDesignate(p2pSigExtensionsEnabled) desig.NEO = neo cs.Designate = desig cs.Oracle.Desig = desig cs.Contracts = append(cs.Contracts, desig) if p2pSigExtensionsEnabled { notary := newNotary() notary.GAS = gas notary.Desig = desig cs.Notary = notary cs.Contracts = append(cs.Contracts, notary) } return cs } // GetPersistScript returns VM script calling "onPersist" method of every native contract. func (cs *Contracts) GetPersistScript() []byte { if cs.persistScript != nil { return cs.persistScript } w := io.NewBufBinWriter() for i := range cs.Contracts { md := cs.Contracts[i].Metadata() // Not every contract is persisted: // https://github.com/neo-project/neo/blob/master/src/neo/Ledger/Blockchain.cs#L90 if md.ContractID == policyContractID || md.ContractID == oracleContractID || md.ContractID == designateContractID { continue } emit.Int(w.BinWriter, 0) emit.Opcodes(w.BinWriter, opcode.NEWARRAY) emit.String(w.BinWriter, "onPersist") emit.AppCall(w.BinWriter, md.Hash) emit.Opcodes(w.BinWriter, opcode.DROP) } cs.persistScript = w.Bytes() return cs.persistScript } // GetPostPersistScript returns VM script calling "postPersist" method of some native contracts. func (cs *Contracts) GetPostPersistScript() []byte { if cs.postPersistScript != nil { return cs.postPersistScript } w := io.NewBufBinWriter() for i := range cs.Contracts { md := cs.Contracts[i].Metadata() // Not every contract is persisted: // https://github.com/neo-project/neo/blob/master/src/neo/Ledger/Blockchain.cs#L103 if md.ContractID == policyContractID || md.ContractID == gasContractID || md.ContractID == designateContractID || md.ContractID == notaryContractID { continue } emit.Int(w.BinWriter, 0) emit.Opcodes(w.BinWriter, opcode.NEWARRAY) emit.String(w.BinWriter, "postPersist") emit.AppCall(w.BinWriter, md.Hash) emit.Opcodes(w.BinWriter, opcode.DROP) } cs.postPersistScript = w.Bytes() return cs.postPersistScript } func postPersistBase(ic *interop.Context) error { if ic.Trigger != trigger.PostPersist { return errors.New("postPersist must be trigered by system") } return nil } func onPersistBase(ic *interop.Context) error { if ic.Trigger != trigger.OnPersist { return errors.New("onPersist must be trigered by system") } return nil }