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 }