diff options
Diffstat (limited to 'common/natspec/natspec.go')
-rw-r--r-- | common/natspec/natspec.go | 195 |
1 files changed, 185 insertions, 10 deletions
diff --git a/common/natspec/natspec.go b/common/natspec/natspec.go index 8e6568cef..38e7c1a9d 100644 --- a/common/natspec/natspec.go +++ b/common/natspec/natspec.go @@ -1,20 +1,137 @@ package natspec import ( + "bytes" + "encoding/json" "fmt" "github.com/robertkrimen/otto" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/docserver" + "github.com/ethereum/go-ethereum/common/resolver" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/xeth" ) +type abi2method map[[8]byte]*method + type NatSpec struct { - jsvm *otto.Otto + jsvm *otto.Otto + userDocJson, abiDocJson []byte + userDoc userDoc + tx, data string + // abiDoc abiDoc +} + +func getFallbackNotice(comment, tx string) string { + + return "About to submit transaction (" + comment + "): " + tx + } -// TODO: should initialise with abi and userdoc jsons -func New() (self *NatSpec, err error) { +func GetNotice(xeth *xeth.XEth, tx string, http *docserver.DocServer) (notice string) { + + ns, err := New(xeth, tx, http) + if err != nil { + if ns == nil { + return getFallbackNotice("no NatSpec info found for contract", tx) + } else { + return getFallbackNotice("invalid NatSpec info", tx) + } + } + + notice, err2 := ns.Notice() + + if err2 != nil { + return getFallbackNotice("NatSpec notice error \""+err2.Error()+"\"", tx) + } + + return + +} + +func New(xeth *xeth.XEth, tx string, http *docserver.DocServer) (self *NatSpec, err error) { + + // extract contract address from tx + + var obj map[string]json.RawMessage + err = json.Unmarshal([]byte(tx), &obj) + if err != nil { + return + } + var tmp []map[string]string + err = json.Unmarshal(obj["params"], &tmp) + if err != nil { + return + } + contractAddress := tmp[0]["to"] - self = new(NatSpec) - self.jsvm = otto.New() + // retrieve contract hash from state + if !xeth.IsContract(contractAddress) { + err = fmt.Errorf("NatSpec error: contract not found") + return + } + codehex := xeth.CodeAt(contractAddress) + codeHash := common.BytesToHash(crypto.Sha3(common.Hex2Bytes(codehex[2:]))) + // parse out host/domain + // set up nameresolver with natspecreg + urlhint contract addresses + res := resolver.New( + xeth, + resolver.URLHintContractAddress, + resolver.HashRegContractAddress, + ) + + // resolve host via HashReg/UrlHint Resolver + uri, hash, err := res.KeyToUrl(codeHash) + if err != nil { + return + } + + // get content via http client and authenticate content using hash + content, err := http.GetAuthContent(uri, hash) + if err != nil { + return + } + + // get abi, userdoc + var obj2 map[string]json.RawMessage + err = json.Unmarshal(content, &obj2) + if err != nil { + return + } + + abi := []byte(obj2["abi"]) + userdoc := []byte(obj2["userdoc"]) + + self, err = NewWithDocs(abi, userdoc, tx) + return +} + +func NewWithDocs(abiDocJson, userDocJson []byte, tx string) (self *NatSpec, err error) { + + var obj map[string]json.RawMessage + err = json.Unmarshal([]byte(tx), &obj) + if err != nil { + return + } + var tmp []map[string]string + err = json.Unmarshal(obj["params"], &tmp) + if err != nil { + return + } + data := tmp[0]["data"] + + self = &NatSpec{ + jsvm: otto.New(), + abiDocJson: abiDocJson, + userDocJson: userDocJson, + tx: tx, + data: data, + } + + // load and require natspec js (but it is meant to be protected environment) _, err = self.jsvm.Run(natspecJS) if err != nil { return @@ -24,20 +141,78 @@ func New() (self *NatSpec, err error) { return } + err = json.Unmarshal(userDocJson, &self.userDoc) + // err = parseAbiJson(abiDocJson, &self.abiDoc) + return } -func (self *NatSpec) Notice(transaction, abi, method, expression string) (string, error) { - var err error - if _, err = self.jsvm.Run("var transaction = " + transaction + ";"); err != nil { +// type abiDoc []method + +// type method struct { +// Name string `json:name` +// Inputs []input `json:inputs` +// abiKey [8]byte +// } + +// type input struct { +// Name string `json:name` +// Type string `json:type` +// } + +// json skeleton for abi doc (contract method definitions) +type method struct { + Notice string `json:notice` + name string +} + +type userDoc struct { + Methods map[string]*method `json:methods` +} + +func (self *NatSpec) makeAbi2method(abiKey [8]byte) (meth *method) { + for signature, m := range self.userDoc.Methods { + name := strings.Split(signature, "(")[0] + hash := []byte(common.Bytes2Hex(crypto.Sha3([]byte(signature)))) + var key [8]byte + copy(key[:], hash[:8]) + if bytes.Equal(key[:], abiKey[:]) { + meth = m + meth.name = name + return + } + } + return +} + +func (self *NatSpec) Notice() (notice string, err error) { + var abiKey [8]byte + if len(self.data) < 10 { + err = fmt.Errorf("Invalid transaction data") + return + } + copy(abiKey[:], self.data[2:10]) + meth := self.makeAbi2method(abiKey) + + if meth == nil { + err = fmt.Errorf("abi key does not match any method") + return + } + notice, err = self.noticeForMethod(self.tx, meth.name, meth.Notice) + return +} + +func (self *NatSpec) noticeForMethod(tx string, name, expression string) (notice string, err error) { + + if _, err = self.jsvm.Run("var transaction = " + tx + ";"); err != nil { return "", fmt.Errorf("natspec.js error setting transaction: %v", err) } - if _, err = self.jsvm.Run("var abi = " + abi + ";"); err != nil { + if _, err = self.jsvm.Run("var abi = " + string(self.abiDocJson) + ";"); err != nil { return "", fmt.Errorf("natspec.js error setting abi: %v", err) } - if _, err = self.jsvm.Run("var method = '" + method + "';"); err != nil { + if _, err = self.jsvm.Run("var method = '" + name + "';"); err != nil { return "", fmt.Errorf("natspec.js error setting method: %v", err) } |