github.com/datachainlab/burrow@v0.25.0/deploy/util/variables.go (about)

     1  package util
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"encoding/json"
    11  
    12  	"unicode"
    13  
    14  	"github.com/hyperledger/burrow/deploy/def"
    15  	"github.com/hyperledger/burrow/deploy/def/rule"
    16  	"github.com/hyperledger/burrow/execution/evm/abi"
    17  	"github.com/hyperledger/burrow/logging"
    18  )
    19  
    20  func Variables(value interface{}) []*abi.Variable {
    21  	rv := reflect.ValueOf(value)
    22  	if rv.Kind() == reflect.Ptr {
    23  		rv = rv.Elem()
    24  	}
    25  	rt := rv.Type()
    26  	var variables []*abi.Variable
    27  	for i := 0; i < rv.NumField(); i++ {
    28  		field := rv.Field(i)
    29  		if field.Kind() == reflect.String {
    30  			variables = append(variables, &abi.Variable{Name: lowerFirstCharacter(rt.Field(i).Name), Value: field.String()})
    31  		}
    32  
    33  	}
    34  	return variables
    35  }
    36  
    37  func lowerFirstCharacter(name string) string {
    38  	if name == "" {
    39  		return name
    40  	}
    41  	bs := []byte(name)
    42  	bs[0] = byte(unicode.ToLower(rune(bs[0])))
    43  	return string(bs)
    44  }
    45  
    46  func PreProcessFields(value interface{}, do *def.DeployArgs, script *def.Playbook, client *def.Client, logger *logging.Logger) (err error) {
    47  	rv := reflect.ValueOf(value)
    48  	if rv.Kind() == reflect.Ptr {
    49  		rv = rv.Elem()
    50  	}
    51  	for i := 0; i < rv.NumField(); i++ {
    52  		field := rv.Field(i)
    53  		if field.Kind() == reflect.String {
    54  			str, err := PreProcess(field.String(), do, script, client, logger)
    55  			if err != nil {
    56  				return err
    57  			}
    58  			field.SetString(str)
    59  		}
    60  	}
    61  	return nil
    62  }
    63  
    64  func PreProcess(toProcess string, do *def.DeployArgs, script *def.Playbook, client *def.Client, logger *logging.Logger) (string, error) {
    65  	// Run through the replacement process for any placeholder matches
    66  	for _, pm := range rule.MatchPlaceholders(toProcess) {
    67  		logger.TraceMsg("Replacement Match Found",
    68  			"match", toProcess)
    69  
    70  		// first parse the reserved words.
    71  		if strings.Contains(pm.JobName, "block") {
    72  			block, err := replaceBlockVariable(pm.Match, client, logger)
    73  			if err != nil {
    74  				logger.InfoMsg("Errir replacing block variable",
    75  					"error", fmt.Sprintf("%v", err))
    76  				return "", err
    77  			}
    78  			/*log.WithFields(log.Fields{
    79  				"var": toProcess,
    80  				"res": block,
    81  			}).Debug("Fixing Variables =>")*/
    82  			toProcess = strings.Replace(toProcess, pm.Match, block, 1)
    83  			continue
    84  		}
    85  
    86  		// second we loop through the jobNames to do a result replace
    87  		var loopJobs func(script *def.Playbook) error
    88  
    89  		loopJobs = func(script *def.Playbook) error {
    90  			if script.Parent != nil {
    91  				err := loopJobs(script.Parent)
    92  				if err != nil {
    93  					return err
    94  				}
    95  			}
    96  
    97  			for _, job := range script.Jobs {
    98  				if pm.JobName == job.Name {
    99  					if pm.VariableName != "" {
   100  						for _, variable := range job.Variables {
   101  							if variable.Name == pm.VariableName { //find the value we want from the bunch
   102  								toProcess = strings.Replace(toProcess, pm.Match, variable.Value, 1)
   103  								logger.TraceMsg("Fixing Inner Vars",
   104  									"job", pm.JobName,
   105  									"varName", pm.VariableName,
   106  									"result", variable.Value)
   107  							}
   108  						}
   109  					} else {
   110  						// If result is returned as string assume that rendering otherwise marshal to JSON
   111  						result, ok := job.Result.(string)
   112  						if !ok {
   113  							bs, err := json.Marshal(job.Result)
   114  							if err != nil {
   115  								return fmt.Errorf("error marhsalling tx result in post processing: %v", err)
   116  							}
   117  							result = string(bs)
   118  						}
   119  						logger.TraceMsg("Fixing Variables",
   120  							"var", string(pm.JobName),
   121  							"res", result)
   122  						toProcess = strings.Replace(toProcess, pm.Match, result, 1)
   123  					}
   124  				}
   125  			}
   126  			return nil
   127  		}
   128  
   129  		err := loopJobs(script)
   130  		if err != nil {
   131  			return "", err
   132  		}
   133  	}
   134  	return toProcess, nil
   135  }
   136  
   137  func replaceBlockVariable(toReplace string, client *def.Client, logger *logging.Logger) (string, error) {
   138  	logger.TraceMsg("Correcting $block variable",
   139  		"var", toReplace)
   140  
   141  	blockHeight, err := GetBlockHeight(client, logger)
   142  	block := itoaU64(blockHeight)
   143  	logger.TraceMsg("Current height", "height", block)
   144  	if err != nil {
   145  		return "", err
   146  	}
   147  
   148  	if toReplace == "$block" {
   149  		logger.TraceMsg("Replacement (=)",
   150  			"block", block)
   151  		return block, nil
   152  	}
   153  
   154  	catchEr := regexp.MustCompile(`\$block\+(\d*)`)
   155  	if catchEr.MatchString(toReplace) {
   156  		height := catchEr.FindStringSubmatch(toReplace)[1]
   157  		h1, err := strconv.Atoi(height)
   158  		if err != nil {
   159  			return "", err
   160  		}
   161  		h2, err := strconv.Atoi(block)
   162  		if err != nil {
   163  			return "", err
   164  		}
   165  		height = strconv.Itoa(h1 + h2)
   166  		logger.TraceMsg("Replacement (+)",
   167  			"replacement", height)
   168  
   169  		return height, nil
   170  	}
   171  
   172  	catchEr = regexp.MustCompile(`\$block\-(\d*)`)
   173  	if catchEr.MatchString(toReplace) {
   174  		height := catchEr.FindStringSubmatch(toReplace)[1]
   175  		h1, err := strconv.Atoi(height)
   176  		if err != nil {
   177  			return "", err
   178  		}
   179  		h2, err := strconv.Atoi(block)
   180  		if err != nil {
   181  			return "", err
   182  		}
   183  		height = strconv.Itoa(h1 - h2)
   184  		logger.TraceMsg("Replacement (-)",
   185  			"replacement", height)
   186  		return height, nil
   187  	}
   188  
   189  	logger.TraceMsg("Replacement (unknown)",
   190  		"replacement", toReplace)
   191  
   192  	return toReplace, nil
   193  }
   194  
   195  func PreProcessInputData(function string, data interface{}, do *def.DeployArgs, script *def.Playbook, client *def.Client, constructor bool, logger *logging.Logger) (string, []interface{}, error) {
   196  	var callDataArray []interface{}
   197  	var callArray []string
   198  	if function == "" && !constructor {
   199  		if reflect.TypeOf(data).Kind() == reflect.Slice {
   200  			return "", []interface{}{""}, fmt.Errorf("Incorrect formatting of deploy.yaml. Please update it to include a function field.")
   201  		}
   202  		function = strings.Split(data.(string), " ")[0]
   203  		callArray = strings.Split(data.(string), " ")[1:]
   204  		for _, val := range callArray {
   205  			output, _ := PreProcess(val, do, script, client, logger)
   206  			callDataArray = append(callDataArray, output)
   207  		}
   208  	} else if data != nil {
   209  		if reflect.TypeOf(data).Kind() != reflect.Slice {
   210  			if constructor {
   211  				logger.InfoMsg("Deprecation Warning: Your deploy job is currently using a soon to be deprecated way of declaring constructor values. Please remember to update your run file to store them as a array rather than a string. See documentation for further details.")
   212  				callArray = strings.Split(data.(string), " ")
   213  				for _, val := range callArray {
   214  					output, _ := PreProcess(val, do, script, client, logger)
   215  					callDataArray = append(callDataArray, output)
   216  				}
   217  				return function, callDataArray, nil
   218  			} else {
   219  				return "", []interface{}{}, fmt.Errorf("Incorrect formatting of deploy.yaml file. Please update it to include a function field.")
   220  			}
   221  		}
   222  		val := reflect.ValueOf(data)
   223  		for i := 0; i < val.Len(); i++ {
   224  			s := val.Index(i)
   225  			var newString string
   226  			switch s.Interface().(type) {
   227  			case bool:
   228  				newString = strconv.FormatBool(s.Interface().(bool))
   229  			case int, int32, int64:
   230  				newString = strconv.FormatInt(int64(s.Interface().(int)), 10)
   231  			case []interface{}:
   232  				var args []string
   233  				for _, index := range s.Interface().([]interface{}) {
   234  					value := reflect.ValueOf(index)
   235  					var stringified string
   236  					switch value.Kind() {
   237  					case reflect.Int:
   238  						stringified = strconv.FormatInt(value.Int(), 10)
   239  					case reflect.String:
   240  						stringified = value.String()
   241  					}
   242  					index, _ = PreProcess(stringified, do, script, client, logger)
   243  					args = append(args, stringified)
   244  				}
   245  				newString = "[" + strings.Join(args, ",") + "]"
   246  				logger.TraceMsg(newString)
   247  			default:
   248  				newString = s.Interface().(string)
   249  			}
   250  			newString, _ = PreProcess(newString, do, script, client, logger)
   251  			callDataArray = append(callDataArray, newString)
   252  		}
   253  	}
   254  	return function, callDataArray, nil
   255  }
   256  
   257  func PreProcessLibs(libs string, do *def.DeployArgs, script *def.Playbook, client *def.Client, logger *logging.Logger) (string, error) {
   258  	libraries, _ := PreProcess(libs, do, script, client, logger)
   259  	if libraries != "" {
   260  		pairs := strings.Split(libraries, ",")
   261  		libraries = strings.Join(pairs, " ")
   262  	}
   263  	logger.TraceMsg("Library String", "libs", libraries)
   264  	return libraries, nil
   265  }
   266  
   267  func GetReturnValue(vars []*abi.Variable, logger *logging.Logger) string {
   268  	var result []string
   269  
   270  	if len(vars) > 1 {
   271  		for _, value := range vars {
   272  			logger.TraceMsg("Value",
   273  				value.Name, value.Value)
   274  			result = append(result, value.Value)
   275  		}
   276  		return "(" + strings.Join(result, ", ") + ")"
   277  	} else if len(vars) == 1 {
   278  		logger.TraceMsg("Debugging",
   279  			"value", vars[0].Value)
   280  		return vars[0].Value
   281  	} else {
   282  		return ""
   283  	}
   284  }