github.com/igggame/nebulas-go@v2.1.0+incompatible/core/access.go (about)

     1  // Copyright (C) 2018 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package core
    20  
    21  import (
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"path/filepath"
    27  	"regexp"
    28  	"strings"
    29  
    30  	"github.com/gogo/protobuf/proto"
    31  	"github.com/nebulasio/go-nebulas/core/pb"
    32  )
    33  
    34  const (
    35  	NRC20FuncTransfer     = "transfer"
    36  	NRC20FuncTransferFrom = "transferFrom"
    37  	NRC20FuncApprove      = "approve"
    38  )
    39  
    40  type Access struct {
    41  	access *corepb.Access
    42  }
    43  
    44  // NewAccess returns the Access
    45  func NewAccess(path string) (*Access, error) {
    46  	if path != "" {
    47  		path, err := filepath.Abs(path)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  		bytes, err := ioutil.ReadFile(path)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		content := string(bytes)
    56  
    57  		access := new(corepb.Access)
    58  		if err = proto.UnmarshalText(content, access); err != nil {
    59  			return nil, err
    60  		}
    61  		return &Access{access: access}, nil
    62  	}
    63  	return &Access{}, nil
    64  }
    65  
    66  // CheckTransaction Check that the transaction meets the conditions
    67  func (a *Access) CheckTransaction(tx *Transaction) error {
    68  	if a.access == nil {
    69  		// no access config need to check
    70  		return nil
    71  	}
    72  	if a.access.Blacklist != nil {
    73  		for _, addr := range a.access.Blacklist.From {
    74  			if addr == tx.from.String() {
    75  				return ErrRestrictedFromAddress
    76  			}
    77  		}
    78  		for _, addr := range a.access.Blacklist.To {
    79  			if addr == tx.to.String() {
    80  				return ErrRestrictedToAddress
    81  			}
    82  		}
    83  
    84  		if tx.Type() == TxPayloadDeployType || tx.Type() == TxPayloadCallType {
    85  			for _, contract := range a.access.Blacklist.Contracts {
    86  				match := false
    87  				if contract.Address != "" {
    88  					match = contract.Address == tx.to.String()
    89  				}
    90  				if tx.Type() == TxPayloadCallType && len(contract.Functions) > 0 {
    91  					payload, err := tx.LoadPayload()
    92  					callPayload := payload.(*CallPayload)
    93  					if err != nil {
    94  						return err
    95  					}
    96  					funcMatch := false
    97  					for _, function := range contract.Functions {
    98  						if function == callPayload.Function {
    99  							funcMatch = true
   100  							break
   101  						}
   102  					}
   103  					match = match && funcMatch
   104  				}
   105  				if match {
   106  					return ErrUnsupportedFunction
   107  				}
   108  				if tx.Type() == TxPayloadDeployType && len(contract.Keywords) > 0 {
   109  					data := strings.ToLower(string(tx.Data()))
   110  					for _, keyword := range contract.Keywords {
   111  						keyword = strings.ToLower(keyword)
   112  						if strings.Contains(data, keyword) {
   113  							unsupportedKeywordError := fmt.Sprintf("transaction data has unsupported keyword(keyword: %s)", keyword)
   114  							return errors.New(unsupportedKeywordError)
   115  						}
   116  					}
   117  				}
   118  			}
   119  		}
   120  	}
   121  
   122  	if a.access.Nrc20List != nil {
   123  		// check nrc20 security
   124  		if err := a.nrc20SecurityCheck(tx); err != nil {
   125  			return err
   126  		}
   127  	}
   128  	return nil
   129  }
   130  
   131  // nrc20SecurityCheck check nrc20 contract params security
   132  func (a *Access) nrc20SecurityCheck(tx *Transaction) error {
   133  	if tx.Type() == TxPayloadCallType && len(a.access.Nrc20List.Contracts) > 0 {
   134  		for _, contract := range a.access.Nrc20List.Contracts {
   135  			// check nrc20 security
   136  			if tx.To().String() == contract {
   137  				payload, err := tx.LoadPayload()
   138  				if err != nil {
   139  					return err
   140  				}
   141  				call := payload.(*CallPayload)
   142  				valueIndex := 0
   143  				switch call.Function {
   144  				case NRC20FuncTransfer:
   145  					valueIndex = 1
   146  				case NRC20FuncTransferFrom:
   147  					valueIndex = 2
   148  				case NRC20FuncApprove:
   149  					valueIndex = 2
   150  				default:
   151  					valueIndex = -1
   152  				}
   153  				if valueIndex > 0 {
   154  					var argsObj []string
   155  					if err := json.Unmarshal([]byte(call.Args), &argsObj); err != nil {
   156  						return ErrNrc20ArgsCheckFailed
   157  					}
   158  					addr := argsObj[0]
   159  					if _, err := AddressParse(addr); err != nil {
   160  						return ErrNrc20AddressCheckFailed
   161  					}
   162  					value := argsObj[valueIndex]
   163  					if matched, err := regexp.MatchString("^[0-9]+$", value); matched == false || err != nil {
   164  						return ErrNrc20ValueCheckFailed
   165  					}
   166  				}
   167  			}
   168  		}
   169  	}
   170  	return nil
   171  }