PCEtLXtzdWJ0ZW1wbGF0ZSBjb21tb24vaGVhZGVyX2NvbW1vbn0tLT4JPCEtLXtpZiAkX0dFVFsnbW9kJ10gPT0gJ3ZpZXd0aHJlYWQnICYmICFlbXB0eSgkX0dbJ3RpZCddKX0tLT4KCTxsaW5rIHJlbD0iY2Fub25pY2FsIiBocmVmPSJodHRwczovL3d3dy5xaWRhbzEyMy5jb20vYmJzL3RocmVhZC17JF9HWyd0aWQnXX0tMS0xLmh0bWwiIC8+Cgk8IS0te2Vsc2VpZiAkX0dFVFsnbW9kJ10gPT0gJ2ZvcnVtZGlzcGxheScgJiYgIWVtcHR5KCRfR1snZmlkJ10pfS0tPgoJPGxpbmsgcmVsPSJjYW5vbmljYWwiIGhyZWY9Imh0dHBzOi8vd3d3LnFpZGFvMTIzLmNvbS9iYnMvZm9ydW0teyRfR1snZmlkJ119LTEuaHRtbCIgLz4KCTwhLS17ZWxzZWlmICRfR0VUWydtb2QnXSA9PSAnZ3VpZGUnfS0tPgoJPGxpbmsgcmVsPSJjYW5vbmljYWwiIGhyZWY9Imh0dHBzOi8vd3d3LnFpZGFvMTIzLmNvbS9iYnMvZ3VpZGUvIiAvPgoJPCEtLXtlbHNlaWYgJF9HWydiYXNlc2NyaXB0J10gPT0gJ2ZvcnVtJyAmJiAoZW1wdHkoJF9HRVRbJ21vZCddKSB8fCAkX0dFVFsnbW9kJ10gPT0gJ2luZGV4Jyl9LS0+Cgk8bGluayByZWw9ImNhbm9uaWNhbCIgaHJlZj0iaHR0cHM6Ly93d3cucWlkYW8xMjMuY29tL2Jicy8iIC8+Cgk8IS0tey9pZn0tLT4KCXJgbmB0PG1ldGEgbmFtZT0iYXBwbGljYXRpb24tbmFtZSIgY29udGVudD0iJF9HWydzZXR0aW5nJ11bJ2JibmFtZSddIiAvPgoJPG1ldGEgbmFtZT0ibXNhcHBsaWNhdGlvbi10b29sdGlwIiBjb250ZW50PSIkX0dbJ3NldHRpbmcnXVsnYmJuYW1lJ10iIC8+Cgk8IS0te2lmICRfR1snc2V0dGluZyddWydwb3J0YWxzdGF0dXMnXX0tLT48bWV0YSBuYW1lPSJtc2FwcGxpY2F0aW9uLXRhc2siIGNvbnRlbnQ9Im5hbWU9JF9HWydzZXR0aW5nJ11bJ25hdnMnXVsxXVsnbmF2bmFtZSddO2FjdGlvbi11cmk9e2VjaG8gIWVtcHR5KCRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ3BvcnRhbCddKSA/ICRfR1snc2NoZW1lJ10uJzovLycuJF9HWydzZXR0aW5nJ11bJ2RvbWFpbiddWydhcHAnXVsncG9ydGFsJ10gOiAkX0dbc2l0ZXVybF0uJ3BvcnRhbC5waHAnfTtpY29uLXVyaT17JF9HW3NpdGV1cmxdfXtJTUdESVJ9L3BvcnRhbC5pY28iIC8+PCEtLXsvaWZ9LS0+Cgk8bWV0YSBuYW1lPSJtc2FwcGxpY2F0aW9uLXRhc2siIGNvbnRlbnQ9Im5hbWU9JF9HWydzZXR0aW5nJ11bJ25hdnMnXVsyXVsnbmF2bmFtZSddO2FjdGlvbi11cmk9e2VjaG8gIWVtcHR5KCRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2ZvcnVtJ10pID8gJF9HWydzY2hlbWUnXS4nOi8vJy4kX0dbJ3NldHRpbmcnXVsnZG9tYWluJ11bJ2FwcCddWydmb3J1bSddIDogJF9HW3NpdGV1cmxdLidmb3J1bS5waHAnfTtpY29uLXVyaT17JF9HW3NpdGV1cmxdfXtJTUdESVJ9L2Jicy5pY28iIC8+Cgk8IS0te2lmICRfR1snc2V0dGluZyddWydncm91cHN0YXR1cyddfS0tPjxtZXRhIG5hbWU9Im1zYXBwbGljYXRpb24tdGFzayIgY29udGVudD0ibmFtZT0kX0dbJ3NldHRpbmcnXVsnbmF2cyddWzNdWyduYXZuYW1lJ107YWN0aW9uLXVyaT17ZWNobyAhZW1wdHkoJF9HWydzZXR0aW5nJ11bJ2RvbWFpbiddWydhcHAnXVsnZ3JvdXAnXSkgPyAkX0dbJ3NjaGVtZSddLic6Ly8nLiRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2dyb3VwJ10gOiAkX0dbc2l0ZXVybF0uJ2dyb3VwLnBocCd9O2ljb24tdXJpPXskX0dbc2l0ZXVybF19e0lNR0RJUn0vZ3JvdXAuaWNvIiAvPjwhLS17L2lmfS0tPgoJPCEtLXtpZiBoZWxwZXJfYWNjZXNzOjpjaGVja19tb2R1bGUoJ2ZlZWQnKX0tLT48bWV0YSBuYW1lPSJtc2FwcGxpY2F0aW9uLXRhc2siIGNvbnRlbnQ9Im5hbWU9JF9HWydzZXR0aW5nJ11bJ25hdnMnXVs0XVsnbmF2bmFtZSddO2FjdGlvbi11cmk9e2VjaG8gIWVtcHR5KCRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2hvbWUnXSkgPyAkX0dbJ3NjaGVtZSddLic6Ly8nLiRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2hvbWUnXSA6ICRfR1tzaXRldXJsXS4naG9tZS5waHAnfTtpY29uLXVyaT17JF9HW3NpdGV1cmxdfXtJTUdESVJ9L2hvbWUuaWNvIiAvPjwhLS17L2lmfS0tPgoJPCEtLXtpZiAkX0dbJ2Jhc2VzY3JpcHQnXSA9PSAnZm9ydW0nICYmICRfR1snc2V0dGluZyddWydhcmNoaXZlciddfS0tPgoJCTxsaW5rIHJlbD0iYXJjaGl2ZXMiIHRpdGxlPSIkX0dbJ3NldHRpbmcnXVsnYmJuYW1lJ10iIGhyZWY9InskX0dbc2l0ZXVybF19YXJjaGl2ZXIvIiAvPgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmICFlbXB0eSgkcnNzaGVhZCl9LS0+JHJzc2hlYWQ8IS0tey9pZn0tLT4KCTwhLS17aWYgd2lkdGhhdXRvKCl9LS0+CgkJPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBpZD0iY3NzX3dpZHRoYXV0byIgdHlwZT0idGV4dC9jc3MiIGhyZWY9J3skX0dbJ3NldHRpbmcnXVsnY3NzcGF0aCddfXtTVFlMRUlEfV93aWR0aGF1dG8uY3NzP3tWRVJIQVNIfScgLz4KCQk8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+SFRNTE5PREUuY2xhc3NOYW1lICs9ICcgd2lkdGhhdXRvJzwvc2NyaXB0PgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmICRfR1snYmFzZXNjcmlwdCddID09ICdmb3J1bScgfHwgJF9HWydiYXNlc2NyaXB0J10gPT0gJ2dyb3VwJ30tLT4KCQk8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSJ7JF9HW3NldHRpbmddW2pzcGF0aF19Zm9ydW0uanM/e1ZFUkhBU0h9Ij48L3NjcmlwdD4KCTwhLS17ZWxzZWlmICRfR1snYmFzZXNjcmlwdCddID09ICdob21lJ30tLT4KCQk8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSJ7JF9HW3NldHRpbmddW2pzcGF0aF19aG9tZS5qcz97VkVSSEFTSH0iPjwvc2NyaXB0PgoJPCEtLXtlbHNlaWYgJF9HWydiYXNlc2NyaXB0J10gPT0gJ3BvcnRhbCd9LS0+CgkJPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ieyRfR1tzZXR0aW5nXVtqc3BhdGhdfXBvcnRhbC5qcz97VkVSSEFTSH0iPjwvc2NyaXB0PgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmICRfR1snYmFzZXNjcmlwdCddICE9ICdwb3J0YWwnICYmICRfR0VUWydkaXknXSA9PSAneWVzJyAmJiBjaGVja19kaXlfcGVybSgkdG9waWMpfS0tPgoJCTxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9InskX0dbc2V0dGluZ11banNwYXRoXX1wb3J0YWwuanM/e1ZFUkhBU0h9Ij48L3NjcmlwdD4KCTwhLS17L2lmfS0tPgoJPCEtLXtpZiAkX0dFVFsnZGl5J10gPT0gJ3llcycgJiYgY2hlY2tfZGl5X3Blcm0oJHRvcGljKX0tLT4KCQk8bGluayByZWw9InN0eWxlc2hlZXQiIHR5cGU9InRleHQvY3NzIiBpZD0iZGl5X2NvbW1vbiIgaHJlZj0ieyRfR1snc2V0dGluZyddWydjc3NwYXRoJ119e1NUWUxFSUR9X2Nzc19kaXkuY3NzP3tWRVJIQVNIfSIgLz4KCTwhLS17L2lmfS0tPgo8L2hlYWQ+Cgo8Ym9keSBpZD0ibnZfeyRfR1tiYXNlc2NyaXB0XX0iIGNsYXNzPSJwZ197Q1VSTU9EVUxFfXtpZiAkX0dbJ2Jhc2VzY3JpcHQnXSA9PT0gJ3BvcnRhbCcgJiYgQ1VSTU9EVUxFID09PSAnbGlzdCcgJiYgIWVtcHR5KCRjYXQpfSB7JGNhdFsnYm9keWNzcyddfXsvaWZ9IiBvbmtleWRvd249ImlmKGV2ZW50LmtleUNvZGU9PTI3KSByZXR1cm4gZmFsc2U7Ij4KCTxkaXYgaWQ9ImFwcGVuZF9wYXJlbnQiPjwvZGl2PjxkaXYgaWQ9ImFqYXh3YWl0aWQiPjwvZGl2PgoJPCEtLXtpZiAkX0dFVFsnZGl5J10gPT0gJ3llcycgJiYgY2hlY2tfZGl5X3Blcm0oJHRvcGljKX0tLT4KCQk8IS0te3RlbXBsYXRlIGNvbW1vbi9oZWFkZXJfZGl5fS0tPgoJPCEtLXsvaWZ9LS0+Cgk8IS0te2lmIGNoZWNrX2RpeV9wZXJtKCR0b3BpYyl9LS0+CgkJPCEtLXt0ZW1wbGF0ZSBjb21tb24vaGVhZGVyX2RpeW5hdn0tLT4KCTwhLS17L2lmfS0tPgoJPCEtLXtpZiBDVVJNT0RVTEUgPT0gJ3RvcGljJyAmJiAkdG9waWMgJiYgZW1wdHkoJHRvcGljWyd1c2VoZWFkZXInXSkgJiYgY2hlY2tfZGl5X3Blcm0oJHRvcGljKX0tLT4KCQkkZGl5bmF2Cgk8IS0tey9pZn0tLT4KCTwhLS17aWYgZW1wdHkoJHRvcGljKSB8fCAkdG9waWNbJ3VzZWhlYWRlciddfS0tPgoJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ21vYmlsZSddWydhbGxvd21vYmlsZSddICYmICghJF9HWydzZXR0aW5nJ11bJ2NhY2hlaW5kZXhsaWZlJ10gJiYgISRfR1snc2V0dGluZyddWydjYWNoZXRocmVhZG9uJ10gfHwgJF9HWyd1aWQnXSkgJiYgKCRfR0VUWydkaXknXSAhPSAneWVzJyB8fCAhJF9HRVRbJ2luYWpheCddKSAmJiAoJF9HWydtb2JpbGUnXSAhPSAnJyAmJiAkX0dbJ2Nvb2tpZSddWydtb2JpbGUnXSA9PSAnJyAmJiAkX0dFVFsnbW9iaWxlJ10gIT0gJ25vJyl9LS0+CgkJCTxkaXYgY2xhc3M9InhpMSBibSBibV9jIj4KCQkJICAgIHtsYW5nIHlvdXJfbW9iaWxlX2Jyb3dzZXJ9PGEgaHJlZj0ieyRfR1snc2l0ZXVybCddfWZvcnVtLnBocD9tb2JpbGU9eWVzIj57bGFuZyBnb190b19tb2JpbGV9PC9hPiA8c3BhbiBjbGFzcz0ieGcxIj58PC9zcGFuPiA8YSBocmVmPSIkX0dbJ3NldHRpbmcnXVsnbW9iaWxlJ11bJ25vbW9iaWxldXJsJ10iPntsYW5nIHRvX2JlX2NvbnRpbnVlfTwvYT4KCQkJPC9kaXY+CgkJPCEtLXsvaWZ9LS0+CgkJPCEtLXtpZiAhZW1wdHkoJF9HWydzZXR0aW5nJ11bJ3Nob3J0Y3V0J10pICYmICRfR1snbWVtYmVyJ11bY3JlZGl0c10gPj0gJF9HWydzZXR0aW5nJ11bJ3Nob3J0Y3V0J119LS0+CgkJCTxkaXYgaWQ9InNob3J0Y3V0Ij4KCQkJCTxzcGFuPjxhIGhyZWY9ImphdmFzY3JpcHQ6OyIgaWQ9InNob3J0Y3V0Y2xvc2VpZCIgdGl0bGU9IntsYW5nIGNsb3NlfSI+e2xhbmcgY2xvc2V9PC9hPjwvc3Bhbj4KCQkJCXtsYW5nIHNob3J0Y3V0X25vdGljZX0KCQkJCTxhIGhyZWY9ImphdmFzY3JpcHQ6OyIgaWQ9InNob3J0Y3V0dGlwIj57bGFuZyBzaG9ydGN1dF9hZGR9PC9hPgoKCQkJPC9kaXY+CgkJCTxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij5zZXRUaW1lb3V0KHNldFNob3J0Y3V0LCAyMDAwKTs8L3NjcmlwdD4KCQk8IS0tey9pZn0tLT4KCQk8ZGl2IGlkPSJ0b3B0YiIgY2xhc3M9ImNsIj4KCQkJPCEtLXtob29rL2dsb2JhbF9jcG5hdl90b3B9LS0+CgkJCTxkaXYgY2xhc3M9IndwIj4KCQkJCTxkaXYgY2xhc3M9InoiPgoJCQkJCTwhLS17bG9vcCAkX0dbJ3NldHRpbmcnXVsndG9wbmF2cyddWzBdICRuYXZ9LS0+CgkJCQkJCTwhLS17aWYgaXNfYXJyYXkoJG5hdikgJiYgJG5hdlsnYXZhaWxhYmxlJ10gJiYgKCEkbmF2WydsZXZlbCddIHx8ICgkbmF2WydsZXZlbCddID09IDEgJiYgJF9HWyd1aWQnXSkgfHwgKCRuYXZbJ2xldmVsJ10gPT0gMiAmJiAkX0dbJ2FkbWluaWQnXSA+IDApIHx8ICgkbmF2WydsZXZlbCddID09IDMgJiYgJF9HWydhZG1pbmlkJ10gPT0gMSkpfS0tPiRuYXZbY29kZV08IS0tey9pZn0tLT4KCQkJCQk8IS0tey9sb29wfS0tPgoJCQkJCTwhLS17aG9vay9nbG9iYWxfY3BuYXZfZXh0cmExfS0tPgoJCQkJPC9kaXY+CgkJCQk8ZGl2IGNsYXNzPSJ5Ij4KCQkJCQk8YSBpZD0ic3dpdGNoYmxpbmQiIGhyZWY9ImphdmFzY3JpcHQ6OyIgb25jbGljaz0idG9nZ2xlQmxpbmQodGhpcykiIHRpdGxlPSJ7bGFuZyBzd2l0Y2hfYmxpbmR9IiBjbGFzcz0ic3dpdGNoYmxpbmQiPjwvYT4KCQkJCQk8IS0te2hvb2svZ2xvYmFsX2NwbmF2X2V4dHJhMn0tLT4KCQkJCQk8IS0te2xvb3AgJF9HWydzZXR0aW5nJ11bJ3RvcG5hdnMnXVsxXSAkbmF2fS0tPgoJCQkJCQk8IS0te2lmIGlzX2FycmF5KCRuYXYpICYmICRuYXZbJ2F2YWlsYWJsZSddICYmICghJG5hdlsnbGV2ZWwnXSB8fCAoJG5hdlsnbGV2ZWwnXSA9PSAxICYmICRfR1sndWlkJ10pIHx8ICgkbmF2WydsZXZlbCddID09IDIgJiYgJF9HWydhZG1pbmlkJ10gPiAwKSB8fCAoJG5hdlsnbGV2ZWwnXSA9PSAzICYmICRfR1snYWRtaW5pZCddID09IDEpKX0tLT4kbmF2W2NvZGVdPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXsvbG9vcH0tLT4KCQkJCQk8IS0te2lmIGVtcHR5KCRfR1snZGlzYWJsZWR3aWR0aGF1dG8nXSkgJiYgJF9HWydzZXR0aW5nJ11bJ3N3aXRjaHdpZHRoYXV0byddfS0tPgoJCQkJCQk8YSBocmVmPSJqYXZhc2NyaXB0OjsiIGlkPSJzd2l0Y2h3aWR0aCIgb25jbGljaz0id2lkdGhhdXRvKHRoaXMpIiB0aXRsZT0ie2lmIHdpZHRoYXV0bygpfXtsYW5nIHN3aXRjaF9uYXJyb3d9e2Vsc2V9e2xhbmcgc3dpdGNoX3dpZGV9ey9pZn0iIGNsYXNzPSJzd2l0Y2h3aWR0aCI+PCEtLXtpZiB3aWR0aGF1dG8oKX0tLT57bGFuZyBzd2l0Y2hfbmFycm93fTwhLS17ZWxzZX0tLT57bGFuZyBzd2l0Y2hfd2lkZX08IS0tey9pZn0tLT48L2E+CgkJCQkJPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXtpZiAkX0dbJ3VpZCddICYmICFlbXB0eSgkX0dbJ3N0eWxlJ11bJ2V4dHN0eWxlJ10pfS0tPjxhIGlkPSJzc2xjdCIgaHJlZj0iamF2YXNjcmlwdDo7IiBvbm1vdXNlb3Zlcj0iZGVsYXlTaG93KHRoaXMsIGZ1bmN0aW9uKCkge3Nob3dNZW51KHsnY3RybGlkJzonc3NsY3QnLCdwb3MnOiczNCEnfSl9KTsiPntsYW5nIGNoYW5nZXN0eWxlfTwvYT48IS0tey9pZn0tLT4KCQkJCQk8IS0te2lmIGNoZWNrX2RpeV9wZXJtKCR0b3BpYyl9LS0+CgkJCQkJCSRkaXluYXYKCQkJCQk8IS0tey9pZn0tLT4KCQkJCTwvZGl2PgoJCQk8L2Rpdj4KCQk8L2Rpdj4KCgkJPCEtLXtpZiAhSVNfUk9CT1R9LS0+CgkJCTwhLS17aWYgJF9HWyd1aWQnXSAmJiAhJF9HWydzZXR0aW5nJ11bJ2JiY2xvc2VkJ10gJiYgZW1wdHkoJF9HWydtZW1iZXInXVsnZnJlZXplJ10pICYmICRfR1snbWVtYmVyJ11bJ2dyb3VwaWQnXSAhPSA1fS0tPgoJCQk8dWwgaWQ9Im15cHJvbXB0X21lbnUiIGNsYXNzPSJwX3BvcCIgc3R5bGU9ImRpc3BsYXk6IG5vbmU7Ij4KCQkJCTxsaT48YSBocmVmPSJob21lLnBocD9tb2Q9c3BhY2UmZG89cG0iIGlkPSJwbV9udGMiIHN0eWxlPSJiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0OyBiYWNrZ3JvdW5kLXBvc2l0aW9uOiAwIDUwJTsiPjxlbSBjbGFzcz0icHJvbXB0X25ld3N7aWYgZW1wdHkoJF9HW21lbWJlcl1bbmV3cG1dKX1fMHsvaWZ9Ij48L2VtPntsYW5nIHBtX2NlbnRlcn08L2E+PC9saT4KCQkJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ2ZvbGxvd3N0YXR1cyddfS0tPgoJCQkJCTxsaT48YSBocmVmPSJob21lLnBocD9tb2Q9Zm9sbG93JmRvPWZvbGxvd2VyIj48ZW0gY2xhc3M9InByb21wdF9mb2xsb3dlcntpZiBlbXB0eSgkX0dbbWVtYmVyXVtuZXdwcm9tcHRfbnVtXVtmb2xsb3dlcl0pfV8wey9pZn0iPjwvZW0+PCEtLXtsYW5nIG5vdGljZV9pbnRlcmFjdGl2ZV9mb2xsb3dlcn0tLT57aWYgJF9HW21lbWJlcl1bbmV3cHJvbXB0X251bV1bZm9sbG93ZXJdfSgkX0dbbWVtYmVyXVtuZXdwcm9tcHRfbnVtXVtmb2xsb3dlcl0pey9pZn08L2E+PC9saT4KCQkJCQk8IS0te2lmICRfR1ttZW1iZXJdW25ld3Byb21wdF0gJiYgJF9HW21lbWJlcl1bbmV3cHJvbXB0X251bV1bZm9sbG93XX0tLT4KCQkJCQkJPGxpPjxhIGhyZWY9ImhvbWUucGhwP21vZD1mb2xsb3ciPjxlbSBjbGFzcz0icHJvbXB0X2NvbmNlcm4iPjwvZW0+PCEtLXtsYW5nIG5vdGljZV9pbnRlcmFjdGl2ZV9mb2xsb3d9LS0+KCRfR1ttZW1iZXJdW25ld3Byb21wdF9udW1dW2ZvbGxvd10pPC9hPjwvbGk+CgkJCQkJPCEtLXsvaWZ9LS0+CgkJCQk8IS0tey9pZn0tLT4KCQkJCTwhLS17aWYgJF9HW21lbWJlcl1bbmV3cHJvbXB0XX0tLT4KCQkJCQk8IS0te2xvb3AgJF9HWydtZW1iZXInXVsnY2F0ZWdvcnlfbnVtJ10gJGtleSAkdmFsfS0tPgoJCQkJCQk8bGk+PGEgaHJlZj0iaG9tZS5waHA/bW9kPXNwYWNlJmRvPW5vdGljZSZ2aWV3PSRrZXkiPjxlbSBjbGFzcz0ibm90aWNlXyRrZXkiPjwvZW0+PCEtLXtlY2hvIGxhbmcoJ3RlbXBsYXRlJywgJ25vdGljZV8nLiRrZXkpfS0tPig8c3BhbiBjbGFzcz0icnEiPiR2YWw8L3NwYW4+KTwvYT48L2xpPgoJCQkJCTwhLS17L2xvb3B9LS0+CgkJCQk8IS0tey9pZn0tLT4KCQkJCTwhLS17aWYgZW1wdHkoJF9HWydjb29raWUnXVsnaWdub3JlX25vdGljZSddKX0tLT4KCQkJCQk8bGkgY2xhc3M9Imlnbm9yZV9ub3RpY2VsaSI+PGEgaHJlZj0iamF2YXNjcmlwdDo7IiBvbmNsaWNrPSJzZXRjb29raWUoJ2lnbm9yZV9ub3RpY2UnLCAxKTtoaWRlTWVudSgnbXlwcm9tcHRfbWVudScpIiB0aXRsZT0ie2xhbmcgdGVtcG9yYXJpbHlfdG9fcmVtaW5kfSI+PGVtIGNsYXNzPSJpZ25vcmVfbm90aWNlIj48L2VtPjwvYT48L2xpPgoJCQkJPCEtLXsvaWZ9LS0+CgkJCTwvdWw+CgkJCTwhLS17L2lmfS0tPgoJCQk8IS0te2lmICRfR1sndWlkJ10gJiYgIWVtcHR5KCRfR1snc3R5bGUnXVsnZXh0c3R5bGUnXSl9LS0+CgkJCQk8ZGl2IGlkPSJzc2xjdF9tZW51IiBjbGFzcz0iY2wgcF9wb3AiIHN0eWxlPSJkaXNwbGF5OiBub25lOyI+CgkJCQkJPCEtLXtpZiAhJF9HW3N0eWxlXVtkZWZhdWx0ZXh0c3R5bGVdfS0tPjxzcGFuIGNsYXNzPSJzc2xjdF9idG4iIG9uY2xpY2s9ImV4dHN0eWxlKCcnKSIgdGl0bGU9IntsYW5nIGRlZmF1bHR9Ij48aT48L2k+PC9zcGFuPjwhLS17L2lmfS0tPgoJCQkJCTwhLS17bG9vcCAkX0dbJ3N0eWxlJ11bJ2V4dHN0eWxlJ10gJGV4dHN0eWxlfS0tPgoJCQkJCQk8c3BhbiBjbGFzcz0ic3NsY3RfYnRuIiBvbmNsaWNrPSJleHRzdHlsZSgnJGV4dHN0eWxlWzBdJykiIHRpdGxlPSIkZXh0c3R5bGVbMV0iPjxpIHN0eWxlPSdiYWNrZ3JvdW5kOiRleHRzdHlsZVsyXSc+PC9pPjwvc3Bhbj4KCQkJCQk8IS0tey9sb29wfS0tPgoJCQkJPC9kaXY+CgkJCTwhLS17L2lmfS0tPgoJCQk8IS0te2lmICRfR1sndWlkJ119LS0+CgkJCQk8dWwgaWQ9Im15aXRlbV9tZW51IiBjbGFzcz0icF9wb3AiIHN0eWxlPSJkaXNwbGF5OiBub25lOyI+CgkJCQkJPCEtLXtpZiAkX0dbJ3NldHRpbmcnXVsnZm9ydW1zdGF0dXMnXX0tLT48bGk+PGEgaHJlZj0iaG9tZS5waHA/bW9kPXNwYWNlJmRvPXRocmVhZCZ2aWV3PW1lIj57bGFuZyBteXBvc3R9PC9hPjwvbGk+PCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXtpZiAkX0dbJ3NldHRpbmcnXVsnZmF2b3JpdGVzdGF0dXMnXX0tLT48bGk+PGEgaHJlZj0iaG9tZS5waHA/bW9kPXNwYWNlJmRvPWZhdm9yaXRlJnZpZXc9bWUiPntsYW5nIGZhdm9yaXRlfTwvYT48L2xpPjwhLS17L2lmfS0tPgoJCQkJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ2ZyaWVuZHN0YXR1cyddfS0tPjxsaT48YSBocmVmPSJob21lLnBocD9tb2Q9c3BhY2UmZG89ZnJpZW5kIj57bGFuZyBmcmllbmRzfTwvYT48L2xpPjwhLS17L2lmfS0tPgoJCQkJCTwhLS17aG9vay9nbG9iYWxfbXlpdGVtX2V4dHJhfS0tPgoJCQkJPC91bD4KCQkJPCEtLXsvaWZ9LS0+CgkJCTwhLS17c3VidGVtcGxhdGUgY29tbW9uL2hlYWRlcl9xbWVudX0tLT4KCQk8IS0tey9pZn0tLT4KCgkJPCEtLXthZC9oZWFkZXJiYW5uZXIvd3AgYV9ofS0tPgoJCTxkaXYgaWQ9ImhkIj4KCQkJPGRpdiBjbGFzcz0id3AiPgoJCQkJPGRpdiBjbGFzcz0iaGRjIGNsIj4KCQkJCQk8IS0te2V2YWwgJG1uaWQgPSBnZXRjdXJyZW50bmF2KCk7fS0tPgoJCQkJCTxoMj48IS0te2lmICFpc3NldCgkX0dbJ3NldHRpbmcnXVsnbmF2bG9nb3MnXVskbW5pZF0pfS0tPjxhIGhyZWY9IntpZiAkX0dbJ3NldHRpbmcnXVsnZG9tYWluJ11bJ2FwcCddWydkZWZhdWx0J119eyRfR1snc2NoZW1lJ119Oi8veyRfR1snc2V0dGluZyddWydkb21haW4nXVsnYXBwJ11bJ2RlZmF1bHQnXX0ve2Vsc2V9Li97L2lmfSIgdGl0bGU9IiRfR1snc2V0dGluZyddWydiYm5hbWUnXSI+eyRfR1snc3R5bGUnXVsnYm9hcmRsb2dvJ119PC9hPjwhLS17ZWxzZX0tLT4kX0dbJ3NldHRpbmcnXVsnbmF2bG9nb3MnXVskbW5pZF08IS0tey9pZn0tLT48L2gyPgoJCQkJCTwhLS17dGVtcGxhdGUgY29tbW9uL2hlYWRlcl91c2Vyc3RhdHVzfS0tPgoJCQkJPC9kaXY+CgoJCQkJPGRpdiBpZD0ibnYiPgoJCQkJCTxhIGhyZWY9ImphdmFzY3JpcHQ6OyIgaWQ9InFtZW51IiBvbm1vdXNlb3Zlcj0iZGVsYXlTaG93KHRoaXMsIGZ1bmN0aW9uICgpIHtzaG93TWVudSh7J2N0cmxpZCc6J3FtZW51JywncG9zJzonMzQhJywnY3RybGNsYXNzJzonYScsJ2R1cmF0aW9uJzoyfSk7c2hvd0ZvcnVtbWVudSgkX0dbZmlkXSk7fSkiPntsYW5nIG15X25hdn08L2E+CgkJCQkJPHVsPgoJCQkJCQk8IS0te2xvb3AgJF9HWydzZXR0aW5nJ11bJ25hdnMnXSAkbmF2fS0tPgoJCQkJCQkJPCEtLXtpZiBpc19hcnJheSgkbmF2KSAmJiAkbmF2WydhdmFpbGFibGUnXSAmJiAoISRuYXZbJ2xldmVsJ10gfHwgKCRuYXZbJ2xldmVsJ10gPT0gMSAmJiAkX0dbJ3VpZCddKSB8fCAoJG5hdlsnbGV2ZWwnXSA9PSAyICYmICRfR1snYWRtaW5pZCddID4gMCkgfHwgKCRuYXZbJ2xldmVsJ10gPT0gMyAmJiAkX0dbJ2FkbWluaWQnXSA9PSAxKSl9LS0+PGxpIHtpZiAkbW5pZCA9PSAkbmF2W25hdmlkXSB8fCBzdWJzdHIoJF9TRVJWRVJbJ1JFUVVFU1RfVVJJJ10sIDEpID09IHN0cl9yZXBsYWNlKCcuLycsICcnLCAkbmF2W2ZpbGVuYW1lXSl9Y2xhc3M9ImEiIHsvaWZ9JG5hdltuYXZdPjwvbGk+PCEtLXsvaWZ9LS0+CgkJCQkJCTwhLS17L2xvb3B9LS0+CgkJCQkJPC91bD4KCQkJCQk8IS0te2hvb2svZ2xvYmFsX25hdl9leHRyYX0tLT4KCQkJCTwvZGl2PgoJCQkJPCEtLXtpZiAhZW1wdHkoJF9HWydzZXR0aW5nJ11bJ3BsdWdpbnMnXVsnanNtZW51J10pfS0tPgoJCQkJCTx1bCBjbGFzcz0icF9wb3AgaF9wb3AiIGlkPSJwbHVnaW5fbWVudSIgc3R5bGU9ImRpc3BsYXk6IG5vbmUiPgoJCQkJCTwhLS17bG9vcCAkX0dbJ3NldHRpbmcnXVsncGx1Z2lucyddWydqc21lbnUnXSAkbW9kdWxlfS0tPgoJCQkJCQkgPCEtLXtpZiBpbl9hcnJheSgkbW9kdWxlWydhZG1pbmlkJ10sIGFycmF5KDAsIC0xKSkgfHwgKCRtb2R1bGVbJ2FkbWluaWQnXSAmJiAkX0dbJ2FkbWluaWQnXSA+IDAgJiYgJG1vZHVsZVsnYWRtaW5pZCddID49ICRfR1snYWRtaW5pZCddKX0tLT4KCQkJCQkJIDxsaT4kbW9kdWxlW3VybF08L2xpPgoJCQkJCQkgPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXsvbG9vcH0tLT4KCQkJCQk8L3VsPgoJCQkJPCEtLXsvaWZ9LS0+CgkJCQkkX0dbc2V0dGluZ11bbWVudW5hdnNdCgkJCQk8ZGl2IGlkPSJtdSIgY2xhc3M9ImNsIj4KCQkJCTwhLS17aWYgJF9HWydzZXR0aW5nJ11bJ3N1Ym5hdnMnXX0tLT4KCQkJCQk8IS0te2xvb3AgJF9HW3NldHRpbmddW3N1Ym5hdnNdICRuYXZpZCAkc3VibmF2fS0tPgoJCQkJCQk8IS0te2lmICRfR1snc2V0dGluZyddWyduYXZzdWJob3ZlciddIHx8ICRtbmlkID09ICRuYXZpZH0tLT4KCQkJCQkJPHVsIGNsYXNzPSJjbCB7aWYgJG1uaWQgPT0gJG5hdmlkfWN1cnJlbnR7L2lmfSIgaWQ9InNuYXZfJG5hdmlkIntpZiAkbW5pZCAhPSAkbmF2aWR9IHN0eWxlPSJkaXNwbGF5Om5vbmUiey9pZn0+CgkJCQkJCSRzdWJuYXYKCQkJCQkJPC91bD4KCQkJCQkJPCEtLXsvaWZ9LS0+CgkJCQkJPCEtLXsvbG9vcH0tLT4KCQkJCTwhLS17L2lmfS0tPgoJCQkJPC9kaXY+CgkJCQk8IS0te2FkL3N1Ym5hdmJhbm5lci9hX211fS0tPgoJCQkJPCEtLXtzdWJ0ZW1wbGF0ZSBjb21tb24vcHVic2VhcmNoZm9ybX0tLT4KCQkJPC9kaXY+CgkJPC9kaXY+CgoJCTwhLS17aG9vay9nbG9iYWxfaGVhZGVyfS0tPgoJPCEtLXsvaWZ9LS0+CgoJPGRpdiBpZD0id3AiIGNsYXNzPSJ3cCI+Cg==
qidao123.com ToB IT社区-企服评测·应用市场»论坛 › 区块链 › 区块链 › 智能合约开辟中13种最常见的毛病
返回列表 发新帖

智能合约开辟中13种最常见的毛病

[复制链接]
发表于 2025-10-11 23:06:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
在智能合约开辟过程中,确实存在多种范例的毛病,这些毛病大概导致资金丧失、合约功能失效或被恶意利用。以下是智能合约开辟中常见的毛病范例:

  • 1.重入攻击
  • 2.整数溢出和下溢
  • 3.未授权访问
  • 4.不妥的继续次序
  • 5.短地点攻击
  • 6.断言失败
  • 7.署理模式中的初始化毛病
  • 8.时间依赖性毛病
  • 9.Gas限定和DoS攻击
  • 10.权限管理不妥
  • 11.外部调用
  • 12.随机数天生
  • 13.存储和盘算服从
1. 重入攻击:

攻击者利用合约在实行过程中的未锁定状态,通过递归调用合约中的函数,重复提取资金或资源。
示例

我们将利用以太坊的智能合约语言 Solidity 来创建一个简单的捐赠合约,然后展示一个潜伏的重入攻击合约。起首,我们创建一个吸收捐赠的合约,这个合约有一个余额,而且允许用户提款。这个合约的代码大概看起来像如许
  1. pragma solidity ^0.8.0;
  2. contract VulnerableDonation {
  3.     mapping (address => uint) public balances;
  4.     address payable public owner;
  5.     constructor() {
  6.         owner = payable(msg.sender);
  7.     }
  8.     function donate() public payable {
  9.         // 接收捐赠
  10.         balances[msg.sender] += msg.value;
  11.     }
  12.     function withdraw(uint _amount) public {
  13.         require(balances[msg.sender] >= _amount, "Insufficient balance");
  14.         // 这里应该先减少余额,再转账,但是顺序颠倒了
  15.         msg.sender.transfer(_amount);
  16.         balances[msg.sender] -= _amount;
  17.     }
  18. }
复制代码
留意这里的题目是在 withdraw 函数中,我们起首实验向用户转账,然后才镌汰他们的余额。这是不安全的,由于转账利用会触发吸收方的 receive 或 fallback 函数,这给攻击者提供了时机来调用 withdraw 函数再次提款。
现在让我们创建一个攻击者合约,它可以利用这个毛病:
  1. pragma solidity ^0.8.0;
  2. contract Attacker {
  3.     VulnerableDonation donationContract;
  4.     constructor(address _donationAddress) {
  5.         donationContract = VulnerableDonation(_donationAddress);
  6.     }
  7.     fallback() external payable {
  8.         if (address(this).balance > 0) {
  9.             // 递归调用 withdraw 函数,只要还有余额就继续提款
  10.             donationContract.withdraw(address(this).balance);
  11.         }
  12.     }
  13.     function attack() public payable {
  14.         // 第一次调用 donate 函数向捐赠合约存入资金
  15.         donationContract.donate{value: msg.value}();
  16.         // 然后立即调用 withdraw 函数开始重入攻击
  17.         donationContract.withdraw(address(this).balance);
  18.     }
  19. }
复制代码
在攻击者合约中,fallback 函数会在吸收到资金时自动触发,假如合约中尚有余额,它会递归地调用捐赠合约的 withdraw 函数,试图尽大概多地提款,直到没有剩余的资金可以转移为止。
为了确保合约的安全,准确的做法是在转账前镌汰用户的余额,这可以通过简单地调解 withdraw 函数的次序来实现:
  1. function withdraw(uint _amount) public {
  2.     require(balances[msg.sender] >= _amount, "Insufficient balance");
  3.     balances[msg.sender] -= _amount;
  4.     // 转账应该在更新状态变量之后
  5.     msg.sender.transfer(_amount);
  6. }
复制代码
如许,纵然攻击者实验在转账之前再次调用withdraw 函数,他们也会发现自己的余额已经被更新,从而无法再次提款。
2. 整数溢出和下溢:

当数学运算的效果超出整数范例所能表现的范围时,会导致数值错误地回绕,这可以被攻击者利用来获取额外的代币或资源。
溢出示例

假设我们有一个智能合约,它吸收用户存款并存储在一个变量中。假如用户实验存入的金额加上现有的余额超出了整数的最大值(在Solidity中,uint256范例的最大值是2^256-1),就会发生溢出。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract OverflowExample {
  4.     uint256 public balance;
  5.     function deposit(uint256 amount) public {
  6.         balance += amount;
  7.     }
  8.     function getBalance() public view returns (uint256) {
  9.         return balance;
  10.     }
  11. }
复制代码
测试溢出

为了测试溢出,我们假设balance已经是uint256范例的最大值,再实验存入任何正数,都将导致溢出,即效果将从最大值回绕到0。
  1. // 假设balance已经是uint256的最大值
  2. uint256 maxUint256 = type(uint256).max;
  3. balance = maxUint256;
  4. // 尝试存入任何正数都会导致溢出
  5. deposit(1);
  6. // 此时,balance将变为0
复制代码
下溢示例

下溢通常发生在减法利用中,假如从一个较小的数中减去一个较大的数,效果将低于最小整数值(对于无符号整数,最小值是0),从而导致下溢。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract UnderflowExample {
  4.     uint256 public balance;
  5.     function withdraw(uint256 amount) public {
  6.         balance -= amount;
  7.     }
  8.     function getBalance() public view returns (uint256) {
  9.         return balance;
  10.     }
  11. }
复制代码
测试下溢

在无符号整数中,下溢现实上会导致值从0回绕到最大值,但这通常不是预期的活动,因此仍然被视为错误。
  1. // 假设balance为0
  2. balance = 0;
  3. // 尝试取出任何正数都会导致下溢
  4. withdraw(1);
  5. // 此时,balance将变成uint256的最大值
复制代码
办理方案

为了制止整数溢出和下溢,Solidity提供了安全数学库SafeMath,它包罗了查抄溢出和下溢的整数运算函数。自Solidity 0.8.0起,安全数学利用符checkedAdd, checkedSub, checkedMul, 和 checkedDiv被引入,可以自动检测并抛出非常。
  1. using SafeMath for uint256;
  2. function deposit(uint256 amount) public {
  3.     balance = balance.checkedAdd(amount);
  4. }
  5. function withdraw(uint256 amount) public {
  6.     balance = balance.checkedSub(amount);
  7. }
复制代码
如许,假如检测到溢出或下溢,Solidity将自动抛出非常,克制交易业务实行,从而掩护合约免受此类错误的影响。
3. 未授权访问:

假如智能合约对关键函数的访问控制不敷,攻击者大概实行不应允许的利用,如修改合约状态或提取资金。
未授权访问示例

假设我们有一个智能合约,用于管理用户的存款和提款。在这个例子中,合约没有准确地限定谁可以调用withdraw函数。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract SimpleBank {
  4.     mapping(address => uint256) public balances;
  5.     function deposit() public payable {
  6.         balances[msg.sender] += msg.value;
  7.     }
  8.     // 缺乏访问控制,任何人都可以调用这个函数
  9.     function withdraw(uint256 amount) public {
  10.         require(balances[msg.sender] >= amount, "Insufficient balance");
  11.         payable(msg.sender).transfer(amount);
  12.         balances[msg.sender] -= amount;
  13.     }
  14. }
复制代码
在这个合约中,withdraw函数可以直接被任何地点调用,只要该地点有充足余额即可。但是,假如合约中存在一些逻辑错误大概状态紊乱,这大概导致资金被非法提取。
攻击者活动

攻击者可以通过调用withdraw函数,纵然他们没有充足的余额,也大概由于某些合约状态的错误而乐成提取资金。比如,假如合约中的某个地方错误地增长了攻击者的余额,攻击者就可以利用这一点来提取不属于他们的资金。
办理方案

为相识决未授权访问的题目,我们必要在函数前添加访问修饰符,确保只有特定的脚色或地点可以调用withdraw函数。这里我们利用一个简单的onlyOwner修饰符来限定对合约全部者的调用。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract SecureBank {
  4.     address private owner;
  5.     mapping(address => uint256) public balances;
  6.     constructor() {
  7.         owner = msg.sender;
  8.     }
  9.     modifier onlyOwner() {
  10.         require(msg.sender == owner, "Only the contract owner can call this function");
  11.         _;
  12.     }
  13.     function deposit() public payable {
  14.         balances[msg.sender] += msg.value;
  15.     }
  16.     // 使用onlyOwner修饰符限制对所有者的调用
  17.     function withdraw(uint256 amount) public onlyOwner {
  18.         require(balances[msg.sender] >= amount, "Insufficient balance");
  19.         payable(msg.sender).transfer(amount);
  20.         balances[msg.sender] -= amount;
  21.     }
  22. }
复制代码
现在,只有合约的创建者(即owner)可以调用withdraw函数。这防止了未授权的用户直接提取资金,进步了合约的安全性。
留意,这种简单的访问控制机制大概不敷以应对复杂的场景,你大概必要更复杂的脚色和权限体系,比如利用OpenZeppelin的Ownable和AccessControl库来提供更细粒度的访问控制。
4. 不妥的继续次序:

在智能合约开辟中,不妥的继续次序大概会导致料想之外的活动,尤其是在处置处罚权限控制和函数覆盖时。当一个合约从多个父合约继续时,构造函数的实行次序和函数的覆盖规则变得尤为紧张。
不妥继续次序示例

假设我们有两个合约ParentA和ParentB,以及一个从这两个合约继续的子合约Child。ParentA合约包罗了一个构造函数和一个函数setOwner,而ParentB也界说了一个setOwner函数,但其功能差别。我们的目的是让Child合约可以或许调用ParentA的setOwner函数,但不妥的继续次序会导致调用的是ParentB的版本
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract ParentA {
  4.     address public owner;
  5.     constructor() {
  6.         owner = msg.sender;
  7.     }
  8.     function setOwner(address newOwner) public {
  9.         owner = newOwner;
  10.     }
  11. }
  12. contract ParentB {
  13.     function setOwner(address newOwner) public {
  14.         // 这里的实现与ParentA不同,但我们不关心具体细节
  15.     }
  16. }
  17. // 不当的继承顺序
  18. contract Child is ParentB, ParentA {
  19.     // ...
  20. }
复制代码
在上述代码中,Child合约继续了ParentB和ParentA。然而,在Solidity中,假如两个父合约界说了同名函数,则继续的次序决定了哪个函数会被优先覆盖。因此,在Child合约中,setOwner函数现实上是ParentB的版本,而不是我们渴望的ParentA的版本。
办理方案

要办理这个题目,我们必要调解继续次序,确保Child合约可以或许调用准确的setOwner函数。同时,为了明确指出我们想要调用哪个父合约的函数,我们可以利用Solidity提供的super关键字。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract ParentA {
  4.     address public owner;
  5.     constructor() {
  6.         owner = msg.sender;
  7.     }
  8.     function setOwnerA(address newOwner) public {
  9.         owner = newOwner;
  10.     }
  11. }
  12. contract ParentB {
  13.     function setOwnerB(address newOwner) public {
  14.         // 这里的实现与ParentA不同
  15.     }
  16. }
  17. // 正确的继承顺序
  18. contract Child is ParentA, ParentB {
  19.     // 调用ParentA的setOwner函数
  20.     function setOwner(address newOwner) public {
  21.         ParentA.setOwnerA(newOwner); // 明确调用ParentA的setOwnerA
  22.     }
  23. }
复制代码
在这个修改后的版本中,Child合约起首继续自ParentA,这意味着ParentA的函数和状态变量会先于ParentB的被初始化。别的,我们重定名了ParentA和ParentB中的setOwner函数以制止定名辩论,并在Child合约中界说了一个新的setOwner函数,它明确调用了ParentA中的setOwnerA函数。
通过这种方式,我们确保了Child合约中的setOwner函数调用的是ParentA的版本,制止了因继续次序不妥导致的函数覆盖题目。
5. 短地点攻击(Short Address Attack):

短地点攻击(Short Address Attack)在以太坊中是挑拨用以太坊地点的十六进制格式(40个字符,即20字节)和某些智能合约对地点参数处置处罚不妥的毛病,来实行恶意利用的一种攻击本事。这种攻击重要出现在智能合约没有准确验证地点参数长度的情况下,只管现实的以太坊地点长度固定,但攻击者大概实验转达较短的地点字符串,试图诱骗合约实行非预期的功能。
在Solidity中,address范例的变量总是占用20字节,因此直接转达短地点不会导致题目,由于Solidity会自动将其添补至20字节。然而,某些合约大概从外部调用吸收数据,假如这些数据被错误地表明为地点,且合约没有准确处置处罚或验证这些数据,就大概发生短地点攻击。
示例,展示了短地点攻击的潜伏风险:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract VulnerableContract {
  4.     address public owner;
  5.     constructor() {
  6.         owner = msg.sender;
  7.     }
  8.     // 错误地尝试将任意数据解析为地址
  9.     function setAddress(bytes data) public {
  10.         // 注意:这里使用了一个不安全的方法将数据解析为地址
  11.         // 实际上,如果data的长度小于20字节,这将产生一个无效的地址
  12.         assembly {
  13.             owner := mload(add(data, 0x14)) // 加载20字节的数据并赋值给owner
  14.         }
  15.     }
  16.     function getOwner() public view returns (address) {
  17.         return owner;
  18.     }
  19. }
复制代码
在这个示例中,VulnerableContract有一个公开的setAddress函数,它担当一个bytes范例的参数data,并实验在低级别汇编中将其分析为一个地点,然后设置为合约的owner。假如攻击者转达的数据长度不敷20字节,Solidity会自动添补剩余的字节为0,这大概会导致一个无效的地点被设置为owner。
攻击过程

假设攻击者构造了长度小于20字节的数据(比方,仅包罗10字节的有用负载),并调用setAddress函数。固然Solidity会自动将不敷的部门添补为0,但假如合约没有准确地验证和处置处罚这种情况,那么owner大概会被设置为一个非预期的地点,大概是一个无效的地点或一个由攻击者控制的地点。
防御步伐

为了防御短地点攻击,智能合约开辟应当:

  • 1、验证数据长度:确保全部吸收的地点数据都是完备的20字节。
  • 2、利用范例安全的函数:制止直接利用低级汇编语句处置处罚数据,而是利用范例安全的Solidity函数。
  • 3、单元测试:举行细致的单元测试,包罗边界条件和非常情况,确保合约在各种输入下都能正常工作。
  • 4、在现实开辟中,应制止直接在低级汇编中利用地点,而是利用Solidity提供的安全函数和范例查抄来处置处罚地点数据。
6. 断言失败:

断言(assert)在智能合约中用于确保内部逻辑的同等性和准确性,但假如利用不妥,确实大概导致不测的合约停止或资金锁定。这是由于assert重要用于检测步调内部的错误,比方算法错误或逻辑错误,它假定这些错误在正常运行时不会发生。一旦assert失败,交易业务将被立即回滚,且不退还gas费用,这对于合约的用户来说大概是劫难性的,特别是假如这导致了合约的关键功能无法利用。
下面是一个不妥利用assert的例子,这大概导致资金锁定:
不妥利用assert的示例
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract WithdrawalContract {
  4.     address payable public owner;
  5.     uint256 public balance;
  6.     constructor() {
  7.         owner = payable(msg.sender);
  8.         balance = 0;
  9.     }
  10.     receive() external payable {
  11.         balance += msg.value;
  12.     }
  13.     function withdraw(uint256 amount) public {
  14.         assert(msg.sender == owner); // 确保只有合约所有者可以提取资金
  15.         require(balance >= amount, "Insufficient funds"); // 确保有足够的余额
  16.         balance -= amount;
  17.         owner.transfer(amount); // 向所有者转移资金
  18.     }
  19. }
复制代码
在这个合约中,assert(msg.sender == owner)用于确保只有合约全部者才气调用withdraw函数。然而,假如在合约摆设后owner地点被不测地设置为一个无效地点(比方,一个没有私钥的地点),那么assert将永世失败,资金将永世锁定在合约中,由于没有人可以调用withdraw函数来提取资金。
办理方案

为了制止资金锁定的风险,可以思量以下几种改进计谋:

  • 1、利用require代替assert:对于用户输入或预条件查抄,利用require更为符合,由于它明确表现这是对外部条件的查抄,而非内部逻辑错误。
  • 2、添加告急撤资功能:筹划一个允许在告急情况下提取资金的机制,比方,假如owner地点被锁定,可以有一个多重署名的“董事会”来决定怎样解锁资金。
  • 3、确保合约全部者的可变动性:允许合约全部者更改,以防原始全部者丢失私钥或地点被锁定。
办理方案示例:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract ImprovedWithdrawalContract {
  4.     address payable public owner;
  5.     uint256 public balance;
  6.     constructor() {
  7.         owner = payable(msg.sender);
  8.     }
  9.     receive() external payable {
  10.         balance += msg.value;
  11.     }
  12.     modifier onlyOwner() {
  13.         require(msg.sender == owner, "Only the contract owner can call this function");
  14.         _;
  15.     }
  16.     function withdraw(uint256 amount) public onlyOwner {
  17.         require(balance >= amount, "Insufficient funds");
  18.         balance -= amount;
  19.         owner.transfer(amount);
  20.     }
  21.     // 添加一个功能,允许更改所有者
  22.     function changeOwner(address payable newOwner) public onlyOwner {
  23.         owner = newOwner;
  24.     }
  25. }
复制代码
在这个改进后的合约中,我们利用了require来查抄条件,并添加了一个changeOwner函数,允许当前全部者在须要时更改全部者地点,从而制止资金永世锁定的风险。
7. 合约署理毛病

署理模式在智能合约开辟中非常常见,尤其是在升级和模块化筹划中。署理合约(Proxy Contract)通常用于分离逻辑实现与合约的外部接口,允许在不改变接口的情况下升级或更换底层实现。然而,假如署理合约的初始化过程没有得到妥善处置处罚,就大概成为攻击的入口。
示例:署理合约初始化毛病

假设我们有如下的署理合约模板,此中implementation变量指向现实实行逻辑的合约地点:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract Proxy {
  4.     address private implementation;
  5.     constructor (address _implementation) {
  6.         implementation = _implementation;
  7.     }
  8.     fallback() external payable {
  9.         address impl = implementation;
  10.         assembly {
  11.             let ptr := mload(0x40)
  12.             calldatacopy(ptr, 0, calldatasize())
  13.             let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0)
  14.             assembly {
  15.                 let free := mload(0x40)
  16.                 mstore(free, ptr)
  17.                 mstore(0x40, add(free, 0x20))
  18.             }
  19.             switch result
  20.             case 0 {
  21.                 revert(0, returndatasize())
  22.             }
  23.             default {
  24.                 return(0, returndatasize())
  25.             }
  26.         }
  27.     }
  28. }
复制代码
这个署理合约通过构造函数担当一个实现合约地点并将其存储在implementation变量中。之后,任何发送到署理合约的交易业务都会被转发到该实现合约。
攻击方向

题目在于,假如构造函数对谁可以设置implementation地点没有得当的限定,攻击者大概会利用这一点,通过发送一笔交易业务直接调用署理合约的构造函数,从而改变implementation地点,指向他们自己的恶意合约。如许,全部后续调用都将被重定向到恶意合约,导致合约功能被篡改或资金被盗。
办理方案

为了防止这种范例的攻击,我们必要确保署理合约的初始化过程是安全的。以下是一种大概的办理方案:

  • 1、利用Initializer Pattern:引入一个初始化状态,确保署理合约只能被初始化一次,而且初始化过程受到严酷控制。可以利用一个initializer修饰符来标志那些只应在初始化过程中调用的方法。
  • 2、引入全部权验证:确保只有合约的全部者或预界说的地点可以或许设置implementation。
办理方案示例:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. abstract contract Initializable {
  4.     bool initialized = false;
  5.     modifier initializer() {
  6.         require(!initialized, "Already initialized");
  7.         initialized = true;
  8.         _;
  9.     }
  10. }
  11. contract SecureProxy is Initializable {
  12.     address private implementation;
  13.     address private admin;
  14.     constructor(address _implementation, address _admin) initializer {
  15.         implementation = _implementation;
  16.         admin = _admin;
  17.     }
  18.     function setImplementation(address _newImplementation) public {
  19.         require(msg.sender == admin, "Only admin can set the implementation");
  20.         implementation = _newImplementation;
  21.     }
  22.     fallback() external payable {
  23.         // ... (same as before)
  24.     }
  25. }
复制代码
在这个改进版本中,我们引入了Initializable抽象合约来管理初始化状态,并在构造函数上应用了initializer修饰符。别的,我们添加了一个setImplementation方法,允许通过合约全部者(admin)来更新implementation地点,进一步加强了安全性。
8. 时间依赖毛病

时间依赖毛病是智能合约中一个常见的安全题目,特别是在以太坊等区块链情况中。这是由于区块链的区块时间戳可以被矿工在肯定水平上利用,这使得依赖于时间戳的智能合约容易受到攻击。攻击者可以通过控制区块时间戳来触发合约中的某些条件,从而得到不公平的上风或造成丧失。
示例:贷款合约中的时间依赖毛病

假设我们有一个基于时间的贷款合约,乞贷人必须在特定的时间窗口内归还贷款,否则将面对高额罚息或失去抵押品。合约大概如下所示:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract LoanContract {
  4.     address public borrower;
  5.     uint256 public loanAmount;
  6.     uint256 public deadline;
  7.     constructor(address _borrower, uint256 _loanAmount, uint256 _deadline) {
  8.         borrower = _borrower;
  9.         loanAmount = _loanAmount;
  10.         deadline = block.timestamp + _deadline; // 设置还款截止日期
  11.     }
  12.     function repayLoan() public {
  13.         require(msg.sender == borrower, "Only borrower can repay");
  14.         require(block.timestamp <= deadline, "Deadline passed");
  15.         // 偿还贷款的逻辑...
  16.     }
  17.     function claimCollateral() public {
  18.         require(block.timestamp > deadline, "Deadline not yet passed");
  19.         // 没有偿还贷款,没收抵押品的逻辑...
  20.     }
  21. }
复制代码
在这个合约中,deadline是基于当前区块时间戳盘算的,乞贷人在deadline之前必须归还贷款。然而,假如攻击者控制了挖矿过程,他们可以延后提交新区块,人为延伸区块时间戳,使deadline看起来还未到达,从而克制抵押品的充公,大概相反,提条件交新区块,使deadline提前到达,迫使乞贷人付出罚息。
办理方案

为相识决时间依赖毛病,可以接纳以下几种计谋:

  • 1、利用Oracle服务:引入一个可信的Oracle服务来提供不可篡改的时间戳,如允许以镌汰矿工利用区块时间戳的影响。
  • 2、利用链上变乱作为时间基准:比方,可以利用特定的区块高度作为时间基准,由于区块高度不能被矿工容易利用。
  • 3、增长时间缓冲区:在时间相干的逻辑中到场肯定的缓冲时间,镌汰对准确时间戳的依赖。
  • 4、利用中位数时间协议(Median Time Protocol,MTP):雷同于比特币网络中的中位数时间协议,可以利用迩来多个区块时间戳的中位数来盘算一个更稳固的时间参考点。
比方,我们可以修改上面的贷款合约,利用区块高度作为时间基准:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract LoanContract {
  4.     address public borrower;
  5.     uint256 public loanAmount;
  6.     uint256 public deadlineBlock;
  7.     constructor(address _borrower, uint256 _loanAmount, uint256 _deadlineBlocks) {
  8.         borrower = _borrower;
  9.         loanAmount = _loanAmount;
  10.         deadlineBlock = block.number + _deadlineBlocks; // 设置还款截止区块
  11.     }
  12.     function repayLoan() public {
  13.         require(msg.sender == borrower, "Only borrower can repay");
  14.         require(block.number <= deadlineBlock, "Deadline block passed");
  15.         // 偿还贷款的逻辑...
  16.     }
  17.     function claimCollateral() public {
  18.         require(block.number > deadlineBlock, "Deadline block not yet passed");
  19.         // 没有偿还贷款,没收抵押品的逻辑...
  20.     }
  21. }
复制代码
通过将时间依赖改为区块高度依赖,我们镌汰了矿工利用时间戳的本事,从而加强了合约的公平性和安全性。然而,每种办理方案都有其衡量,比方利用区块高度大概会引入与区块天生时间相干的不确定性,因此在现实应用中必要过细评估和选择最得当的方案。
9. Gas限定和DoS攻击

Gas限定和DoS(Denial of Service,拒绝服务)攻击是在区块链和智能合约情况下常见的安全威胁,尤其是对于像以太坊如许的平台,此中Gas是一种用于衡量实行智能合约资本的单元。Gas机制筹划的初志是为了防止无穷循环和资源滥用,但同时也为攻击者提供了可利用的空间。
Gas限定机制

在以太坊中,每一笔交易业务都会携带肯定命量的Gas,这是为了确保任何实行的利用都不会斲丧过多的盘算资源,从而制止网络拥堵或资源耗尽。当一笔交易业务开始实行时,它会从交易业务者提供的Gas总量中扣除费用,直到合约实行完成或Gas耗尽。假如在实行过程中Gas耗尽,那么交易业务将被回滚,且已经斲丧的Gas不会退还给用户。
DoS攻击方式

耗尽Gas

攻击者可以通过构造高复杂度的交易业务或智能合约来故意斲丧大量的Gas,从而使正常交易业务无法被包罗在区块中。比方,攻击者可以创建一个合约,该合约在吸收到消息时实行大量盘算或存储利用,斲丧靠近最大Gas限额的Gas量。当许多如许的交易业务被同时发送到网络时,它们会占据大部门以致全部的Gas容量,导致其他用户的正常交易业务无法被确认,从而到达拒绝服务的效果。
无穷循环

另一种DoS攻击的方式是通过使智能合约进入无穷循环,这将导致Gas立即耗尽,交易业务失败并回滚。这种攻击通常发生在合约逻辑中存在错误的情况下,比方没有准确处置处罚循环退出条件,或在递归调用中缺少停止条件。当合约进入无穷循环时,它会实验斲丧全部可用的Gas,终极导致交易业务失败,并大概使合约处于不可用状态。
防御步伐

为了防御这类DoS攻击,开辟者在编写智能合约时必要接纳一些防备步伐:

  • 限定循环次数:确保任何循环都有明确的停止条件,制止无穷循环的大概性。
  • 优化代码服从:只管镌汰不须要的盘算和存储利用,制止高复杂度的算法。
  • 利用安全框架和库:利用如OpenZeppelin等智能合约安全库,它们通常包罗了颠末严酷审计的安全模式和函数,可以资助制止常见的安全陷阱。
  • 代码检察和测试:定期举行代码检察和安全审计,利用情势化验证工具查抄潜伏的毛病。
  • 设置Gas上限:在智能合约调用中设置公道的Gas上限,制止恶意调用斲丧过多资源。
  • 动态Gas订价:思量实办法态的Gas订价机制,根据网络负载自动调解Gas代价,以鼓励优先处置处罚紧张交易业务。
通过以上这些步伐,可以显着低沉智能合约遭受DoS攻击的风险,保障网络的稳固性和用户的资产安全。然而,由于区块链情况的复杂性,连续的安全意识和最新的安全实践是必不可少的。
毛病合约示例
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract InfiniteLoopVulnerable {
  4.     function loopUntilZero(uint256 startValue) public payable {
  5.         uint256 currentValue = startValue;
  6.         while (currentValue > 0) {
  7.             currentValue--;
  8.         }
  9.         // 正常操作...
  10.     }
  11. }
复制代码
在这个合约中,loopUntilZero函数将进入一个无穷循环,假如startValue设置得充足大,那么这个循环会斲丧全部可用的Gas,导致交易业务失败并回滚。
攻击演示

攻击者可以调用loopUntilZero函数,传入一个极大的数值,比方2^256-1,这将使循环险些不大概竣事,因此斲丧全部的Gas。
  1. InfiniteLoopVulnerable contract = new InfiniteLoopVulnerable();
  2. contract.loopUntilZero(2**256-1);
复制代码
防御步伐

为了防止这种无穷循环的DoS攻击,我们必要在合约筹划中到场一些限定和优化:

  • 1、限定循环次数:可以设定一个最大循环次数的上限,以制止无穷循环的发生。
  • 2、查抄和修复逻辑:确保循环中有准确的退出条件。
  • 3、Gas服从优化:尽大概镌汰每次循环中的利用,以低沉Gas斲丧。
下面是一个修复后的合约示例:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract SafeInfiniteLoop {
  4.     function safeLoopUntilZero(uint256 startValue) public payable {
  5.         require(startValue <= 10000, "Value too large"); // 设定最大循环次数
  6.         uint256 currentValue = startValue;
  7.         while (currentValue > 0) {
  8.             currentValue--;
  9.         }
  10.         // 正常操作...
  11.     }
  12. }
复制代码
10.  权限管理不妥 :

权限管理不妥是智能合约中常见的安全题目之一,尤其是在管理员或特定账户被过分赋予权限的情况下。假如合约中的关键功能,如转移资产、修改合约状态或升级合约逻辑,可以被未经授权的实体随意利用,这将构成严肃的安全风险。下面我将给出一个示例,展示权限管理不妥大概导致的效果,以及怎样通过公道筹划来缓解这种风险。
毛病合约示例

假设我们有一个智能合约,用于管理一个数字资产的发行和转账。在这个合约中,管理员账户被赋予了无穷的权利,可以无穷定地铸造新资产并将其转移到恣意账户。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract MismanagedPermissions {
  4.     mapping(address => uint256) public balances;
  5.     address public admin;
  6.     constructor() {
  7.         admin = msg.sender;
  8.     }
  9.     function mint(address to, uint256 amount) public {
  10.         require(msg.sender == admin, "Only admin can mint");
  11.         balances[to] += amount;
  12.     }
  13.     function transfer(address from, address to, uint256 amount) public {
  14.         require(balances[from] >= amount, "Insufficient balance");
  15.         balances[from] -= amount;
  16.         balances[to] += amount;
  17.     }
  18. }
复制代码
在这个合约中,mint函数允许管理员账户无穷定地创建新资产。固然这看起来像是一个公道的权限,但假如管理员账户的安全性受损,大概合约开辟者错误地将一个不受信托的地点设置为管理员,这将为攻击者打开大门。
攻击演示

攻击者大概通过各种本事获取管理员账户的私钥,大概合约开辟者大概不鉴戒将一个恶意地点设置为管理员。一旦攻击者控制了管理员账户,他们就可以随意调用mint函数,无穷定地创建资产并将其转移到自己的账户,从而非法赢利。
  1. MismanagedPermissions contract = new MismanagedPermissions();
  2. contract.mint(msg.sender, 1000000); // 攻击者铸造大量资产
复制代码
办理方案

为了防止权限管理不妥导致的安全题目,我们可以接纳以下步伐:

  • 1、最小权限原则:只授予实行特定使命所需的最小权限。比方,管理员可以被授予-  - 2、铸造资产的权限,但这种权限应该是有限的,比方每天只能铸造肯定命量的资产。
  • 3、多因素认证:引入多签机制或多因素认证,纵然一个管理员账户被攻破,也必要多个独立的允许才气实行关键利用。
  • 4、权限审计日志日志:纪录全部权限利用情况,以便于监控监控和审计。
  • 5、权限时效性:设定权限的有用期,逾期后必要重新授权。
下面是一个改进后的合约示例,此中增长了权限限定和多签机制:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract SafePermissions {
  4.     mapping(address => uint256) public balances;
  5.     mapping(address => bool) public admins;
  6.     uint256 public dailyMintLimit;
  7.     uint256 public dailyMinted;
  8.     constructor(uint256 _dailyMintLimit) {
  9.         dailyMintLimit = _dailyMintLimit;
  10.         admins[msg.sender] = true; // 初始管理员
  11.     }
  12.     modifier onlyAdmin() {
  13.         require(admins[msg.sender], "Only admin can perform this action");
  14.         _;
  15.     }
  16.     function mint(address to, uint256 amount) public onlyAdmin {
  17.         require(dailyMinted + amount <= dailyMintLimit, "Daily mint limit exceeded");
  18.         balances[to] += amount;
  19.         dailyMinted += amount;
  20.     }
  21.     function addAdmin(address newAdmin) public onlyAdmin {
  22.         admins[newAdmin] = true;
  23.     }
  24.     function removeAdmin(address adminToRemove) public onlyAdmin {
  25.         delete admins[adminToRemove];
  26.     }
  27. }
复制代码
在这个改进后的合约中,我们引入了多个管理员的概念,并设置了逐日铸造资产的上限,以防止无穷量的资产创造。同时,我们还提供了添加和移除管理员的功能,这必要现有管理员的权限。
通过这些改进,我们可以大大加强合约的安全性,镌汰权限管理不妥的风险。在现实应用中,还必要联合详细的业务场景和安全需求,进一步细化权限管理和安全控制机制。
11. 外部调用  :

在智能合约开辟中,调用不受信托的外部合约是一个常见的安全风险点。这是由于,当你调用另一个合约的函数时,你现实上是在实行谁人合约的代码,而这大概会引入你未曾预料的活动,包罗恶意活动。下面我将通过一个示例来分析这一风险,并提出相应的缓解计谋。
毛病合约示例

假设我们有一个智能合约,它允许用户通过调用一个外部合约来完成某种使命,比如兑换代币。这里,我们假设外部合约提供了一个transferFrom函数,用于从一个账户向另一个账户转移代币。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract ExternalCallVulnerable {
  4.     address public externalTokenContract;
  5.     constructor(address _externalTokenContract) {
  6.         externalTokenContract = _externalTokenContract;
  7.     }
  8.     function exchangeTokens(uint256 amount) public {
  9.         IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
  10.     }
  11. }
复制代码
在这个合约中,exchangeTokens函数调用了外部合约的transferFrom函数。然而,这里存在一个潜伏的题目:外部合约大概包罗恶意代码,大概其逻辑大概与预期不符,导致资金丧失或其他不良效果。
攻击演示

攻击者可以通过摆设一个恶意的ERC20代币合约,并将这个合约地点转达给我们的合约。恶意合约大概在transferFrom函数中包罗额外的逻辑,比如在转移代币的同时,调用我们的合约中的其他函数,大概实行一些未授权的利用。
  1. // 恶意合约示例
  2. contract MaliciousToken is IERC20 {
  3.     function transferFrom(address, address, uint256) public override returns (bool) {
  4.         // 正常转移代币逻辑...
  5.         // 执行额外的恶意操作,例如调用合约中的其他函数
  6.         ExternalCallVulnerable(0x...).someUnsafeFunction();
  7.         return true;
  8.     }
  9. }
复制代码
当用户实验通过我们的合约互换恶意合约中的代币时,恶意合约的transferFrom函数会被调用,实行恶意利用。
办理方案

为了减轻外部调用带来的风险,我们可以接纳以下步伐:

  • 1、代码检察:在允许调用外部合约之前,对其举行彻底的代码检察,确保其逻辑符合预期,没有包罗恶意代码。
  • 2、白名单机制:只允许调用颠末验证的、可信托的合约列表。如许,纵然出现新的恶意合约,也无法通过我们的合约举行调用。
  • 3、利用安全库:利用如OpenZeppelin等安全库中的尺度化接口,这些接口通常已经思量到了安全性和兼容性题目。
  • 4、限定调用深度:制止在调用外部合约时再次调用其他外部合约,以防止递归调用导致的攻击。
  • 5、变乱监听与非常处置处罚:在调用外部合约时,监听返回值和非常,确保调用乐成而且没有发生非常活动。
下面是一个改进后的合约示例,此中实现了白名单机制:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. interface IERC20 {
  4.     function transferFrom(address, address, uint256) external returns (bool);
  5. }
  6. contract SafeExternalCall {
  7.     mapping(address => bool) public approvedContracts;
  8.     address public externalTokenContract;
  9.     constructor(address _externalTokenContract) {
  10.         approveContract(_externalTokenContract);
  11.         externalTokenContract = _externalTokenContract;
  12.     }
  13.     function exchangeTokens(uint256 amount) public {
  14.         require(approvedContracts[externalTokenContract], "Contract not approved");
  15.         IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
  16.     }
  17.     function approveContract(address contractAddress) public {
  18.         approvedContracts[contractAddress] = true;
  19.     }
  20. }
复制代码
在智能合约开辟中,调用不受信托的外部合约是一个常见的安全风险点。这是由于,当你调用另一个合约的函数时,你现实上是在实行谁人合约的代码,而这大概会引入你未曾预料的活动,包罗恶意活动。下面我将通过一个示例来分析这一风险,并提出相应的缓解计谋。
毛病合约示例

假设我们有一个智能合约,它允许用户通过调用一个外部合约来完成某种使命,比如兑换代币。这里,我们假设外部合约提供了一个transferFrom函数,用于从一个账户向另一个账户转移代币。
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. contract ExternalCallVulnerable {
  4.     address public externalTokenContract;
  5.     constructor(address _externalTokenContract) {
  6.         externalTokenContract = _externalTokenContract;
  7.     }
  8.     function exchangeTokens(uint256 amount) public {
  9.         IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
  10.     }
  11. }
复制代码
在这个合约中,exchangeTokens函数调用了外部合约的transferFrom函数。然而,这里存在一个潜伏的题目:外部合约大概包罗恶意代码,大概其逻辑大概与预期不符,导致资金丧失或其他不良效果。
攻击演示

攻击者可以通过摆设一个恶意的ERC20代币合约,并将这个合约地点转达给我们的合约。恶意合约大概在transferFrom函数中包罗额外的逻辑,比如在转移代币的同时,调用我们的合约中的其他函数,大概实行一些未授权的利用。
  1. // 恶意合约示例
  2. contract MaliciousToken is IERC20 {
  3.     function transferFrom(address, address, uint256) public override returns (bool) {
  4.         // 正常转移代币逻辑...
  5.         // 执行额外的恶意操作,例如调用合约中的其他函数
  6.         ExternalCallVulnerable(0x...).someUnsafeFunction();
  7.         return true;
  8.     }
  9. }
复制代码
当用户实验通过我们的合约互换恶意合约中的代币时,恶意合约的transferFrom函数会被调用,实行恶意利用。
安全改进

为了减轻外部调用带来的风险,我们可以接纳以下步伐:

  • 代码检察:在允许调用外部合约之前,对其举行彻底的代码检察,确保其逻辑符合预期,没有包罗恶意代码。
  • 白名单机制:只允许调用颠末验证的、可信托的合约列表。如许,纵然出现新的恶意合约,也无法通过我们的合约举行调用。
  • 利用安全库:利用如OpenZeppelin等安全库中的尺度化接口,这些接口通常已经思量到了安全性和兼容性题目。
  • 限定调用深度:制止在调用外部合约时再次调用其他外部合约,以防止递归调用导致的攻击。
  • 变乱监听与非常处置处罚:在调用外部合约时,监听返回值和非常,确保调用乐成而且没有发生非常活动。
下面是一个改进后的合约示例,此中实现了白名单机制:
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.0;
  3. interface IERC20 {
  4.     function transferFrom(address, address, uint256) external returns (bool);
  5. }
  6. contract SafeExternalCall {
  7.     mapping(address => bool) public approvedContracts;
  8.     address public externalTokenContract;
  9.     constructor(address _externalTokenContract) {
  10.         approveContract(_externalTokenContract);
  11.         externalTokenContract = _externalTokenContract;
  12.     }
  13.     function exchangeTokens(uint256 amount) public {
  14.         require(approvedContracts[externalTokenContract], "Contract not approved");
  15.         IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
  16.     }
  17.     function approveContract(address contractAddress) public {
  18.         approvedContracts[contractAddress] = true;
  19.     }
  20. }
复制代码
在这个改进后的合约中,我们添加了一个approvedContracts映射,用于存储颠末审批的外部合约地点。只有当外部合约地点被列入白名单时,才气通过我们的合约举行调用。
通过这些改进,我们可以大大低沉因调用不受信托的外部合约而引入的安全风险。然而,在现实应用中,还必要连续关注新的安全威胁和最佳实践,以维护合约的安全性。
12. 随机数天生

一样平常在区块链上的随机数天生通常难以实现,依赖于区块哈希等可猜测因素,这大概导致攻击者可以或许猜测效果、以下是一些比力常见的场景。
1. 猜测随机数
  1. contract GuessTheNumber {
  2.     function guess(bool isHigher) public {
  3.         uint256 randomNumber = block.timestamp % 100; // 这里使用时间戳作为随机数来源
  4.         if ((randomNumber > 50) == isHigher) {
  5.             // 玩家猜对了,分配奖励
  6.         }
  7.     }
  8. }
复制代码
攻击者可以通过监控监控区块链上的交易业务和时间戳来猜测将来交易业务的时间戳,从而猜测随机数并始终做出准确的推测。
2. 交互式随机数天生
  1. contract Auction {
  2.     function endAuction() public {
  3.         uint256 random = ExternalRandomService.getLastBlockHash() % bidders.length;
  4.         // 假设bidders是一个数组,random用来选择获胜的投标人
  5.     }
  6. }
复制代码
攻击者可以观察到合约即将竣事拍卖的交易业务,然后在合约调用getLastBlockHash()之条件交一个自己的交易业务,影响区块哈希,从而影响终极的随机数。
3. 依赖预言机
  1. contract Game {
  2.     function play() public {
  3.         uint256 random = OracleService.getRandomNumber();
  4.         // 使用预言机提供的随机数
  5.     }
  6. }
复制代码
假如预言机服务被攻击者控制,他们可以提供卖弄的随机数,影响游戏效果。
发起方案

要办理上述题目,可以接纳以下几种计谋:

  • 1、利用可信预言机:选择信誉良好的预言机服务提供商,最好是颠末审计且有良好汗青纪录的服务。
  • 2、多因素随机数天生:联合多个难以猜测的因子来天生随机数,如区块难度、区块头信息以及链下提供的熵。
  • 3、时间延伸:在天生随机数和利用随机数之间到场延伸,使得攻击者难以及时猜测效果。
  • 4、链下随机数天生:利用链下随机数天生服务,然后通过预言机将效果提交到链上。
  • 5、加密技能:利用暗码学技能,犹如态加密或零知识证明,来确保随机数天生过程的隐私性和安全性。
13.存储和盘算服从
  1. 不当的存储结构或计算密集型操作可能导致高Gas费用和性能瓶颈。
复制代码
示例场景:频仍读取和写入大数组

假设你正在构建一个投票体系,此中每个提案都有一个独立的计票器。为了实现这一点,你大概最初会思量利用一个映射(map),此中键是提案ID,值是一个数组,存储全部投给该提案的选民地点。
  1. // 不当的存储结构
  2. contract VotingSystem {
  3.     mapping(uint => address[]) public voters;
  4.     function vote(uint proposalId, address voter) public {
  5.         voters[proposalId].push(voter);
  6.     }
  7.     function getVotesCount(uint proposalId) public view returns (uint) {
  8.         return voters[proposalId].length;
  9.     }
  10. }
复制代码
存在的题目


  • 1、Gas费用高昂:每当有人投票时,数组必要重新分配内存空间来容纳新元素,这会斲丧大量Gas。
  • 2、性能瓶颈:读取和写入大数组会变得非常痴钝,由于每次读取或写入利用都必要遍历整个数组。
办理方案:优化存储结构

优化发起
为了镌汰Gas费用并进步性能,我们可以重新筹划数据结构,利用映射来追踪每个选民是否已经投票给某个提案,而不是维护一个选民数组。
  1. // 优化后的存储结构
  2. contract OptimizedVotingSystem {
  3.     mapping(uint => mapping(address => bool)) public hasVoted;
  4.     function vote(uint proposalId, address voter) public {
  5.         require(!hasVoted[proposalId][voter], "Already voted");
  6.         hasVoted[proposalId][voter] = true;
  7.     }
  8.     function getVotesCount(uint proposalId) public view returns (uint) {
  9.         uint count;
  10.         for (address voter = address(1); voter != address(0); voter = address(uint(voter) + 1)) {
  11.             if (hasVoted[proposalId][voter]) {
  12.                 count++;
  13.             }
  14.         }
  15.         return count;
  16.     }
  17. }
复制代码
改进分析


  • 1、镌汰Gas费用:利用映射追踪投票状态比维护数组更高效,由于映射的利用(如插入和查找)通常更快,斲丧的Gas更少。
  • 2、提升性能:映射利用险些恒定时间复杂度O(1),不会随数据量增长而变慢。
留意事项

只管利用映射可以显着进步服从,但在getVotesCount函数中遍历全部地点来盘算投票数仍然是低效的。现实应用中,你可以引入额外的映射或变量来追踪每个提案的投票总数,以制止全地点空间的遍历。
  1. // 进一步优化
  2. contract FurtherOptimizedVotingSystem {
  3.     mapping(uint => mapping(address => bool)) public hasVoted;
  4.     mapping(uint => uint) public votesCount;
  5.     function vote(uint proposalId, address voter) public {
  6.         require(!hasVoted[proposalId][voter], "Already voted");
  7.         hasVoted[proposalId][voter] = true;
  8.         votesCount[proposalId]++;
  9.     }
  10.     function getVotesCount(uint proposalId) public view returns (uint) {
  11.         return votesCount[proposalId];
  12.     }
  13. }
复制代码
如许,每次投票时只需更新投票计数器,大大低沉了Gas费用和进步了查询速率。在智能合约开辟中,公道的筹划和优化存储结构对于低竣工本和提升性能至关紧张。
回复

使用道具 举报

返回列表 发新帖
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录

QQ|手机版|qidao123.com IT社区;IT企服评测▪应用市场 ( 浙ICP备20004199|浙ICP备20004199号 )|网站地图

GMT+8, 2026-10-2 02:40 , Processed in 0.032016 second(s), 8 queries , Redis On.

Powered by Discuz! X3.5 Licensed

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表